Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using vec3 in BindGroupLayout gives error during draw. #7

Open
philogb opened this issue Dec 12, 2021 · 3 comments
Open

Using vec3 in BindGroupLayout gives error during draw. #7

philogb opened this issue Dec 12, 2021 · 3 comments

Comments

@philogb
Copy link

philogb commented Dec 12, 2021

I was not able to spot exactly where the issue happens, but was able to work around it by making all Uniforms within a BindGroupLayout vec4 instead of vec3.

When using vec3 I would get:

Binding sizes are too small for bind group 0

For reference the current bindgrouplayout I'm using:

const circlesBindGroupLayout = new BindGroupLayout([ { buffer: {}, visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, name: "Circles", uniforms: [ new Uniform("D_a", "vec4"), new Uniform("D_A", "vec4"), new Uniform("D_b", "vec4"), new Uniform("D_B", "vec4"), new Uniform("dimensions", "vec4") ], }, ]); return circlesBindGroupLayout;

@dmnsgn
Copy link
Owner

dmnsgn commented Dec 12, 2021

Yeah I bumped into that one too:

mat4.multiply(normalMatrix, camera.viewMatrix, modelMatrix);
mat4.invert(normalMatrix, normalMatrix);
mat4.transpose(normalMatrix, normalMatrix);
// TODO: alignment seems to be wrong, using multiple of 4 bytes instead
// mat3.fromMat4(mat3.create(), normalMatrix)

I haven't had much time to figure out but tried based on glsl spec

dgel/src/core/Variable.ts

Lines 140 to 166 in 228fde8

// https://www.khronos.org/registry/OpenGL/specs/gl/glspec45.core.pdf#page=159
public static getAlignement(
size: number,
qualifier?: GLSLLayoutQualifier
): number {
// Shader storage blocks only
if (!qualifier || qualifier === "std430") {
return size;
}
// Standard Uniform Block Layout and Shader storage blocks
else if (qualifier === "std140") {
switch (size) {
case 0:
return 1;
case 1:
return 1;
case 2:
return 2;
case 3:
return 4;
case 4:
return 4;
default:
console.error(`Unsupported variable size ${size}`);
}
}
}

@CodyJasonBennett
Copy link

Stumbled across this lib/issue in earlier research and thought I'd share my solution. Think of std140 like Tetris, but you're filling equal chunks of memory to 16 bytes:

  • primitives (ints, bools, floats) have a minimum packing size of 4 bytes (1/4 chunk)
  • vec2s have a minimum packing size of 8 bytes (1/2 chunk)
  • anything above aligns to 16 bytes (1 chunk)

Here, I pad before vec2s/more complex uniforms to fill in empty space left by primitives. For an ideal buffer size, pack uniforms by length descending so that you can stack vec2s and primitives towards the end to minimize wasted space.

const uniforms = Object.values({
  time: 0,
  uv: [0, 1],
  position: [1, 2, 3],
  color: [1, 1, 0, 1],
})

// Pad to 16 byte chunks of 2, 4 (std140 layout)
const pad2 = (n) => n + (n % 2)
const pad4 = (n) => n + ((4 - (n % 4)) % 4)

// Calculate packing size
const length = pad4(
  uniforms.reduce(
    (n, u) => n + (typeof u === 'number' ? 1 : u.length <= 2 ? pad2(u.length) : pad4(u.length)),
    0,
  ),
)
const data = new Float32Array(length)

// Pack data
let offset = 0
for (const uniform of uniforms) {
  if (typeof uniform === 'number') {
    data[offset] = uniform
    offset += 1 // leave empty space to stack primitives
  } else {
    const pad = uniform.length <= 2 ? pad2 : pad4
    offset = pad(offset) // fill in empty space
    data.set(uniform, offset)
    offset += pad(uniform.length)
  }
}

// Create buffer (don't need to re-align to 4 bytes)
const buffer = device.createBuffer({
  size: data.byteLength,
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
  mappedAtCreation: true,
})

// Map packed buffer
new Float32Array(buffer.getMappedRange()).set(data)
buffer.unmap()
device.queue.writeBuffer(buffer, 0, data)

// Usage in bind group/layout
const bindGroup = device.createBindGroup({
  layout: pipeline.getBindGroupLayout(0),
  entries: [
    {
      binding: 0,
      resource: {
        buffer,
      },
    },
  ],
})

@dmnsgn
Copy link
Owner

dmnsgn commented Apr 22, 2022

Looks like wgsl is also matching the std140 alignment so fixing glsl alignment should fix wgsl too here: https://www.w3.org/TR/WGSL/#alignment-and-size

Note: Each alignment value is always a power of two, by construction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants