闪烁的金属输出纹理...为什么?

时间:2017-09-26 23:07:13

标签: metal metalkit

我试图在现有纹理的顶部放置一些叠加层(纹理)。在大多数情况下,这很好。

但是,对于我的生活,我无法弄清楚为什么我的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的顶部,这样做。为了澄清,为每个帧(来自CoreVideo像素缓冲区)创建了theTexture。在这个编码器之后,我只从内核着色器中的theTexture读取以调整亮度 - 所有这些都正常工作。

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

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

A sample "good" frame of video[1]

更新#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:

如果采用非传统手段,我就能使用它。

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

然后,我使用内核着色器将此结果纹理与我的theTexture混合。我应该必须这样做,但它有效!

1 个答案:

答案 0 :(得分:1)

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

关键(但本身不足)是减少对金属层的缓冲:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="stylesheet" href="./Grid2.css">
  <title>Awesome Website</title>
</head>

<body>
  <div class="header">
    <div class="inner-header">
      <div class="logo-container">
        <h1>MY<span>SITE</span></h1>
      </div>
      <ul class="navigation">
        <a>
          <li>Home</li>
        </a>
        <a>
          <li>About</li>
        </a>
        <a>
          <li>Portgolio</li>
        </a>
        <a>
          <li>Contact</li>
        </a>
      </ul>
    </div>
  </div>
  <div class="mission">
    <img src="./330px-GABRIEL_VELLA_vs_ROMINHO_51.jpg">
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ducimus nesciunt ratione animi facilis. Quo, et?</p>
  </div>

</body>

</html>

第二,减少缓冲后,我发现我必须经历一个渲染/呈现/提交循环,以在渲染过程描述符上设置metalLayer_.maximumDrawableCount = 2 来绘制一个琐碎的,不可见的项目-非常简单:

.clear

((绘制了一些不可见的三角形可能是不相关的;可能是renderPassDescriptor.colorAttachments[0].loadAction = .clear 属性可以区分通过)。我使用了与上面@zzyzy相同的清晰颜色,我认为这与上述解决方案相呼应。 )

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