改变 uv 坐标对金属的意外性能影响

Posted

技术标签:

【中文标题】改变 uv 坐标对金属的意外性能影响【英文标题】:Unexpected performance hit in metal by changing uv coordinates 【发布时间】:2017-08-08 11:39:45 【问题描述】:

下面是metal 中的简单顶点和片段着色器组合,可渲染 64 个相同的 2D 四边形。

vertex VertexOut vertexMain(uint k [[ vertex_id ]],
                            uint ii [[instance_id]],
                            device float2* tex [[buffer(2)]],
                            device float2* position [[buffer(1)]],
                            device float* state [[buffer(0)]])
    VertexOut output;
    int i = 4*ii+1;
    float2 pos = position[k];
    pos *= float2(state[i+2],state[i+3]);
    pos += float2(state[i],state[i+1]);
    pos.x *= state[0];
    output.position = float4(pos,0,1);
    output.tex = tex[k]*float2(du,dv);
    return output;
;
fragment float4 fragmentMain(VertexOut input [[stage_in]],
                             texture2d<float> texture [[texture(0)]],
                             sampler sam [[sampler(0)]] )
    return texture.sample(sam, input.tex);
;

采样器使用标准化坐标,因此dudv 的范围可以从 0 到 1,并控制从左下角开始对纹理剪辑的采样大小。

我似乎对金属采样的工作原理存在误解。无论dudv 保持什么值,我都希望计算成本保持不变。但是,当我将 dudv 增加到 1 时,帧速率会下降。我没有使用任何 mipmapping,也没有更改屏幕上光栅化的四边形的大小。线性过滤的影响更为显着,但最近的过滤也会发生。在我看来,由于绘制到屏幕上的像素数是相同的,GPU 上的负载不应该取决于dudv。我错过了什么?

编辑:这是我的采样器和颜色附件:

    let samplerDescriptor = MTLSamplerDescriptor()
    samplerDescriptor.normalizedCoordinates = true
    samplerDescriptor.minFilter = .linear
    samplerDescriptor.magFilter = .linear
    let sampler = device.makeSamplerState(descriptor: samplerDescriptor)

    let attachment = pipelineStateDescriptor.colorAttachments[0]
            attachment?.isBlendingEnabled = true
            attachment?.sourceRGBBlendFactor = .one
            attachment?.destinationRGBBlendFactor = .oneMinusSourceAlpha

【问题讨论】:

你能量化一下你经历的帧率下降多少吗? 线性采样从 60 到 40。从 60 到 50,采用最近抽样。 在哪个设备和操作系统版本上? iPad mini 和 iPad Pro 9.7 都运行 10.2 【参考方案1】:

随着dudv 的增加,您的四边形会显示更多的纹理。 GPU 往往具有用于纹理数据的小型缓存,并且当您显示更多纹理时,您将更多地丢弃和重新填充该缓存。

抖动纹理缓存将使用更多的内存带宽,这是一个相当有限的资源,通常纹理内存带宽不是瓶颈,但是由于您的片段着色器除了纹理提取之外几乎什么都不做,所以它并不奇怪你的瓶颈。因此,更改 UV 会影响性能也就不足为奇了。

令人惊讶的是,在这些功能强大的设备上,帧速率降至 60 以下,而您所做的只是渲染 64 个四边形(iPad Pro 尤其是一款功能非常强大的设备)。也就是说,如果所有 64 个四边形都覆盖了大部分屏幕,那么帧率下降可能是可以理解的。

为了提高性能,您需要减少需要由 GPU 处理的纹理数据量。从 32 位纹理格式 (8888) 更改为 16 位 (565/4444) 或 4 位(PVRTC 压缩纹理)会产生很大的影响。

真正的大胜利可能是启用 mipmapping。假设使用较高的 dudv 值,您最终会最小化纹理,然后使用 mipmapping 将带来巨大的性能优势,并作为额外的奖励你的纹理也会看起来更好(它会修复锯齿)。纹理内存增加 33% 的回报不错。

【讨论】:

这很有趣。我正在使用实例渲染,这意味着所有 64 个四边形都通过一个纹理绑定和一个绘制调用绘制在一起。在这种情况下还会出现这种缓存问题吗? 是的,纹理缓存通常只有几 KB,大多数纹理只有几百 KB。这是一本很好的入门书:fgiesen.wordpress.com/2011/07/04/… 更好地理解您的渲染场景可能会很有用——也许是屏幕截图。 64 个小四边形不算什么,如果你不能达到 60fps,就会发生一些有趣的事情。 64 个四边形,每个四边形都覆盖了整个屏幕,然后我就可以理解这个问题了。 每个四边形仅占屏幕的 1%。我添加了更多代码来描述我的管道。 听起来纹理可能与屏幕覆盖率相比非常大,因此 mipmapping 将是一个巨大的胜利。但我真的很惊讶,即使在病态纹理采样场景中,帧速率也会在如此简单的场景中受到影响。纹理格式、宽度和高度是什么?以及四边形的近似像素大小? 原始图像是 3900 x 1864。光栅化的四边形大约是 200 x 150。我肯定会开始使用 mipmapping,但显然我想了解为什么会发生这种性能损失。跨度>

以上是关于改变 uv 坐标对金属的意外性能影响的主要内容,如果未能解决你的问题,请参考以下文章

Cadence画版图时改变全部金属层

Unity Shaders学习笔记之通过修改UV坐标实现纹理贴图的滚动

锻造与铸造的区别

在matlab中如何改变示波器显示的横轴的坐标

『ORACLE』SPA性能分析器

准备:新V8即将到来,Node.js的性能正在改变