【发布时间】:2018-03-08 07:18:31
【问题描述】:
我正在尝试在现有纹理的顶部放置多个叠加层(纹理)。在大多数情况下,这可以正常工作。
但是,就我的一生而言,我无法弄清楚为什么在我的 drawRect 的 MTKView 方法中这个输出偶尔会“闪烁”。一切似乎都很好;在循环放置叠加层后,我对theTexture(在内核着色器中)进行进一步处理。出于某种原因,我觉得这种编码提前结束了,而且还没有完成足够的工作。
澄清一下,一切开始正常,但大约 5 秒后,闪烁开始并逐渐恶化。出于调试目的(现在,无论如何),循环只运行一次——只有一个覆盖元素。每次开始之前,输入纹理 (theTexture) 都是真实的(使用 storageMode 为 MTLStorageModeManaged 且使用为 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:
我附上了一个“好”框架的图像,显示了顶点区域(右下角)。我的编码器负责将绿色替代“图像”以 30fps 的速度放置在视频帧 theTexture 的顶部,它确实这样做了。澄清一下,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:
如果通过非常规的方式,我得到了这个工作。
我使用此顶点着色器将我的叠加层渲染到新创建的空白纹理的透明背景上,特别是我的 loadAction 是 MTLLoadActionClear,其 clearColor 为 (0,0,0,0)。
然后我将这个生成的纹理与我的theTexture 与内核着色器混合。我应该不必须这样做,但它有效!
【问题讨论】:
-
或许添加好帧和坏帧的截图。管道状态是如何设置的(即创建它的描述符是什么)?显示顶点和片段函数。显示
AAPLVertex的声明。计算着色器只是使用theTexture作为输入还是修改它?为什么这与显示无关?从您写的内容中不清楚:您是否为每一帧重新创建theTexture?你每次都以某种方式填充或清除它?还是我弄错了,您从一帧到下一帧重复使用它?如果是后者,那么所有的绘图都会累积。 -
Ken,我添加了一些更新来澄清一些事情。希望这些回答您的问题。
-
我不知道你是如何从视频帧创建
theTexture,但也许this 是相关的? -
我正在做同样的事情——使用带有 AVPlayer 输出的 CVMetalTextureCacheCreateTextureFromImage 来获取 CVMetalTexture,然后使用 CVMetalTextureGetTexture 来获取 MTLTexture。在我介绍这个新的顶点编码器之前似乎工作正常。在渲染循环结束之前,我确实持有指向 CVPixelBuffer 的强指针。
-
引入带有
theTexture作为颜色附件的渲染编码器会强烈引用theTexture,它至少会持续到命令缓冲区被处理超过该编码器在缓冲区中的位置,可能直到缓冲区已完成并且本身已被释放。也许你有太多的在飞,你正在耗尽像素缓冲区/纹理池。