在片段着色器中读取自定义纹理格式

Reading custom texture format in a fragment shader

我正在创建一个 NES 模拟器,并且正在试验将功能(在适当的情况下)卸载到 GPU 的想法。让我印象深刻的一个想法是以自定义格式将屏幕像素输出到缓冲区的方式,这种方式对每个像素的属性进行编码,以便着色器可以确定如何显示它们。这将从模拟器中最热门的功能之一卸载相当多的 logic/branching。

下面是单个像素的编码方式示例,每个像素使用 2 个字节:

字节 1(调色板索引)bbbb ssss

字节 2(像素属性)bbss rgbp

我对编写着色器比较陌生,所以我的第一个问题是:这甚至可能吗?在我所做的研究中,似乎纹理需要采用特定格式才能在每个像素处从中读取颜色信息,而我自定义的 2 字节 "color" 格式甚至不代表颜色在其本身。如果可能的话,可以采用什么高级方法来实现这一目标?我计划使用 vulkan,但欢迎任何更普遍适用的方法(因为它与着色器相关)。

图形自 2000 年以来发生了变化。着色器(大部分)是计算值的任意程序。这些值有时可能被解释为颜色是无关紧要的。着色器是程序;他们做你想让他们做的事。

同样,纹理不包含颜色,除非您的着色器选择将它们解释为颜色。纹理只不过是值的查找表。同样,这些值可能是颜色,但也可能不是。这完全取决于您的着色器如何使用这些值。

您可以创建使用整数格式(与规范化整数相对)的纹理。然后你可以在你的着色器中做任何你喜欢的位操作。您上面描述的任何事情对于着色器来说都不是特别困难的。

在您的情况下,纹理可能是 2 通道格式,每个通道 8 位,使用无符号整数值。在 OpenGL 中,这种格式拼写为 GL_RG8UI: red/green(两个通道的名称),每个通道 8 位,无符号整数值。 OpenGL 可能将这些通道称为 "red" 和 "green",但重要的是您的着色器如何处理它们,而不是它们的名称。

在 Vulkan 中,这种格式拼写为 VK_FORMAT_R8G8_UINT:两个无符号整数值的 8 位通道。

您将要遇到的主要问题不是图像格式或着色器。它以高效的方式将数据传输到 GPU。

在 OpenGL 中,您别无选择,只能不断地将数据从 CPU 可访问的内存传输到 GPU 可访问的纹理。在 Vulkan 中,您可能不必这样做。

Vulkan 实现可以说线性纹理可以存储在 CPU GPU 都可以访问的内存中。他们不需要提供此信息,但许多人可以这样做。在这样的实现中,不需要 DMA; GPU 可以直接读取 CPU 写的内容。您仍然需要对此类图像进行双缓冲(以最小化 GPU/CPU 同步。当 GPU 正在读取前一帧的数据时,您正在写入一个图像),但这应该会提高 DMA 版本的性能。

当然,如果无法实现,您仍然需要一个代码路径。