lovr icon indicating copy to clipboard operation
lovr copied to clipboard

Support structured uniform types

Open porglezomp opened this issue 5 years ago • 3 comments

Uniforms can be struct or array types, which becomes extra important when dealing with compute shaders. In particular, SSBOs can only have a single variable-length array member, which means that you are required to use a variable-length array of structs.

Some type features that would be good to be supported for these purposes:

• Struct types • Variable length arrays • Non-square matrix types

I was trying to think about the interface that would work for declaring this, but it seems like it would be a real mess :/

Possible ideas:

You create struct types with a function. They track their names so they can render their declaration as well as their use-sites when rendering as shader code. ShaderBlock construction validates that there aren't more than one struct with the same name but different definitions.

A possible usage example:

struct particle_t {
    vec4 positionAndSize;
    vec4 velocity;
};

layout(std430) buffer particleInfo {
    particle_t mainParticles[4];
    particle_t particles[];
};
particleStruct = lovr.graphics.newStruct("particle_t", {
    { "positionAndSize", "vec4" },
    { "velocity", "vec4" },
})
particleInfo = lovr.graphics.newShaderBlock("compute", {
    { "mainParticles", { particleStruct, 4 } },
    { "particles", { particleStruct, "variable" } },
})

porglezomp avatar Jan 31 '20 09:01 porglezomp

Lately I've been trying to design a generic Buffer interface that can be used for vertex buffers as well as UBOs/SSBOs. It does get really complicated, I'll try to clean up some of my notes and paste here soon to discuss.

Other random thoughts that came up just now:

  • Can structs be nested? It seems like that would make things even more complicated, it might be best to outright forbid it.
  • A ShaderBlock could have an "outer" array size that doesn't influence the layout, this might potentially make VLAs easier? (Not to be confused with arrays of interface blocks).

This is exciting!

bjornbytes avatar Jan 31 '20 09:01 bjornbytes

Structs can indeed be nested, and I think that's probably really useful to handle, but it can at least be worked around. Having an "outer" array size which just translates to a (possibly variable-length) array of a single struct type would be a good enough workaround for my current use-cases, but probably wouldn't be very useful once the general system was implemented.

porglezomp avatar Jan 31 '20 09:01 porglezomp

Can you think of any reason this API wouldn't work or be undesirable?

lovr.graphics.newShaderBlock({
  { 'someParticles', 'particle_t', 4 },
  { 'moreParticles', 'particle_t', 'variable' },
  types = {
    particle_t = {
      { 'positionAndSize', 'vec4' },
      { 'velocity', 'vec4' },
      -- fields could reference other custom types, for nesting
      { 'material', 'material_t' }
    },
    material_t = { ... }
  }
})

Some other thoughts I've been having:

  • The Struct object could be cool, it could even go in the data module and be used as a general facility for all of the numerous situations where we want to write a Lua value to binary memory (blobs, sound data, texture data, vertex data, uniform data). I'm not sure how much use it would get though, and it could even get annoying if you were required to use it. It would at least be really helpful to have a Struct as an internal concept that can be used to read formatted table data into memory (this would simplify the bindings in so many places).
  • There'll need to be a more rich syntax for the keys in ShaderBlock:send, e.g. 'field.subfield[index]', maybe we skip the array syntax and you can only update full arrays.
  • Supporting variable array lengths means we can't know the size of the buffer, and I think it would be complicated to support variable length buffers (unpredictable GPU memory reallocations). Maybe the VLA intent can be communicated through ShaderBlock:getShaderCode, like there is a "use a VLA for last field" option. (Note that Shader:sendBlock would also probably need to accept offset/size stuff so you can bind custom regions of your buffer).

Still thinking through all the use cases

bjornbytes avatar Feb 06 '20 02:02 bjornbytes

This is closed as half wontfix / half done. The new Buffer object is basically an array of compound structures. Doing the full struct API was too complicated to support, but you can get pointers to GPU buffers now to do it with FFI.

bjornbytes avatar Aug 26 '22 18:08 bjornbytes