From 653026720bafcc4abb64dcf8ff37e4910c346786 Mon Sep 17 00:00:00 2001 From: Tae Won Ha Date: Tue, 5 Mar 2019 18:35:51 +0100 Subject: [PATCH] Add a flag for parallel rendering --- NvimView/NvimView/AttributesRunDrawer.swift | 42 +++++---------------- NvimView/NvimView/NvimView.swift | 6 +++ NvimView/NvimView/SwiftCommons.swift | 30 +++++++++++++++ VimR/VimR/AdvancedPrefReducer.swift | 6 ++- VimR/VimR/AdvencedPref.swift | 34 ++++++++++++++++- VimR/VimR/States.swift | 4 ++ 6 files changed, 86 insertions(+), 36 deletions(-) diff --git a/NvimView/NvimView/AttributesRunDrawer.swift b/NvimView/NvimView/AttributesRunDrawer.swift index 93caceae..f7b65a7b 100644 --- a/NvimView/NvimView/AttributesRunDrawer.swift +++ b/NvimView/NvimView/AttributesRunDrawer.swift @@ -20,6 +20,8 @@ final class AttributesRunDrawer { } var usesLigatures: Bool + var drawsParallel = false + private(set) var cellSize: CGSize = .zero private(set) var baselineOffset: CGFloat = 0 private(set) var descent: CGFloat = 0 @@ -40,15 +42,11 @@ final class AttributesRunDrawer { offset: CGPoint, `in` context: CGContext ) { - #if DEBUG - self.drawByParallelComputation( - attrsRuns, - defaultAttributes: defaultAttributes, - offset: offset, - in: context - ) - #else - let runs = attrsRuns.map { self.fontGlyphRuns(from: $0, offset: offset) } + let runs = self.drawsParallel ? + attrsRuns.parallelMap(chunkSize: 50) { run in + self.fontGlyphRuns(from: run, offset: offset) + } + : attrsRuns.map { self.fontGlyphRuns(from: $0, offset: offset) } for i in 0..( + chunkSize: Int = 1, + _ transform: @escaping (Element) -> T + ) -> [T] { + let count = self.count + guard count > chunkSize else { return self.map(transform) } + + var result = Array(repeating: nil, count: count) + + // If we don't use Array.withUnsafeMutableBufferPointer, + // then we get crashes. + result.withUnsafeMutableBufferPointer { pointer in + if chunkSize == 1 { + DispatchQueue.concurrentPerform(iterations: count) { i in + pointer[i] = transform(self[i]) + } + } else { + let chunkCount = Int(ceil(Double(self.count) / Double(chunkSize))) + DispatchQueue.concurrentPerform(iterations: chunkCount) { chunkIndex in + let start = Swift.min(chunkIndex * chunkSize, count) + let end = Swift.min(start + chunkSize, count) + + (start..( with marker: (Index, Element) -> T ) -> [CountableClosedRange] { diff --git a/VimR/VimR/AdvancedPrefReducer.swift b/VimR/VimR/AdvancedPrefReducer.swift index 16a2070f..fe78921c 100644 --- a/VimR/VimR/AdvancedPrefReducer.swift +++ b/VimR/VimR/AdvancedPrefReducer.swift @@ -17,7 +17,11 @@ class AdvancedPrefReducer: ReducerType { case let .setUseLiveResize(value): state.mainWindowTemplate.useLiveResize = value - state.mainWindows.keys.forEach { state.mainWindows[$0]?.useLiveResize = value } + state.mainWindows.keys.forEach { state.mainWindows[$0]?.useLiveResize = value } + + case let .setDrawsParallel(value): + state.mainWindowTemplate.drawsParallel = value + state.mainWindows.keys.forEach { state.mainWindows[$0]?.drawsParallel = value } case let .setTrackpadScrollResistance(value): state.mainWindowTemplate.trackpadScrollResistance = value diff --git a/VimR/VimR/AdvencedPref.swift b/VimR/VimR/AdvencedPref.swift index 5f03da63..498861fc 100644 --- a/VimR/VimR/AdvencedPref.swift +++ b/VimR/VimR/AdvencedPref.swift @@ -17,6 +17,7 @@ class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate { case setUseSnapshotUpdate(Bool) case setTrackpadScrollResistance(Double) case setUseLiveResize(Bool) + case setDrawsParallel(Bool) } override var displayName: String { @@ -34,6 +35,7 @@ class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate { self.useSnapshotUpdate = state.useSnapshotUpdate self.sensitivity = 1 / state.mainWindowTemplate.trackpadScrollResistance self.useLiveResize = state.mainWindowTemplate.useLiveResize + self.drawsParallel = state.mainWindowTemplate.drawsParallel super.init(frame: .zero) @@ -63,11 +65,13 @@ class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate { private var useInteractiveZsh: Bool private var useSnapshotUpdate: Bool private var useLiveResize: Bool + private var drawsParallel: Bool private var sensitivity: Double private let useInteractiveZshCheckbox = NSButton(forAutoLayout: ()) private let useSnapshotUpdateCheckbox = NSButton(forAutoLayout: ()) private let useLiveResizeCheckbox = NSButton(forAutoLayout: ()) + private let drawsParallelCheckbox = NSButton(forAutoLayout: ()) private let sensitivitySlider = NSSlider(forAutoLayout: ()) required init?(coder: NSCoder) { @@ -78,6 +82,7 @@ class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate { self.useSnapshotUpdateCheckbox.boolState = self.useSnapshotUpdate self.useInteractiveZshCheckbox.boolState = self.useInteractiveZsh self.useLiveResizeCheckbox.boolState = self.useLiveResize + self.drawsParallelCheckbox.boolState = self.drawsParallel // We don't update the value of the NSSlider since we don't know when events are fired. } @@ -108,7 +113,7 @@ class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate { """) let useLiveResize = self.useLiveResizeCheckbox - self.configureCheckbox(button: self.useLiveResizeCheckbox, + self.configureCheckbox(button: useLiveResize, title: "Use Live Window Resizing", action: #selector(AdvancedPref.useLiveResizeAction(_:))) @@ -117,6 +122,18 @@ class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate { If you do, please report them at [GitHub](https://github.com/qvacua/vimr/issues). """) + let drawsParallelBox = self.drawsParallelCheckbox + self.configureCheckbox(button: drawsParallelBox, + title: "Use Concurrent Rendering", + action: #selector(AdvancedPref.drawParallelAction(_:))) + + let drawsParallelInfo = self.infoTextField( + markdown: """ + VimR can compute the glyphs concurrently. This will result in faster rendering, + but also in higher CPU usage when scrolling very fast. + """ + ) + let sensitivityTitle = self.titleTextField(title: "Scroll Sensitivity:") let sensitivity = self.sensitivitySlider sensitivity.maxValue = 1 / 5.0 @@ -142,6 +159,8 @@ class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate { self.addSubview(sensitivityInfo) self.addSubview(useLiveResize) self.addSubview(useLiveResizeInfo) + self.addSubview(drawsParallelBox) + self.addSubview(drawsParallelInfo) paneTitle.autoPinEdge(toSuperviewEdge: .top, withInset: 18) paneTitle.autoPinEdge(toSuperviewEdge: .left, withInset: 18) @@ -165,7 +184,14 @@ class AdvancedPref: PrefPane, UiComponent, NSTextFieldDelegate { useLiveResizeInfo.autoPinEdge(.left, to: .left, of: useLiveResize) useLiveResizeInfo.autoSetDimension(.width, toSize: 300) - useSnapshotUpdate.autoPinEdge(.top, to: .bottom, of: useLiveResizeInfo, withOffset: 18) + drawsParallelBox.autoPinEdge(.top, to: .bottom, of: useLiveResizeInfo, withOffset: 18) + drawsParallelBox.autoPinEdge(.left, to: .right, of: sensitivityTitle, withOffset: 5) + + drawsParallelInfo.autoPinEdge(.top, to: .bottom, of: drawsParallelBox, withOffset: 5) + drawsParallelInfo.autoPinEdge(.left, to: .left, of: drawsParallelBox) + drawsParallelInfo.autoSetDimension(.width, toSize: 300) + + useSnapshotUpdate.autoPinEdge(.top, to: .bottom, of: drawsParallelInfo, withOffset: 18) useSnapshotUpdate.autoPinEdge(.left, to: .right, of: sensitivityTitle, withOffset: 5) useSnapshotUpdateInfo.autoPinEdge(.top, to: .bottom, of: useSnapshotUpdate, withOffset: 5) @@ -188,6 +214,10 @@ extension AdvancedPref { self.emit(.setUseLiveResize(sender.boolState)) } + @objc func drawParallelAction(_ sender: NSButton) { + self.emit(.setDrawsParallel(sender.boolState)) + } + @objc func sensitivitySliderAction(_ sender: NSSlider) { self.emit(.setTrackpadScrollResistance(1 / sender.doubleValue)) } diff --git a/VimR/VimR/States.swift b/VimR/VimR/States.swift index d82cf053..10b051fa 100644 --- a/VimR/VimR/States.swift +++ b/VimR/VimR/States.swift @@ -263,6 +263,7 @@ extension MainWindow { var trackpadScrollResistance = 5.0 var useLiveResize = false + var drawsParallel = false var isTemporarySession = false @@ -308,6 +309,7 @@ extension MainWindow { case trackpadScrollResistance = "trackpad-scroll-resistance" case useInteractiveZsh = "use-interactive-zsh" case useLiveResize = "use-live-resize" + case drawsParallel = "draws-parallel" case isShowHidden = "is-show-hidden" case appearance = "appearance" @@ -326,6 +328,7 @@ extension MainWindow { self.trackpadScrollResistance = try container.decode(forKey: .trackpadScrollResistance, default: State.default.trackpadScrollResistance) self.useLiveResize = try container.decode(forKey: .useLiveResize, default: State.default.useLiveResize) + self.drawsParallel = try container.decode(forKey: .drawsParallel, default: State.default.drawsParallel) if let frameRawValue = try container.decodeIfPresent(String.self, forKey: .frame) { self.frame = NSRectFromString(frameRawValue) } else { @@ -380,6 +383,7 @@ extension MainWindow { try container.encode(NSStringFromRect(self.frame), forKey: .frame) try container.encode(self.trackpadScrollResistance, forKey: .trackpadScrollResistance) try container.encode(self.useLiveResize, forKey: .useLiveResize) + try container.encode(self.drawsParallel, forKey: .drawsParallel) try container.encode(self.isLeftOptionMeta, forKey: .isLeftOptionMeta) try container.encode(self.isRightOptionMeta, forKey: .isRightOptionMeta) try container.encode(self.useInteractiveZsh, forKey: .useInteractiveZsh)