如Metal Shading Language Guide中所述:
片段函数不允许写入缓冲区或纹理。
我明白这是事实,但我很好奇为什么。能够从片段着色器中写入缓冲区非常有用;我知道在硬件端可能更复杂的是不提前知道特定线程的内存写入的结束位置,这对于原始缓冲区写入并不总是知道,但这是在Metal计算中公开的一种能力着色器,为什么不在片段着色器中呢?
我应该澄清为什么我认为来自片段函数的缓冲区写入是有用的。在光栅化管道的最常见用例中,三角形被栅格化和着色(根据片段着色器)并写入预定义的存储器位置,在每个片段着色器调用之前已知并由预定义的映射确定规范化设备坐标和帧缓冲区。这适用于大多数使用情况,因为大多数情况下您只想将三角形直接渲染到缓冲区或屏幕上。
在其他情况下,您可能希望在片段着色器中进行延迟写入,其末端位置基于片段属性,而不是片段的确切位置;有效地,光栅化与副作用。例如,大多数基于GPU的体素化通过从一些期望的角度渲染具有正交投影的场景,然后写入3D纹理,将片段的XY坐标及其相关联的深度值映射到3D纹理中的位置来工作。这被描述为here。
其他用途包括某些形式的与订单无关的透明度(透明度,其中绘制顺序不重要,允许重叠透明对象)。一种解决方案是使用多层帧缓冲区,然后在单独的传递中基于它们的深度值对片段进行排序和混合。由于没有硬件支持这样做(在大多数GPU上,我相信英特尔有硬件加速),你必须维护每个像素的原子计数器和手动纹理/缓冲区写入,以协调写入分层帧缓冲区。
另一个例子可能是通过光栅化提取GI的虚拟点光源(即在光栅化时为相关片段写出点光源)。在所有这些用例中,都需要来自片段着色器的缓冲区写入,因为ROP仅为每个像素存储一个结果片段。在没有这个特征的情况下获得相同结果的唯一方法是通过某种深度剥离方式,这对于高深度复杂度的场景来说非常慢。
现在我意识到我给出的示例并不是特别关于缓冲区写入,而是更广泛地关于从片段着色器进行动态内存写入的想法,理想情况下是对原子性的支持。缓冲区写入似乎只是一个简单的问题,它们的包含将对改善这种情况有很大帮助。
由于我在这里没有得到任何答案,我最终posting the question on Apple's developer forums。我在那里得到了更多的反馈,但仍然没有真正的答案。除非我遗漏了某些内容,否则几乎所有官方支持Metal的OS X设备都支持此功能。据我所知,这个功能最初在2009年左右开始出现在GPU中。这是当前DirectX和OpenGL的一个共同特征(甚至不考虑DX12或Vulkan),因此Metal将是唯一缺乏它的“尖端”API
我意识到PowerVR硬件可能不支持此功能,但Apple在功能集方面没有区分金属着色语言的问题。例如,iOS上的Metal允许片段着色器中的“空闲”帧缓冲器提取,这由高速缓存的PowerVR架构直接支持在硬件中。此功能直接在Metal Shading Language中显示,因为它允许您使用iOS着色器的[[color(m)]]
属性限定符声明片段函数输入。可以说,允许声明具有device
存储空间限定符的缓冲区或带有access::write
的纹理作为片段着色器的输入,对语言的语义更改不会比Apple为优化iOS所做的更多。因此,就我而言,PowerVR缺乏支持并不能解释我在OS X上缺少的功能。
答案 0 :(得分:5)
现在支持从片段着色器写入缓冲区,如中所述 What’s New in iOS 10, tvOS 10, and macOS 10.12
中没有指定限制(来自原始问题)的行功能缓冲区读写可用于:iOS_GPUFamily3_v2, OSX_GPUFamily1_v2
片段函数现在可以写入缓冲区。可写缓冲区必须是 在设备地址空间中声明,不得为const。使用 动态索引写入缓冲区。
答案 1 :(得分:1)
我认为你不能在OpenGL或DirectX上的片段函数上写任意像素或纹素。一件事是渲染API和其他东西是片段或顶点函数。
片段函数旨在产生像素/纹素输出,每次运行一个,甚至每个都有多个通道。通常,如果要写入缓冲区或纹理,则需要使用片段函数在表面(缓冲区或纹理)上渲染某些内容(四边形,三角形或其他内容)。结果,每个像素/纹素将使用您的片段函数进行渲染例如,光线投射或光线追踪片段功能通常使用这种方法。
有一个很好的理由不允许你写任意像素/纹素:并行化。片段函数通常在大多数GPU上以非常高的并行化模式一次同时执行许多不同的像素/纹素,每个GPU都有自己的并行化方式(SMP,矢量...),但都进行了非常高的并行化。因此,您只能通过返回一个像素或纹素通道输出作为片段函数的返回来编写,以避免常见的并行化问题,如种族。这适用于我所知道的每个图形库。