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

What to do with uniform blocks #37

Open
greggman opened this issue Jan 17, 2017 · 0 comments
Open

What to do with uniform blocks #37

greggman opened this issue Jan 17, 2017 · 0 comments

Comments

@greggman
Copy link
Owner

I added uniform block support but some of the benefits are missing.

As it is it works similar to normal twgl uniforms. You make a JavaScript object with all the values for your uniforms and then instead of calling setUniforms you call setBlockUniforms which copies the values from the JavaScript object into the typedarray that's holding the values for the uniforms. You then call setUniformBlock which uploads the typedarray to the corresponding buffer.

var uniforms = {
  u_lightWorldPos: [1, 8, -10],
  u_lightColor: [1, 0.8, 0.8, 1],
  u_ambient: [0, 0, 0, 1],
  u_specular: [1, 1, 1, 1],
  u_shininess: 50,
  u_specularFactor: 1,
  u_diffuse: tex,
};
// copies the uniform values into the typedarray for the uniforms
twgl.setBlockUniforms(someUniformBlockInfo, uniforms);
...
// uploads the typedarray to WebGL and binds it to the correspondingshader's uniforn block
twgl.setUniformBlock(gl, programInfo, someUniformBlockInfo);

The reason there's this 2 step process is because the code becomes brittle without it. If you hardcode uniform names your code will break when debugging and commenting out uniforms etc. In other words you can set the typedarray directly like this

someUniformBlockInfo.uniforms.u_shininess[0] = 50;
someUniformBlockInfo.uniforms.u_lightColor.set([1,0,1,1]);  

This will write directly into the typedarray which should be faster than putting your values in JavaScript objects and then calling twgl.setBlockUniforms. But .... If you then go comment out u_lightcolor from your shader your code breaks. Using twgl.setBlockUniforms solves this issue.

Taking it a step further though, the whole point of using Uniform Blocks are (1) they're faster and (2) they can be shared, as in you can use them across programs. Uniform Blocks in WebGL only support the std140 format which means individual uniforms will not get optimized out. If you declare a block

 uniform Lights {
  mediump vec3 u_lightWorldPos;
  mediump vec4 u_lightColor;
} light;

And don't use u_lightColor it will still exist in the block. So in this case you're safe.

On the other hand if you were to NOT use both u_lightColor and u_lightWorldPos then the entire block will not exist and your code trying to set that block will fail. TWGL solves this by making dummy blocks (and printing a warning). It's a trade off. If you aren't checking the console for messages you might mis issues with typos but if you're debugging and commenting out lines in a shader you won't have to restructure your JavaScript

Another solution though would be to be able to give TWGL some kind of block definition separate from the shader. It could create the typedarray with all the accessors which, having no direct connection to the shader, would mean it just always exists, no errors.

But now you have a new problem, you've got to make these uniform definitions separate from the shader itself and keep them in sync with the actual shaders. Now it's turning into a bigger problem. In the interest of D.R.Y. you could have TWGL parse the shader. That's not going to happen as it would take way too much code. You could have TWGL generate the uniform block definition for the shader code. This would be a win because it would mean your blocks would match across shaders and it means D.R.Y. As it is if you have a block in one shader and you're supposed to have a matching block in another but you forget to edit both when adding a field you're screwed. Defining them in one place and including them in your shader would be a win. Something like

const uniformBlockGLSLSnippet = twgl.generateUniformBlockGLSLFromUniformBlockInfo(...)

Then you could make your shader something like

const shaderSource = `#version 300 es
    ${uniformBlockGLSLSnippet}

    ...
    void main() {
        ....

But, ... it would also seem to be imposing too much structure. Meaning you'd have to do things the TWGL way. I suppose that's not really true. You wouldn't have to use this stuff. It would just be there if you want to. But no one wants someone else's structure. The structure defining structure would also end up being something that has to be learned

 const uniformBlockDefintion = [
    {name: "u_lightWorldPos", type: "vec3", precision: "mediump", },
    {name: "u_lightColor", type: "vec4", precision: "mediump", },
 ]);

And adding on structs, arrays, structs of structs, arrays of structs, structs of arrays, arrays of arrays etc. No fun.

I don't really know if there's a solution at the moment or if I should even try to make one. Maybe if you really want to get that optimized you wouldn't be using TWGL in the first place? Or maybe solutions at that level are out of scope. You can still use TWGL even if you decide to implement solutions like these.

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

1 participant