带字节的着色器存储缓冲区对象

Posted

技术标签:

【中文标题】带字节的着色器存储缓冲区对象【英文标题】:Shader storage buffer object with bytes 【发布时间】:2015-10-13 09:17:27 【问题描述】:

我正在开发一个计算着色器,其中输出被写入SSBO。现在,这个缓冲区的使用者是 CUDA,它希望它包含无符号字节。我目前看不到如何编写SSBO 中每个索引的字节。使用纹理或图像,标准化浮点到无符号字节的转换由 OpenGL 处理。例如,我可以附加一个内部格式为 R8 的纹理并存储每个条目的字节。但是使用 SSBO 是不可能的。意味着除了 bool 数据类型之外,SSBO 中的所有数字存储类型只能是每个条目至少 4 个字节?

实际上,我希望能够做到以下几点:

计算着色器:

  #version 430 core
  layout (local_size_x = 8,local_size_y = 8 ) in;
  struct SSBOBlock 
  
    byte mydata;
  ;


  layout(std430,binding = BUFFER_OUTPUT) writeonly buffer bBuffer
  

     SSBOBlock Ouput[];

   Out;


   void main()
  
        //..... Compute shader stuff...
        //.......
       Out.Ouput[globalIndex].mydata = val;//where val is normalized float
   

【问题讨论】:

如果您一直认为 GLSL 中的 bool 是 1 字节,您可能需要重新编写一些着色器 :) #version 423 core...wat @AndonM.Coleman 我总是很高兴揭示有关 OpenGL 的新问题 :) 【参考方案1】:

在 GPU 上公开的最小类型往往是 32 位的标量。即使您提到的布尔类型实际上也是 32 位的。像 C 这样的语言也是如此。布尔值不需要超过 1 位,但即便如此,bool 也不是 “给我可用的最小数据类型”的同义词。

您可以使用一些内在函数来打包和解包数据类型,我将在下面展示如何使用它们的示例:

#version 420 core
layout (local_size_x = 8,local_size_y = 8 ) in;
struct SSBOBlock 

  uint mydata;
;


layout(std430,binding = BUFFER_OUTPUT) writeonly buffer bBuffer


  SSBOBlock Ouput[];

 Out;


void main()

  //..... Compute shader stuff...
  //.......
  Out.Output [globalIndex].mydata = packUnorm4x8 (val)
  // where val is a 4-component unsigned normalized vector to pack into globalIndex

您的示例着色器显示了将单个标量写入“字节”数据类型的尝试,这是不可能的,您将不得不以某种方式对其进行修改以使用引用 4 个标量的打包组的索引。在最坏的情况下,这可能意味着解包三个值,然后重新打包整个内容以写入一个标量。

此内在函数在GL_ARB_shading_languge_packing 的扩展规范中进行了讨论,是 GL 4.2 及更高版本的核心。


即使您使用的实现不支持该扩展,扩展规范的文本中也会准确解释每个扩展的功能。 packUnorm4x8 的等效操作是:

uint fixed_val = round(clamp(float_val, 0, +1) * 255.0);

为了正确打包每个组件,需要进行一些位移,但这些都是微不足道的。

【讨论】:

不错的答案,但是打包可以打包到 4 个字节。而且我每个条目只需要一个字节。事实上我找到了解决方案 - TBO.Partially。它只解决了我写线性缓冲区的问题字节。但它在 CUDA 方面搞砸了,因为 NVENC 似乎不支持 TBO 的资源映射。 那条评论我错了 - NVENC 确实支持 TBO,而且步伐适当。【参考方案2】:

我找到了一种将无符号字节数据写入计算着色器中的缓冲区的方法。Buffer texture 完成了这项工作。它基本上是带有缓冲区作为存储的图像纹理。这样我可以将图像格式指定为允许我存储的 R8缓冲区每个索引的字节大小值。

GLuint _tbo_buffer,_tbo_tex;
glGenBuffers(1, &_tbo_buffer);
glBindBuffer(GL_TEXTURE_BUFFER, _tbo_buffer);
glBufferData(GL_TEXTURE_BUFFER, SCREEN_WIDTH * SCREEN_HEIGHT, NULL, GL_DYNAMIC_COPY);
glGenTextures(1, &_tbo_tex);
glBindTexture(GL_TEXTURE_BUFFER, _tbo_tex);
//attach the TBO to the texture:
glTexBuffer(GL_TEXTURE_BUFFER, GL_R8, _tbo_buffer);
glBindImageTexture(0, _tbo_tex, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R8);

计算着色器:

#version 430 core

layout (local_size_x = 8,local_size_y = 8 ) in;
layout(binding=0) uniform sampler2D   TEX_IN;
layout(r8) writeonly uniform imageBuffer mybuffer;

void main()
   vec2 texSize =  vec2(textureSize(TEX_IN,0));
   vec2 uv      =  vec2(gl_GlobalInvocationID.xy / texSize);
   vec4 tex     =  texture(TEX_IN,uv); 
   uint globalIndex =  gl_GlobalInvocationID.y * nThreads.x +    gl_GlobalInvocationID.x;
  //store only r:
   imageStore(mybuffer,int(globalIndex),vec4(0.5,0,0,0));


然后我们可以在 CPU 上逐字节读取或映射到 CUDA 缓冲区资源:

 GLubyte* ptr = (GLubyte*)glMapBuffer(GL_TEXTURE_BUFFER, GL_READ_ONLY);

【讨论】:

以上是关于带字节的着色器存储缓冲区对象的主要内容,如果未能解决你的问题,请参考以下文章

从顶点着色器中修改着色器存储缓冲区对象

着色器存储缓冲区对象的长度为零,glMapBufferRange 不起作用

OpenGL:着色器存储缓冲区映射/绑定

我应该在统一缓冲区或着色器存储缓冲区对象中使用“vec3”吗?

着色器存储缓冲区中的 OpenGL 顶点

OpenGL着色器存储缓冲区/memoryBarrierBuffer