我知道这是事实,但我很好奇为什么会这样。从片段着色器中写入缓冲区非常有用;我知道直接写入缓冲区时硬件端可能更复杂,因为不总是能预知特定线程的内存写入的最终位置,而原始缓冲区写入就不总是知道,但这是Metal计算着色器中公开的功能,那为什么不在片段着色器中也使用呢?在片段函数中,禁止向缓冲区或纹理写入。
附加说明
我应该澄清一下为什么我认为从片段函数中写入缓冲区很有用。在典型的栅格化管线使用情况下,三角形正在被栅格化和着色(根据片段着色器),并写入到预定义的内存位置中,在每个片段着色器调用之前都已知,并由规范化设备坐标与帧缓冲区的预定义映射确定。这适用于大多数使用情况,因为大多数情况下您只想直接将三角形渲染到缓冲区或屏幕。还有其他情况,您可能希望在片段着色器内进行延迟写入,其最终位置基于片段属性而不是片段的确切位置;实际上是具有副作用的栅格化。例如,大多数基于GPU的体素化都是通过从一些理想角度使用正交投影渲染场景,然后写入3D纹理来完成的,将片段的XY坐标和其相关深度值映射到3D纹理中的位置。这在此处有所描述。
其他用途包括一些形式的无序透明度 (透明度顺序无关紧要,允许重叠透明对象)。一种解决方法是使用多层帧缓冲区,然后根据它们的深度值在单独的处理中进行排序和混合片段。由于大多数 GPU 上没有硬件支持这样做 (我相信 Intel 的 GPU 有硬件加速),因此您必须维护原子计数器并从每个像素手动纹理/缓冲区写入以协调对分层帧缓冲区的写入。
另一个例子可能是通过光柱化提取 GI 的虚拟点光源(即,当您进行光柱化时,会为相关片段编写出点光源)。在所有这些用法情况下,都需要从片段着色器进行缓冲区写入,因为 ROPs 只存储每个像素的一个结果片段。在没有此功能的情况下获得等效结果的唯一方法是进行某种深度剥离,但对于深度复杂度高的场景来说非常慢。
现在我意识到我所举的例子并不是特别关于缓冲区写入的,而更多地是关于片段着色器动态内存写入的概念,最好还要支持原子性。缓冲区写入似乎只是一个简单的问题,它们的包含将在很大程度上改善情况。
由于我在这里没有得到任何答案,最终我在 苹果开发者论坛上发布了这个问题。我在那里得到了更多的反馈,但仍然没有真正的答案。除非我漏掉了什么,否则几乎每台正式支持 Metal 的 OS X 设备都具有此功能的硬件支持。据我所知,这个功能首次出现在 2009 年左右的 GPU 上。它是当前 DirectX 和 OpenGL (甚至不考虑 DX12 或 Vulkan) 中的常见功能,因此 Metal 将是唯一缺少该功能的“尖端” API。
我知道这个特性可能在PowerVR硬件上不被支持,但是苹果已经成功地通过功能集区分了Metal Shading Language。例如,在iOS上使用的Metal允许在片元着色器中进行“自由”帧缓冲提取,这直接受到高速缓存密集型PowerVR架构的硬件支持。这个特性会直接体现在Metal Shading语言中,因为它允许你使用[[color(m)]]
属性限定符来声明iOS着色器中的片元函数输入。可以说,允许将device
存储空间限定符用于缓冲区声明,或将access::write
用于纹理声明,作为片元着色器的输入,与苹果为优化iOS所做的工作相比,并没有更大的语义变化。因此,在我看来,PowerVR的不支持并不能解释我在OS X上寻找的特性缺失。