Повреждение данных при замене константы GLSL равным значением - PullRequest
0 голосов
/ 16 апреля 2019

Следите за этим недавним вопросом .

Я занимаюсь программированием GPGPU в WebGL2, и я передаю большой 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);
}

Если я обновлю SIZE2 и SIZE3, чтобы они были непостоянными и инициализировались в 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;
  ...
}

1 Ответ

0 голосов
/ 17 апреля 2019

Я не могу воспроизвести вашу проблему. Разместите минимальный, полный, проверяемый пример в фрагменте

Вот рабочий пример

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; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>

пробуя с кодом, который вы разместили, я тоже не вижу проблем

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; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>

Обратите внимание, что я не буду использовать одномерную текстуру, поскольку существует ограничение на размеры. Я бы использовал 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; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
...