Eni*_*nie 17 cocoa core-graphics objective-c cgcontext
我的课程是在屏幕外渲染图像.我认为重复使用CGContext而不是一次又一次地为每个图像创建相同的上下文将是一件好事.我设置了一个成员变量,_imageContext所以我只需要创建一个新的上下文,如果_imageContext是这样的话:
if(!_imageContext)
_imageContext = [self contextOfSize:imageSize];
Run Code Online (Sandbox Code Playgroud)
代替:
CGContextRef imageContext = [self contextOfSize:imageSize];
Run Code Online (Sandbox Code Playgroud)
当然我不再发布CGContext了.
这些是我所做的唯一改变,结果是重用上下文减慢了从大约10ms到60ms的渲染速度.我错过了什么吗?在重新插入之前,我是否必须清除上下文或其他内容?或者它是重建每个图像的上下文的正确方法?
编辑
找到最奇怪的连接..
当我在应用程序开始渲染图像时,我正在寻找应用程序内存难以置信地增加的原因,我发现问题在于我将渲染图像设置为NSImageView.
imageView.image = nil;
imageView.image = [[NSImage alloc] initWithCGImage:_imageRef size:size];
Run Code Online (Sandbox Code Playgroud)
看起来ARC并没有发布以前的版本NSImage.避免这种情况的第一种方法是将新图像绘制成旧图像.
[imageView.image lockFocus];
[[[NSImage alloc] initWithCGImage:_imageRef size:size] drawInRect:NSMakeRect(0, 0, size.width, size.height) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
[imageView.image unlockFocus];
[imageView setNeedsDisplay];
Run Code Online (Sandbox Code Playgroud)
内存问题已经消失了,并且CGContext-reuse问题发生了什么?不重复使用上下文现在需要20ms而不是10ms - 当然,绘制到图像所需的时间比设置它要长.重用上下文也需要20ms而不是60ms.但为什么?我没有看到可能有任何连接,但我可以重现旧的状态,重用只需要设置NSImageView图像而不是绘制图像.
ipm*_*mcc 15
我对此进行了调查,并观察到同样的减速情况.使用设置为内核调用示例的仪器以及用户空调调用显示了罪魁祸首.@ RyanArtecona的评论是在正确的轨道上.我将Instruments集中CGSColorMaskCopyARGB8888_sse在两个测试运行中的最底层用户空调(一个重用上下文,另一个每次创建一个新的),然后反转生成的调用树.在不重用上下文的情况下,我看到最重的内核跟踪是:
Running Time Self Symbol Name
668.0ms 32.3% 668.0 __bzero
668.0ms 32.3% 0.0 vm_fault
668.0ms 32.3% 0.0 user_trap
668.0ms 32.3% 0.0 CGSColorMaskCopyARGB8888_sse
Run Code Online (Sandbox Code Playgroud)
这是内核将CGSColorMaskCopyARGB8888_sse访问它们的内存页面归零.这意味着CGContext将VM页面映射到后面的位图上下文,但是在有人实际访问该内存之前,内核实际上并不执行与该操作相关的工作.第一次访问时会发生实际的映射/故障.
现在让我们看看当我们重用上下文时最重的内核跟踪:
Running Time Self Symbol Name
1327.0ms 35.0% 1327.0 bcopy
1327.0ms 35.0% 0.0 user_trap
1327.0ms 35.0% 0.0 CGSColorMaskCopyARGB8888_sse
Run Code Online (Sandbox Code Playgroud)
这是内核复制页面.我的钱就是这个底层的写时复制机制,它提供了@RyanArtecona在他的评论中所讨论的行为:
在CGBitmapContextCreateImage的Apple文档中,它表示直到在原始上下文上进行更多绘制才会发生实际的位复制操作.
在我曾经测试的设计案例中,非重用案例需要3392ms才能执行,重用案例需要4693ms(明显更慢).考虑到每种情况下单个最重的跟踪,内核跟踪表明我们在第一次访问时花费668.0ms零填充新页面,并且在图像获得引用后在第一次写入时写入写入时复制页面1327.0ms那些页面.这相差659ms.仅这一个差异就占了两个案例之间差距的约50%.
因此,为了将其提取一点,非重用的上下文会更快,因为当您创建上下文时,它知道页面是空的,并且没有其他人提到这些页面以强制它们在您写入时被复制他们.当您重复使用上下文时,页面会被其他人(您创建的图像)引用,并且必须在第一次写入时复制,以便在上下文状态发生更改时保留图像的状态.
您可以通过在调试器中逐步查看进程的虚拟内存映射来进一步了解此处发生的情况.vmmap是有用的工具.
实际上,你应该每次只创建一个新的CGContext.
为了补充@ ipmcc的优秀和彻底的答案,这里是一个教学概述.
在Apple的文档CGBitmapContextCreateImage中说明:
CGImage此函数返回的对象由复制操作创建.在某些情况下,复制操作实际上遵循写时复制语义,因此只有在位图图形上下文中的基础数据被修改时才会发生位的实际物理复制.
因此,当调用此函数时,可能不会立即复制图像的基础位,而是可能在下次修改位图上下文时等待复制.这种位复制可能很昂贵(取决于上下文的大小和颜色空间),并且可能在仪器配置文件中伪装成自己在CGContext...上下文中被调用的任何绘图函数的一部分(当位被强制复制时).这可能是CGContextDrawImage.k 发生的事情
但是,文档继续这样说:
因此,在对位图图形上下文执行其他绘制之前,您可能希望使用生成的图像并将其释放.这样,您就可以避免数据的实际物理副本.
这意味着,如果您需要在上下文中进行更多绘制时,您将完成使用内存中创建的图像(即它已保存到磁盘,通过网络发送等),图像将永远不需要完全被复制!
如果在某些时候你需要拉出一个CGImage位图上下文,并且你不需要保留任何对它的引用(包括将它设置为一个UIImageView图像),然后再在上下文中进行绘图,那么它是使用可能是一个好主意CGBitmapContextCreateImage.如果没有,您的图像将在某个时刻进行物理复制,这可能需要一段时间,而且每次只使用新的上下文可能会更好.
| 归档时间: |
|
| 查看次数: |
1465 次 |
| 最近记录: |