【问题标题】:Data corruption when replacing a GLSL constant with a uniform value用统一值替换 GLSL 常量时数据损坏
【发布时间】:2019-09-07 00:41:30
【问题描述】:

关注this recent question

我正在 WebGL2 中进行 GPGPU 编程,并且通过将其打包到纹理中以绕过统一计数限制,将一个大型 4 维方形数组传递给我的着色器。从不得不使用相对较小的固定大小数组中解放出来,我希望能够指定实际以编程方式传入的数据的大小。

以前,我使用const int 硬编码要读取的数据大小,如下所示:

const int SIZE = 5;
const int SIZE2 = SIZE*SIZE;
const int SIZE3 = SIZE2*SIZE;

uniform sampler2D u_map;

int get_cell(vec4 m){
    ivec4 i = ivec4(mod(m,float(SIZE)));
    float r = texelFetch(u_map, ivec2(i.x*SIZE3+i.y*SIZE2+i.z*SIZE+i.w, 0), 0).r;
    return int(r * 255.0);
}

如果我将 SIZE2SIZE3 更新为非常量并在 main 中初始化,它仍然有效:

const int SIZE = 5;
int SIZE2;
int SIZE3;

uniform sampler2D u_map;

int get_cell(vec4 m){
    ivec4 i = ivec4(mod(m,float(SIZE)));
    float r = texelFetch(u_map, ivec2(i.x*SIZE3+i.y*SIZE2+i.z*SIZE+i.w, 0), 0).r;
    return int(r * 255.0);
}

...

void main(){
  SIZE2 = SIZE*SIZE;
  SIZE3 = SIZE*SIZE2;

  ...
}

但是,如果我随后将const int SIZE = 5; 替换为uniform int SIZE;,然后添加

const size_loc = gl.getUniformLocation(program, "SIZE");
gl.uniform1i(size_loc, 5);

在 JavaScript 端将其设置为与过去硬编码相同的整数值,我开始看到从纹理中读取的值不正确。我做错了什么?

更新 1:我做了一个小实验,我保持不变的 SIZE 规范,但随后也传递了一个统一的 int 旁边。如果它们不相等,我会让着色器退出并返回全零。这样,我可以验证正确的整数值实际上是在统一变量上设置的——但是如果我随后将SIZE 设为非常量,并将其设置为统一变量的值 只是比较并发现相等然后事情就破裂了。什么鬼?

更新 2:

这行得通:

int SIZE = 5;
uniform int u_size;
....
void main() {
  if (u_size != SIZE) return;
  SIZE = u_size;
  ...
}

这不是:

int SIZE = 5;
uniform int u_size;
....
void main() {
  SIZE = u_size;
  ...
}

【问题讨论】:

  • 你确实在gl.uniform1i(size_loc, 5);之前的某个地方打电话给gl.useProgram(program);,不是吗?
  • @Rabbid76 是的。有几个预先存在的统一变量有效,并且一直有效。
  • 我突然想到,除了纹理位置之外,我所有的其他统一值都是浮点数。因此,我尝试传入一个统一的浮点数,然后将其转换为 int 以初始化 SIZE——但最终得到完全相同(不正确)的结果。
  • 这只是一个猜测,但如果你这样做,问题是否仍然存在ivec2(i.x*SIZE*SIZE*SIZE+i.y*SIZE*SIZE+i.z*SIZE+i.w, 0)
  • @Rabbid76 是的。 :(

标签: gpgpu webgl2


【解决方案1】:

我无法重现您的问题。在snippet 中发布minimal, complete, verifiable, example

这是一个工作示例

const vs = `#version 300 es
void main() {
  gl_PointSize = 1.0;
  gl_Position = vec4(0, 0, 0, 1);
}
`;
const fs = `#version 300 es
precision highp float;
uniform ivec4 cell;
uniform int SIZE;
int SIZE2;
int SIZE3;

uniform highp isampler2D u_map;

int get_cell(ivec4 m){
    ivec4 i = m % SIZE;
    int r = texelFetch(u_map, ivec2(i.x*SIZE3 + i.y*SIZE2 + i.z*SIZE + i.w, 0), 0).r;
    return r;
}

out int result;

void main(){
  SIZE2 = SIZE*SIZE;
  SIZE3 = SIZE*SIZE2;
  result = get_cell(cell);
}
`;


const gl = document.createElement('canvas').getContext('webgl2');
// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

// make a 1x1 R32I texture and attach to framebuffer
const framebufferInfo = twgl.createFramebufferInfo(gl, [
  { internalFormat: gl.R32I, minMag: gl.NEAREST, },
], 1, 1);

const size = 5;
const totalSize = size * size * size * size;
const data = new Int32Array(totalSize);
for (let i = 0; i < data.length; ++i) {
  data[i] = 5 + i * 3;
}
// create a size*size*size*size by 1
// R32I texture
const tex = twgl.createTexture(gl, {
  width: totalSize,
  src: data,
  minMag: gl.NEAREST,
  internalFormat: gl.R32I,
});

gl.bindFramebuffer(gl.FRAMEBUFFER, framebufferInfo.framebuffer);
gl.viewport(0, 0, 1, 1);
gl.useProgram(programInfo.program);

const result = new Int32Array(1);

for (let w = 0; w < size; ++w) {
  for (let z = 0; z < size; ++z) {
    for (let y = 0; y < size; ++y) {
      for (let x = 0; x < size; ++x) {
        // calls gl.activeTexture, gl.bindTexture, gl.uniformXXX
        twgl.setUniforms(programInfo, {
          cell: [x, y, z, w],
          u_map: tex,
          SIZE: size,
        });
        gl.drawArrays(gl.POINTS, 0, 1);  // draw 1 point
        gl.readPixels(0, 0, 1, 1, gl.RED_INTEGER, gl.INT, result);
        log(x, y, z, w, ':', result[0], data[x * size * size * size + y * size * size + z * size + w]);
      }
    }
  }
}

function log(...args) {
  const elem = document.createElement('pre');
  elem.textContent = [...args].join(' ');
  document.body.appendChild(elem);
}
pre { margin: 0; }
&lt;script src="https://twgljs.org/dist/4.x/twgl-full.min.js"&gt;&lt;/script&gt;

尝试使用您发布的代码我也没有发现任何问题

const vs = `#version 300 es
void main() {
  gl_PointSize = 1.0;
  gl_Position = vec4(0, 0, 0, 1);
}
`;
const fs = `#version 300 es
precision highp float;
uniform vec4 cell;
uniform int SIZE;
int SIZE2;
int SIZE3;

uniform sampler2D u_map;

int get_cell(vec4 m){
    ivec4 i = ivec4(mod(m,float(SIZE)));
    float r = texelFetch(u_map, ivec2(i.x*SIZE3+i.y*SIZE2+i.z*SIZE+i.w, 0), 0).r;
    return int(r * 255.0);
}

out float result;

void main(){
  SIZE2 = SIZE*SIZE;
  SIZE3 = SIZE*SIZE2;
  // output to texture is normalized float
  result = float(get_cell(cell)) / 255.0;
}
`;


const gl = document.createElement('canvas').getContext('webgl2');
// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

const size = 5;
const totalSize = size * size * size * size;
const data = new Uint8Array(totalSize);
for (let i = 0; i < data.length; ++i) {
  data[i] = (5 + i * 3) % 256;
}
// create a size*size*size*size by 1
// R8 texture
const tex = twgl.createTexture(gl, {
  width: totalSize,
  src: data,
  minMag: gl.NEAREST,
  internalFormat: gl.R8,
});

gl.viewport(0, 0, 1, 1);
gl.useProgram(programInfo.program);

const result = new Uint8Array(4);

for (let w = 0; w < size; ++w) {
  for (let z = 0; z < size; ++z) {
    for (let y = 0; y < size; ++y) {
      for (let x = 0; x < size; ++x) {
        // calls gl.activeTexture, gl.bindTexture, gl.uniformXXX
        twgl.setUniforms(programInfo, {
          cell: [x, y, z, w],
          u_map: tex,
          SIZE: size,
        });
        gl.drawArrays(gl.POINTS, 0, 1);  // draw 1 point
        gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, result);
        log(x, y, z, w, ':', result[0], data[x * size * size * size + y * size * size + z * size + w]);
      }
    }
  }
}

function log(...args) {
  const elem = document.createElement('pre');
  elem.textContent = [...args].join(' ');
  document.body.appendChild(elem);
}
pre { margin: 0; }
&lt;script src="https://twgljs.org/dist/4.x/twgl-full.min.js"&gt;&lt;/script&gt;

请注意,我不会使用一维纹理,因为尺寸有限制。我会使用 3 维纹理来增加限制

const vs = `#version 300 es
void main() {
  gl_PointSize = 1.0;
  gl_Position = vec4(0, 0, 0, 1);
}
`;
const fs = `#version 300 es
precision highp float;
uniform ivec4 cell;
uniform int SIZE;

uniform highp isampler3D u_map;

int get_cell(ivec4 m){
    // no idea why you made x major
    ivec4 i = m % SIZE;
    int r = texelFetch(
      u_map,
      ivec3(
          i.z * SIZE + i.w,
          i.yx),
      0).r;
    return r;
}

out int result;

void main(){
  result = get_cell(cell);
}
`;


const gl = document.createElement('canvas').getContext('webgl2');
// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

// make a 1x1 R32I texture and attach to framebuffer
const framebufferInfo = twgl.createFramebufferInfo(gl, [
  { internalFormat: gl.R32I, minMag: gl.NEAREST, },
], 1, 1);

const size = 5;
const totalSize = size * size * size * size;
const data = new Int32Array(totalSize);
for (let i = 0; i < data.length; ++i) {
  data[i] = 5 + i * 3;
}
// create a size*size*size*size by 1
// R32I texture 3D
const tex = twgl.createTexture(gl, {
  target: gl.TEXTURE_3D,
  width: size * size,
  height: size,
  src: data,
  minMag: gl.NEAREST,
  internalFormat: gl.R32I,
});

gl.bindFramebuffer(gl.FRAMEBUFFER, framebufferInfo.framebuffer);
gl.viewport(0, 0, 1, 1);
gl.useProgram(programInfo.program);

const result = new Int32Array(1);

for (let w = 0; w < size; ++w) {
  for (let z = 0; z < size; ++z) {
    for (let y = 0; y < size; ++y) {
      for (let x = 0; x < size; ++x) {
        // calls gl.activeTexture, gl.bindTexture, gl.uniformXXX
        twgl.setUniforms(programInfo, {
          cell: [x, y, z, w],
          u_map: tex,
          SIZE: size,
        });
        gl.drawArrays(gl.POINTS, 0, 1);  // draw 1 point
        gl.readPixels(0, 0, 1, 1, gl.RED_INTEGER, gl.INT, result);
        log(x, y, z, w, ':', result[0], data[x * size * size * size + y * size * size + z * size + w]);
      }
    }
  }
}

function log(...args) {
  const elem = document.createElement('pre');
  elem.textContent = [...args].join(' ');
  document.body.appendChild(elem);
}
pre { margin: 0; }
&lt;script src="https://twgljs.org/dist/4.x/twgl-full.min.js"&gt;&lt;/script&gt;

【讨论】:

  • 正如我对这个问题的评论,它似乎是一个特定于平台的错误。它在 MacOS 上突破了 Chromium,但到目前为止还没有在其他地方突破。我会提交一个错误报告,但完整的应用程序很大,而且我不值得将它缩减为一个最小的示例,因为我可以让它在其他地方工作。不过,关于纹理尺寸限制,这是一个很好的观点;如果我在当前方法中遇到问题,我将不得不对此进行调查。谢谢!
  • 我在 MacOS 上。没有看到错误。你可能发现了一个错误。您的代码更有可能出现问题。存在特定于平台的问题精度问题,因此如果您没有正确计算 UV,那么您会遇到问题。例如,从整数浮点数转换为实际整数可能会导致意想不到的结果。这条线ivec4(mod(m,float(SIZE))); 可能是个问题。见:stackoverflow.com/questions/47388074/… 还有其他东西。如果你需要帮助,你需要发布一个 repo。
  • 为什么将相同的整数SIZE 指定为常量或统一值会影响该行?
  • 一个是在编译时以 CPU 精度计算的。另一个在运行时以 GPU 精度执行。关键是没有看到您的代码,我们无法检查您是否遇到了众所周知的边缘情况。
  • 嗯。我会认为“精度”对于 32 位整数应该无关紧要......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-10
  • 1970-01-01
  • 2014-01-05
  • 1970-01-01
  • 2021-03-02
  • 2021-02-01
  • 2019-02-14
相关资源
最近更新 更多