从专用渲染线程/循环渲染到CAMetalLayer

时间:2019-01-02 13:49:46

标签: macos cocoa metal metalkit

在Windows World中,专用的渲染线程会循环执行以下操作:

void RenderThread()
{
    while (!quit)
    {
        UpdateStates();
        RenderToDirect3D();
        // Can either present with no synchronisation,
        // or synchronise after 1-4 vertical blanks.
        // See docs for IDXGISwapChain::Present
        PresentToSwapChain();
    }
}

可可中CAMetalLayer的等效内容是什么?所有示例都处理在主线程中完成的更新,或者使用MTKView(带有内部计时器),或者在iOS示例中使用CADisplayLink

我想控制整个渲染循环,而不是仅以某个未指定的时间间隔接收回调(并且如果启用了V-Sync,则最好阻止它)。

2 个答案:

答案 0 :(得分:4)

在某种程度上,可绘制对象的可用性会限制您的工作。 CAMetalLayer具有固定的可绘制对象池,调用nextDrawable将阻塞当前线程,直到可绘制对象可用为止。不过,这并不意味着您必须在渲染循环的顶部调用nextDrawable

如果您希望按自己的时间表进行绘制而不会在等待绘制对象时被阻塞,请渲染到屏幕外的渲染缓冲区(即,MTLTexture的尺寸与您的绘制对象大小匹配),然后从最大-最近绘制的纹理到可绘制对象的纹理,并以您喜欢的任何节奏显示。这对于获取帧定时很有用,但是绘制然后不显示的每一帧都是浪费工作。它还增加了颤抖的风险。

在获取与垂直同步节奏匹配的回调时,您的选择受到限制。最好的办法是几乎可以肯定,CVDisplayLink是在默认运行和跟踪运行循环模式下安排的,尽管它有caveats

如果您想自由运行而又不走得太远,可以将计数信号灯与显示链接配合使用。

如果您的应用程序能够保持实时帧速率,则通常会在玻璃板上渲染一帧或两帧,因此您不想在垂直同步上进行阻塞;您只想通知窗口服务器您希望演示文稿与垂直同步匹配。在macOS上,您可以通过将图层的displaySyncEnabled设置为true(默认值)来完成此操作。关闭此功能可能会导致某些显示器破裂。

答案 1 :(得分:2)

在要渲染到屏幕上的位置,您可以通过调用nextDrawable从图层获取可绘制对象。您可以从其texture属性获得可绘制的纹理。您可以使用该纹理来设置MTLRenderPassDescriptor的渲染目标(颜色附件)。例如:

id<CAMetalDrawable> drawable = layer.nextDrawable;
id<MTLTexture> texture = drawable.texture;
MTLRenderPassDescriptor *desc = [MTLRenderPassDescriptor renderPassDescriptor];
desc.colorAttachments[0].texture = texture;

从这里开始,这与您在MTKView的{​​{1}}方法中所做的非常相似。创建命令缓冲区(如果还没有),使用描述符创建渲染命令编码器,编码绘图命令,结束编码,告诉命令缓冲区显示可绘制对象(使用drawRect:方法) ,并提交命令缓冲区。无论绘制到可绘制对象的纹理上的是什么,呈现时最终都会在屏幕上显示出来。

我同意沃伦(Warren)的观点,您可能真的不想将循环与显示刷新同步。您需要并行性。您希望CPU在GPU渲染最新帧(并且显示器显示最后一帧)时在下一帧上工作。

一次可运行的可绘制对象数量受到限制并且-presentDrawable:...会阻止等待一个事实,这将防止渲染循环过快。 (在此之前,您可能会使用其他一些同步方式,例如管理一个小的缓冲池。)如果只需要双缓冲而不是三缓冲,则可以将层的nextDrawable设置为2而不是其层。默认值为3。