1
1
mirror of https://github.com/qvacua/vimr.git synced 2024-12-24 22:33:52 +03:00

Add a flag for parallel rendering

This commit is contained in:
Tae Won Ha 2019-03-05 18:35:51 +01:00
parent 05c351c09e
commit 653026720b
No known key found for this signature in database
GPG Key ID: E40743465B5B8B44
6 changed files with 86 additions and 36 deletions

View File

@ -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..<attrsRuns.count {
self.draw(
@ -58,9 +56,10 @@ final class AttributesRunDrawer {
in: context
)
}
#endif
}
private let typesetter = Typesetter()
private func draw(
_ run: AttributesRun,
fontGlyphRuns: [FontGlyphRun],
@ -144,8 +143,6 @@ final class AttributesRunDrawer {
context.strokePath()
}
private let typesetter = Typesetter()
private func draw(
backgroundFor run: AttributesRun,
with defaultAttributes: CellAttributes,
@ -196,27 +193,6 @@ final class AttributesRunDrawer {
return fontGlyphRuns
}
private func drawByParallelComputation(
_ attrsRuns: [AttributesRun],
defaultAttributes: CellAttributes,
offset: CGPoint,
`in` context: CGContext
) {
var result = Array(repeating: [FontGlyphRun](), count: attrsRuns.count)
DispatchQueue.concurrentPerform(iterations: attrsRuns.count) { i in
result[i] = self.fontGlyphRuns(from: attrsRuns[i], offset: offset)
}
attrsRuns.enumerated().forEach { (i, attrsRun) in
self.draw(
attrsRun,
fontGlyphRuns: result[i],
defaultAttributes: defaultAttributes,
in: context
)
}
}
private func updateFontMetrics() {
self.cellSize = FontUtils.cellSize(
of: self.font, linespacing: self.linespacing

View File

@ -139,6 +139,12 @@ public class NvimView: NSView,
}
}
public var drawsParallel = false {
didSet {
self.drawer.drawsParallel = self.drawsParallel
}
}
public var linespacing: CGFloat {
get {
return self._linespacing

View File

@ -16,6 +16,36 @@ extension Array where Element: Hashable {
extension RandomAccessCollection where Index == Int {
func parallelMap<T>(
chunkSize: Int = 1,
_ transform: @escaping (Element) -> T
) -> [T] {
let count = self.count
guard count > chunkSize else { return self.map(transform) }
var result = Array<T?>(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..<end).forEach { i in pointer[i] = transform(self[i]) }
}
}
}
return result.map { $0! }
}
func groupedRanges<T: Equatable>(
with marker: (Index, Element) -> T
) -> [CountableClosedRange<Index>] {

View File

@ -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

View File

@ -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))
}

View File

@ -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)