perf(editor): extend textkit profiling probes
This commit is contained in:
parent
c02bac3c2d
commit
26ed4956e1
1 changed files with 81 additions and 0 deletions
|
|
@ -391,6 +391,31 @@ public enum EditorBenchmarkProfiler {
|
||||||
notes: "\(attributedStringResult.value.styledLineCount) styled lines"
|
notes: "\(attributedStringResult.value.styledLineCount) styled lines"
|
||||||
))
|
))
|
||||||
|
|
||||||
|
let coldViewportStack = makeTextKitStack(attributedString: textStorage)
|
||||||
|
let coldViewportResult = measure {
|
||||||
|
coldViewportStack.layoutManager.glyphRange(
|
||||||
|
forBoundingRect: NSRect(x: 0, y: 50_000, width: 760, height: 900),
|
||||||
|
in: coldViewportStack.textContainer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
measurements.append(EditorBenchmarkMeasurement(
|
||||||
|
name: "cold_viewport_glyph_range_middle",
|
||||||
|
category: .layout,
|
||||||
|
durationMilliseconds: coldViewportResult.durationMilliseconds,
|
||||||
|
notes: "Visible glyph range before explicit full-document layout"
|
||||||
|
))
|
||||||
|
|
||||||
|
let coldLineFragmentStack = makeTextKitStack(attributedString: textStorage)
|
||||||
|
let coldLineFragmentResult = measure {
|
||||||
|
lineFragmentRect(atCharacterLocation: coldLineFragmentStack.textStorage.length / 2, in: coldLineFragmentStack)
|
||||||
|
}
|
||||||
|
measurements.append(EditorBenchmarkMeasurement(
|
||||||
|
name: "cold_line_fragment_calculation_midpoint",
|
||||||
|
category: .layout,
|
||||||
|
durationMilliseconds: coldLineFragmentResult.durationMilliseconds,
|
||||||
|
notes: "lineFragmentRect near midpoint before explicit full-document layout"
|
||||||
|
))
|
||||||
|
|
||||||
let layoutInvalidationResult = measure {
|
let layoutInvalidationResult = measure {
|
||||||
var actualRange = NSRange(location: 0, length: 0)
|
var actualRange = NSRange(location: 0, length: 0)
|
||||||
layoutManager.invalidateLayout(
|
layoutManager.invalidateLayout(
|
||||||
|
|
@ -425,6 +450,30 @@ public enum EditorBenchmarkProfiler {
|
||||||
notes: "NSLayoutManager.ensureLayout for text container"
|
notes: "NSLayoutManager.ensureLayout for text container"
|
||||||
))
|
))
|
||||||
|
|
||||||
|
let cachedLineFragmentResult = measure {
|
||||||
|
lineFragmentRect(atCharacterLocation: textStorage.length / 2, in: (
|
||||||
|
textStorage: textStorage,
|
||||||
|
layoutManager: layoutManager,
|
||||||
|
textContainer: textContainer
|
||||||
|
))
|
||||||
|
}
|
||||||
|
measurements.append(EditorBenchmarkMeasurement(
|
||||||
|
name: "cached_line_fragment_calculation_midpoint",
|
||||||
|
category: .layout,
|
||||||
|
durationMilliseconds: cachedLineFragmentResult.durationMilliseconds,
|
||||||
|
notes: "lineFragmentRect near midpoint after full layout"
|
||||||
|
))
|
||||||
|
|
||||||
|
let textContainerUsedRectResult = measure {
|
||||||
|
layoutManager.usedRect(for: textContainer)
|
||||||
|
}
|
||||||
|
measurements.append(EditorBenchmarkMeasurement(
|
||||||
|
name: "text_container_used_rect_after_full_layout",
|
||||||
|
category: .layout,
|
||||||
|
durationMilliseconds: textContainerUsedRectResult.durationMilliseconds,
|
||||||
|
notes: "NSTextContainer usedRect after full layout"
|
||||||
|
))
|
||||||
|
|
||||||
let firstViewportResult = measure {
|
let firstViewportResult = measure {
|
||||||
layoutManager.glyphRange(
|
layoutManager.glyphRange(
|
||||||
forBoundingRect: NSRect(x: 0, y: 0, width: 760, height: 900),
|
forBoundingRect: NSRect(x: 0, y: 0, width: 760, height: 900),
|
||||||
|
|
@ -544,5 +593,37 @@ public enum EditorBenchmarkProfiler {
|
||||||
_ = changedSource
|
_ = changedSource
|
||||||
return measurements
|
return measurements
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func makeTextKitStack(attributedString: NSAttributedString) -> (
|
||||||
|
textStorage: NSTextStorage,
|
||||||
|
layoutManager: NSLayoutManager,
|
||||||
|
textContainer: NSTextContainer
|
||||||
|
) {
|
||||||
|
let textStorage = NSTextStorage(attributedString: attributedString)
|
||||||
|
let layoutManager = NSLayoutManager()
|
||||||
|
let textContainer = NSTextContainer(size: NSSize(width: 760, height: CGFloat.greatestFiniteMagnitude))
|
||||||
|
textContainer.widthTracksTextView = false
|
||||||
|
textContainer.heightTracksTextView = false
|
||||||
|
layoutManager.addTextContainer(textContainer)
|
||||||
|
textStorage.addLayoutManager(layoutManager)
|
||||||
|
return (textStorage, layoutManager, textContainer)
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
private static func lineFragmentRect(
|
||||||
|
atCharacterLocation characterLocation: Int,
|
||||||
|
in stack: (
|
||||||
|
textStorage: NSTextStorage,
|
||||||
|
layoutManager: NSLayoutManager,
|
||||||
|
textContainer: NSTextContainer
|
||||||
|
)
|
||||||
|
) -> NSRect {
|
||||||
|
guard stack.textStorage.length > 0 else { return .zero }
|
||||||
|
|
||||||
|
let characterLocation = max(0, min(characterLocation, stack.textStorage.length - 1))
|
||||||
|
let glyphIndex = stack.layoutManager.glyphIndexForCharacter(at: characterLocation)
|
||||||
|
var effectiveRange = NSRange(location: 0, length: 0)
|
||||||
|
return stack.layoutManager.lineFragmentRect(forGlyphAt: glyphIndex, effectiveRange: &effectiveRange)
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue