2

我正在尝试在现有纹理的顶部放置许多叠加层(纹理)。在大多数情况下,这工作正常。

但是,对于我的生活,我无法弄清楚为什么在我drawRectMTKView. 一切似乎都很好;theTexture在循环放置叠加层后,我会(在内核着色器中)进行进一步处理。出于某种原因,我觉得这种编码提前结束了,而且还没有完成足够的工作。

澄清一下,一切开始都很好,但大约 5 秒后,闪烁开始并逐渐恶化。出于调试目的(现在,无论如何),循环只运行一次——只有一个覆盖元素。theTexture每次开始之前,输入纹理 ( ) 都是真实的(使用 storageModeMTLStorageModeManaged和 usage的描述符创建MTLTextureUsageUnknown)。

我还尝试将编码器实例化/结束填充到循环中;没有不同。

有人可以帮我看看我做错了什么吗?

id<MTLTexture> theTexture; // valid input texture as "background"

MTLRenderPassDescriptor *myRenderPassDesc = [MTLRenderPassDescriptor renderPassDescriptor];
myRenderPassDesc.colorAttachments[0].texture = theTexture;
myRenderPassDesc.colorAttachments[0].storeAction = MTLStoreActionStore;
myRenderPassDesc.colorAttachments[0].loadAction = MTLLoadActionLoad;

id<MTLRenderCommandEncoder> myEncoder = [commandBuffer renderCommandEncoderWithDescriptor:myRenderPassDesc];
MTLViewport viewPort = {0.0, 0.0, 1920.0, 1080.0, -1.0, 1.0};
vector_uint2 imgSize = vector2((u_int32_t)1920,(u_int32_t)1080);
[myEncoder setViewport:viewPort];
[myEncoder setRenderPipelineState:metalVertexPipelineState];

for (OverlayWrapper *ow in overlays) {
    id<MTLTexture> overlayTexture = ow.overlayTexture;

    VertexRenderSet *v = [ow getOverlayVertexInfoPtr];
    NSUInteger vSize = v->metalVertexCount*sizeof(AAPLVertex);
    id<MTLBuffer> mBuff = [self.device newBufferWithBytes:v->metalVertices
                                                   length:vSize
                                                  options:MTLResourceStorageModeShared];

    [myEncoder setVertexBuffer:mBuff offset:0 atIndex:0];
    [myEncoder setVertexBytes:&imgSize length:sizeof(imgSize) atIndex:1];
    [myEncoder setFragmentTexture:overlayTexture atIndex:0];
    [myEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:v->metalVertexCount];
}

[myEncoder endEncoding];

// do more work (kernel shader) with "theTexture"...


更新#1: 我附上了一个“好”框架的图像,显​​示了顶点区域(右下角)。我的编码器负责将绿色替代“图像”theTexture以 30fps 的速度放置在视频帧的顶部,它确实这样做了。只是为了澄清,theTexture为每一帧创建(来自 CoreVideo 像素缓冲区)。在这个编码器之后,我只从theTexture内核着色器中读取来调整亮度——一切都很好。

我的问题必须存在于其他地方,因为视频帧停止流动(尽管音频继续播放),并且一旦插入此编码器,我最终会在 2 或 3 个先前的帧之间交替(因此,闪烁)。我现在相信我的视频像素缓冲区供应商无意中被这个“覆盖”供应商所取代。

如果我注释掉整个顶点渲染器,我的视频帧就会很好地流过;这不是我的视频帧供应商的问题。

一个样品

更新#2:

这是我的渲染管道的声明:

MTLRenderPipelineDescriptor *p = [[MTLRenderPipelineDescriptor alloc] init];
if (!p)
    return nil;
p.label = @"Vertex Mapping Pipeline";
p.vertexFunction   = [metalLibrary newFunctionWithName:@"vertexShader"];
p.fragmentFunction = [metalLibrary newFunctionWithName:@"samplingShader"];
p.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;

NSError *error;
metalVertexPipelineState = [self.device newRenderPipelineStateWithDescriptor:p
                                                                       error:&error];
if (error || !metalVertexPipelineState)
    return nil;

这是用于创建的纹理描述符theTexture

metalTextureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm 
                                                                            width:width
                                                                           height:height
                                                                        mipmapped:NO];
metalTextureDescriptor.storageMode = MTLStorageModePrivate;
metalTextureDescriptor.usage = MTLTextureUsageUnknown;

由于这个原因,我没有包含AAPLVertex和 顶点/片段函数:如果我只是在我的渲染代码中注释掉OverlayWrapper循环(即,甚至不设置顶点缓冲区或绘制图元),视频帧仍然会闪烁。从该编码器“运行”的时间开始,视频仍在播放,但只有 2-3 帧左右在连续循环中播放。

我还在 之后添加了此代码,[... endEncoding]并将纹理使用更改为MTLStorageModeManaged- 仍然,没有骰子:

id<MTLBlitCommandEncoder> blitEncoder = [commandBuffer blitCommandEncoder];
[blitEncoder synchronizeResource:crossfadeOutput];
[blitEncoder endEncoding];

澄清一些事情:随后的计算机着色器theTexture仅用于输入。这些是视频帧;因此,theTexture每次都重新创建。在它经过这个渲染阶段之前,它有一个真正的“背景”


更新#3:

我得到了这个工作,如果通过非常规的方式。

我使用这个顶点着色器将我的叠加层渲染到新创建的空白纹理的透明背景上,特别是我loadActionMTLLoadActionClearclearColor 为 (0,0,0,0)。

然后我将这个生成的纹理与我theTexture的内核着色器混合。我应该这样做,但它有效!

4

1 回答 1

2

我有同样的问题,想在尝试@zzyzy 之前探索一个更简单的解决方案。该解决方案也有些令人不满意,但至少似乎有效。

关键(但本身并不充分)是减少金属层上的缓冲:

metalLayer_.maximumDrawableCount = 2

其次,一旦缓冲减少,我发现我必须通过一个渲染/呈现/提交周期来绘制一个微不足道的、不可见的项目,.clear并在渲染通道描述符上设置——非常简单:

renderPassDescriptor.colorAttachments[0].loadAction = .clear

(绘制了一些不可见的三角形可能无关紧要;这可能是MTLLoadActionClear区分通道的属性。我使用了与上面@zzyzy 相同的清晰颜色,我认为这与上述解决方案相呼应。)

第三,我发现我必须第二次通过渲染/呈现/提交周期运行代码——即连续两次。在这三个中,这似乎是最武断的,我不假装理解它,但三个一起为我工作。

于 2019-09-01T15:56:12.617 回答