CALayer drawinContext 调用@60fps 但查看更新图形@1fps
CALayer drawinContext called @ 60fps but view update graphics @ 1fps
我正在尝试使用 Cocoa 在 OSX 中实现绘图视图,并在使用 NSBezierPath 和 add/delete 数据点时使用 Quartz 框架。
在 drawRect 中这样做效果很好,因为图表经常更新,但是当我需要增加总 datapoints/sampling 速率时遇到性能问题。
我决定移动到 drawLayer: inContext: 但由于该函数以 60fps 的速度调用,因此在调用该函数时视图不会更新图形,而是以 1fps 的速度更新。
我做错了什么?
class CustomDrawLayer: CALayer {
convenience init(view: NSView, drawsAsynchronously : Bool = false) {
self.init()
self.bounds = view.bounds
self.anchorPoint = CGPointZero
self.opaque = false
self.frame = view.frame
self.drawsAsynchronously = drawsAsynchronously
// for multiple draws in hosting view
// self.delegate = self
}
override func actionForLayer(layer: CALayer, forKey event: String) -> CAAction? {
return nil
}}
override func drawLayer(layer: CALayer, inContext ctx: CGContext) {
if layer == self.layer {
Swift.print("axes drawing")
graphBounds.origin = self.frame.origin
graphAxes.drawAxesInRect(graphBounds, axeOrigin: plotOrigin, xPointsToShow: CGFloat(totalSecondsToDisplay), yPointsToShow: CGFloat(totalChannelsToDisplay))
}
if layer == self.board {
Swift.print(1/NSDate().timeIntervalSinceDate(fpsTimer))
fpsTimer = NSDate()
drawPointsInGraph(graphAxes, context: ctx)
}
}
func drawPointsInGraph(axes: AxesDrawer, context: CGContext)
{
color.set()
var x : CGFloat = 0
var y : CGFloat = 0
for var channel = 0; channel < Int(totalChannelsToDisplay); channel++ {
path.removeAllPoints()
var visibleIndex = (dirtyRect.origin.x - axes.position.x) / (axes.pointsPerUnit.x / samplingRate)
if visibleIndex < 2 {
visibleIndex = 2
}
for var counter = Int(visibleIndex); counter < dataStream![channel].count; counter++ {
if dataStream![channel][counter] == 0 {
if path.elementCount > 0 {
path.stroke()
}
break
}
let position = axes.position
let ppY = axes.pointsPerUnit.y
let ppX = axes.pointsPerUnit.x
let channelYLocation = CGFloat(channel)
x = position.x + CGFloat(counter-1) * (ppX / samplingRate)
y = ((channelYLocation * ppY) + position.y) + (dataStream![channel][counter-1] * (ppY))
path.moveToPoint(CGPoint(x: align(x), y: align(y)))
x = position.x + CGFloat(counter) * (ppX / samplingRate)
y = ((channelYLocation * ppY) + position.y) + (dataStream![channel][counter] * (ppY) )
path.lineToPoint(CGPoint(x: align(x), y: align(y)))
if x > (axes.position.x + axes.bounds.width) * 0.9 {
graphAxes.forwardStep = 5
dirtyRect = graphBounds
for var c = 0; c < Int(totalChannelsToDisplay); c++ {
for var i = 0; i < Int(samplingRate) * graphAxes.forwardStep; i++
{
dataStream![c][i] = 0
}
}
return
}
}
path.stroke()
}
if inLiveResize {
dirtyRect = graphBounds
} else {
dirtyRect.origin.x = x
dirtyRect.origin.y = bounds.minY
dirtyRect.size.width = 10
dirtyRect.size.height = bounds.height
}
}
如果您的路径需要如此频繁地绘制,请查看 CAShapeLayer,您可以在其中更改路径 属性。这将是硬件加速的,并且比 drawRect 或 drawLayer 快得多。
以 60 Hz 的频率调用函数的情况非常罕见。在任何情况下,您都不应该尝试以 60 Hz 的频率调用绘图函数;这在 Cocoa 中毫无意义。如果您真的是指 "at the screen refresh interval,",请参阅 CADisplayLink
,它专门用于允许您以屏幕刷新间隔进行绘制。这可能比 60 Hz 慢。如果您尝试以 60 Hz 精确绘制,您可能会失去同步并导致动画出现节拍。但这实际上只适用于实时视频之类的东西。如果那是您所拥有的,那么这就是工具,但它听起来并不像它。
理解你的代码有点困难。目前尚不清楚您的 60fps 是从哪里来的。但我假设您正在尝试做的是动画绘制图形。如果是这样,如 Mark F 所述,请参阅 CAShapeLayer。它内置了自动路径动画,绝对是你想要的。它会自动处理计时并与屏幕刷新和 GPU 优化同步,以及许多您不应该尝试解决的其他事情。
即使 CAShapeLayer
不是您想要的,您也应该查看 Core Animation,它旨在与您一起对值进行动画处理并在必要时重新绘制。例如,它会自动处理在多个内核上渲染您的层,这将显着提高性能。有关更多信息,请参阅 Animating Custom Layer Properties.
我正在尝试使用 Cocoa 在 OSX 中实现绘图视图,并在使用 NSBezierPath 和 add/delete 数据点时使用 Quartz 框架。
在 drawRect 中这样做效果很好,因为图表经常更新,但是当我需要增加总 datapoints/sampling 速率时遇到性能问题。
我决定移动到 drawLayer: inContext: 但由于该函数以 60fps 的速度调用,因此在调用该函数时视图不会更新图形,而是以 1fps 的速度更新。
我做错了什么?
class CustomDrawLayer: CALayer {
convenience init(view: NSView, drawsAsynchronously : Bool = false) {
self.init()
self.bounds = view.bounds
self.anchorPoint = CGPointZero
self.opaque = false
self.frame = view.frame
self.drawsAsynchronously = drawsAsynchronously
// for multiple draws in hosting view
// self.delegate = self
}
override func actionForLayer(layer: CALayer, forKey event: String) -> CAAction? {
return nil
}}
override func drawLayer(layer: CALayer, inContext ctx: CGContext) {
if layer == self.layer {
Swift.print("axes drawing")
graphBounds.origin = self.frame.origin
graphAxes.drawAxesInRect(graphBounds, axeOrigin: plotOrigin, xPointsToShow: CGFloat(totalSecondsToDisplay), yPointsToShow: CGFloat(totalChannelsToDisplay))
}
if layer == self.board {
Swift.print(1/NSDate().timeIntervalSinceDate(fpsTimer))
fpsTimer = NSDate()
drawPointsInGraph(graphAxes, context: ctx)
}
}
func drawPointsInGraph(axes: AxesDrawer, context: CGContext)
{
color.set()
var x : CGFloat = 0
var y : CGFloat = 0
for var channel = 0; channel < Int(totalChannelsToDisplay); channel++ {
path.removeAllPoints()
var visibleIndex = (dirtyRect.origin.x - axes.position.x) / (axes.pointsPerUnit.x / samplingRate)
if visibleIndex < 2 {
visibleIndex = 2
}
for var counter = Int(visibleIndex); counter < dataStream![channel].count; counter++ {
if dataStream![channel][counter] == 0 {
if path.elementCount > 0 {
path.stroke()
}
break
}
let position = axes.position
let ppY = axes.pointsPerUnit.y
let ppX = axes.pointsPerUnit.x
let channelYLocation = CGFloat(channel)
x = position.x + CGFloat(counter-1) * (ppX / samplingRate)
y = ((channelYLocation * ppY) + position.y) + (dataStream![channel][counter-1] * (ppY))
path.moveToPoint(CGPoint(x: align(x), y: align(y)))
x = position.x + CGFloat(counter) * (ppX / samplingRate)
y = ((channelYLocation * ppY) + position.y) + (dataStream![channel][counter] * (ppY) )
path.lineToPoint(CGPoint(x: align(x), y: align(y)))
if x > (axes.position.x + axes.bounds.width) * 0.9 {
graphAxes.forwardStep = 5
dirtyRect = graphBounds
for var c = 0; c < Int(totalChannelsToDisplay); c++ {
for var i = 0; i < Int(samplingRate) * graphAxes.forwardStep; i++
{
dataStream![c][i] = 0
}
}
return
}
}
path.stroke()
}
if inLiveResize {
dirtyRect = graphBounds
} else {
dirtyRect.origin.x = x
dirtyRect.origin.y = bounds.minY
dirtyRect.size.width = 10
dirtyRect.size.height = bounds.height
}
}
如果您的路径需要如此频繁地绘制,请查看 CAShapeLayer,您可以在其中更改路径 属性。这将是硬件加速的,并且比 drawRect 或 drawLayer 快得多。
以 60 Hz 的频率调用函数的情况非常罕见。在任何情况下,您都不应该尝试以 60 Hz 的频率调用绘图函数;这在 Cocoa 中毫无意义。如果您真的是指 "at the screen refresh interval,",请参阅 CADisplayLink
,它专门用于允许您以屏幕刷新间隔进行绘制。这可能比 60 Hz 慢。如果您尝试以 60 Hz 精确绘制,您可能会失去同步并导致动画出现节拍。但这实际上只适用于实时视频之类的东西。如果那是您所拥有的,那么这就是工具,但它听起来并不像它。
理解你的代码有点困难。目前尚不清楚您的 60fps 是从哪里来的。但我假设您正在尝试做的是动画绘制图形。如果是这样,如 Mark F 所述,请参阅 CAShapeLayer。它内置了自动路径动画,绝对是你想要的。它会自动处理计时并与屏幕刷新和 GPU 优化同步,以及许多您不应该尝试解决的其他事情。
即使 CAShapeLayer
不是您想要的,您也应该查看 Core Animation,它旨在与您一起对值进行动画处理并在必要时重新绘制。例如,它会自动处理在多个内核上渲染您的层,这将显着提高性能。有关更多信息,请参阅 Animating Custom Layer Properties.