And*_*000 9 core-animation redraw calayer nsview nsopenglview
我有一个窗口,其中包含类型的主视图NSView和子视图,子视图NSOpenGLView的名称是子类CustomOpenGLView.子类NSOpenGLView是通过Custom ViewInterface Builder并通过将其类设置为获得的CustomOpenGLView.这是根据Apple Sample Code Layer Backed OpenGLView制作的.
该应用程序用于向OpenGLContext绘制一些东西,比方说,0.05秒.禁用核心动画层后,我能够在视图中看到移动对象,这是连续重绘视图的结果.一切都完美无瑕.
我现在希望在CustomOpenGLView播放/停止/ ecc等房子控制按钮的顶部有一个半透明的视图.
为此,我添加了一个子视图,CustomOpenGLView并启用了Core Animation Layer CustomOpenGLView.控制按钮位于此新子视图中.
这样,带有控制按钮的视图正确显示在顶部,CustomOpenGLView但现在视图不会重绘.仅当我调整包含所有这些视图的窗口时,它才会绘制.
结果是我没有看到任何"动画"...我只看到一个静止图像,它表示绘图循环开始时绘制的第一帧.如果我调整窗口大小,openGLContext会重新绘制,直到我停止调整窗口大小.之后,我再次看到一张静止图像,在调整大小期间发生了最后一次绘图.
此外,当绘图循环开始时,屏幕上只显示第一个"框架",如果我调整窗口大小,让我们说,5秒钟之后,我会在视图中看到它应该在开始后5秒内准确绘制的内容.绘制循环.好像我需要设置[glView setNeedsDisplay:TRUE].我这样做但没有任何改变.
哪里出错了?为什么添加核心动画层会破坏重绘?这是否意味着我没有得到什么?
Mec*_*cki 24
当你有一个法线时NSOpenGLView,你可以简单地通过OpenGL绘制一些东西,然后调用-flushBuffer它NSOpenGLContext来使渲染出现在屏幕上.如果你的上下文没有双缓冲,如果你渲染到一个窗口就没有必要,因为所有的窗口都已经在MacOS X中自己进行了双重缓冲,所以调用glFlush()就足够了(只有真正的全屏OpenGL渲染,你需要双倍缓冲以避免伪影).然后OpenGL将直接渲染到视图的像素存储中(实际上是窗口的后备存储),或者在双缓冲的情况下,它将渲染到后缓冲区,然后将其与前缓冲区交换; 因此,新内容立即在屏幕上可见(实际上不是在下一次屏幕刷新之前,但是这样的刷新每秒至少发生50-60次).
如果NSOpenGLView是层支持的,情况会有所不同.当你调用-flushBuffer或者glFlush()渲染确实发生时,就像它前后一样,图像直接渲染到视图的像素存储,但是,这个像素存储不再是窗口的后备存储,它是视图的"后备层".因此,您的OpenGL图像会更新,您只是看不到它发生,因为"绘制到图层"和"在屏幕上显示图层"是两个完全不同的东西!要使新图层内容可见,您必须调用setNeedsDisplay:YES图层支持的图层NSOpenGLView.
你打电话时为什么不适合你setNeedsDisplay:YES?首先,确保在主线程上执行此调用.您可以在任何您喜欢的线程上执行此调用,它肯定会将视图标记为脏,但只有在主线程上执行此调用时,它还会为其调度重绘调用(没有该调用它被标记为脏但是它在重新绘制任何其他父/子视图之前,不会重绘.)另一个问题可能是该drawRect:方法.当您将视图标记为脏并重新绘制时,将调用此方法,并且此方法"绘制"的任何内容都会覆盖当前层内的任何内容.只要您的视图不是图层支持的,您在何处呈现OpenGL内容并不重要,但对于图层支持的视图,这实际上是您应该执行所有绘图的方法.
请尝试以下操作:NSTimer在主线程上创建一个每20毫秒触发一次并调用一个方法来调用setNeedsDisplay:YES支持图层的方法NSOpenGLView.将所有OpenGL渲染代码移动到drawRect:图层支持的方法中NSOpenGLView.这应该工作得很好.如果你需要比a更可靠的东西NSTimer,试试CVDisplayLink(CV = CoreVideo).A CVDisplayLink就像一个计时器,但每次重新绘制屏幕时它都会触发.
分层的NSOpenGLView有点过时了,从10.6开始它们不再是真正需要的了.在内部,NSOpenGLView在你使它成为分层时创建一个NSOpenGLLayer,所以你也可以自己直接使用这样一个层并"构建"你自己的NSOpenGLView:
NSOpenGLLayer,让我们调用它MyOpenGLLayerNSView,让我们调用它MyGLView- (CALayer *)makeBackingLayer以返回自动释放的实例MyOpenGLLayerwantsLayer:YES为MyGLView您现在拥有自己的图层支持视图,它由NSOpenGLLayer子类支持.由于它是图层支持,因此可以向其添加子视图(例如按钮,文本字段等).
对于您的支持层,您基本上有两个选项.
选项1
正确且官方支持的方法是将渲染保留在主线程上.因此,您必须执行以下操作:
canDrawInContext:...返回YES/ NO,取决于您是否可以/想要绘制下一帧.drawInContext:...以执行实际的OpenGL渲染.setAsynchronous:YES)setNeedsDisplayOnBoundsChange:YES)时"更新" ,否则在调整图层大小时不会调整OpenGL背景表面的大小(并且每次重绘图层时必须拉伸/缩小渲染的OpenGL上下文)Apple将为CVDisplayLink您创建一个,canDrawInContext:...每次触发时调用主线程,如果此方法返回YES,则调用drawInContext:....这就是你应该怎么做的方式.
如果您的渲染在主线程上太昂贵,您可以执行以下操作:覆盖openGLContextForPixelFormat:...以创建与您之前创建的另一个上下文共享的上下文(上下文B)(上下文A).在Context A中创建一个帧缓冲区(你可以在创建Context B之前或之后做到这一点,它并不重要); 如果需要,附加深度和/或模板渲染缓冲区(选择的位深度),但是不要使用颜色渲染缓冲区,而是将"纹理"(纹理X)附加为颜色附件(glFramebufferTexture()).现在,渲染到该帧缓冲区时,所有颜色渲染输出都会写入该纹理.在您选择的任何线程上使用上下文A对此帧缓冲执行所有渲染!渲染完成后,canDrawInContext:...返回YES并在drawInContext:...刚绘制一个简单的四边形,填充整个活动帧缓冲区(Apple已经为您设置了它,并且视口也完全填充它),并且纹理X纹理化.这是可能的,因为共享上下文也共享所有对象(例如像纹理,帧缓冲区等).所以你的drawInContext:...方法永远不会比绘制一个简单的纹理四边形更重要.所有其他(可能是昂贵的渲染)都发生在后台线程上的这个纹理上,而且没有阻塞你的主线程.
选项2
Apple未正式支持其他选项,可能适用于您,也可能不适用于您:
canDrawInContext:...,默认实现总是返回YES,这就是你想要的.drawInContext:...以执行所有实际的OpenGL渲染.needsDisplayOnBoundsChange.每当你想重新绘制这个图层时,display直接打电话(不是 setNeedsDisplay!这是真的,Apple说你不应该打电话给它,但"不应该"不是"绝对不")并且在打电话后display,打电话[CATransaction flush].这将工作,即使从后台线程调用!drawInContext:...从调用display哪个线程的同一线程调用您的方法.display直接调用将确保您的OpenGL渲染代码执行,但新渲染的内容仍然只能在图层的后备存储中可见,要将其带到屏幕,您必须强制系统执行图层合成,并且[CATransaction flush]将完全执行此操作.CATransaction类只有类方法(你永远不会创建它的实例)是隐式线程安全的,并且可以随时从任何线程使用它(它随时随地执行锁定).
虽然不建议使用此方法,因为它可能会导致其他视图的重绘问题(因为这些问题也可能会在主线程以外的线程上重新绘制而不是所有视图都支持),它也不被禁止,它不使用私有API,它已经在苹果邮件列表上建议没有任何人在Apple反对它.
| 归档时间: |
|
| 查看次数: |
5995 次 |
| 最近记录: |