Retrospective changes: storing attributes and uniforms on the program

For lesson 13, I needed to be able to easily switch the WebGL program object (which manages the shaders) so that you can see the effects of per-vertex or per-pixel lighting just by toggling a checkbox. Obviously, changing which program you’re using means that the code that draws the scene needs to use the current program’s attribute and uniform locations. One easy way to do this was to keep the attribute and the uniform locations as fields of the program object; this not only made it easier to switch everything across in one go by just changing a currentProgram object, but it also gave me a chance to change things so that we get the uniform locations in one go while initialising, instead of getting them every repaint; as Easy WebGL pointed out the other day, getting uniforms can be costly in terms of processor power, so it’s worth avoiding doing it unnecessarily.

Anyhow, this was such a nice change that I decided to push it back into the older lessons. Here’s an example of what the new code looks like — first, the new initShaders from lesson 1:

  var shaderProgram;
  function initShaders() {
    var fragmentShader = getShader(gl, "shader-fs");
    var vertexShader = getShader(gl, "shader-vs");

    shaderProgram = gl.createProgram();
    gl.attachShader(shaderProgram, vertexShader);
    gl.attachShader(shaderProgram, fragmentShader);
    gl.linkProgram(shaderProgram);

    if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
      alert("Could not initialise shaders");
    }

    gl.useProgram(shaderProgram);

    shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
    gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

    shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
    shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
  }

So, you can see that it stores the attributes and uniforms as fields in the program object. This means that setMatrixUniforms can be simplified, as it no longer needs to look up uniforms:

  function setMatrixUniforms() {
    gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, new Float32Array(pMatrix.flatten()));
    gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, new Float32Array(mvMatrix.flatten()));
  }

…and also, the code in drawScene needs a small adjustment to pick up the attribute locations from the program object rather than the no-longer-existent global variables:

    gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer);
    gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
    setMatrixUniforms();
    gl.drawArrays(gl.TRIANGLES, 0, triangleVertexPositionBuffer.numItems);

Right, hopefully I can get round to writing up lesson 13 properly now, it’s been too long coming!

You can leave a response, or trackback from your own site.

10 Responses to “Retrospective changes: storing attributes and uniforms on the program”

  1. Hi Giles,

    that is more or less how the attributes and uniforms management was implemented in my library (SpiderGL).
    In fact, when a program is created, a series of “reflection” is performed on it, querying and storing locations for vertex attributes and uniforms.
    Given the fact that in my framework mesh attributes are named, then a linking (actually binding points) with the vertex buffers is performed via a JavaScript object of the form { programAttrName : “meshAttrName “, … }.
    The same concepts applies to uniforms and samplers (same thing from a WebGL point of view, but I kept them separate). You can have a look at the draw function in Example 1 for a concrete usage.

    Keep going with this site! Actually, is am redirecting here everyone interested in WebGL ;)

    Cheers,
    Marco.

  2. WebGLU does something similar as well, it parses all attributes and uniforms from the shader source, then queries them once for each shader program which uses those shaders at link time. It even sets up the buffers for you too.

  3. Paul Brunt says:

    GLGE does the same now. Another optimisation(not quite the same impact) I’m just implementing in GLGE is not creating a new webglarray on every frame unless needed. ie:
    gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, new WebGLFloatArray(pMatrix.flatten()));
    is several times slower(under chromium linux at least) then then say:
    if(!pMatrix.glarray) pMatrix.glarray=new WebGLFloatArray(pMatrix.flatten())
    gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix.glarray);

  4. giles says:

    Excellent, it sounds like we’re all converging on a WebGL best practice — I’ll make sure I keep up with SpiderGL, WebGLU and GLGE for hints and tips.

    @Marco — the reflection sounds interesting, are you reflecting by parsing the SL like Benjamin or have you found a different way? Thank you very much for sending people here, I’m really glad that I’m explaining stuff usefully (especially as I’m learning it myself as I go along :-)

    @Benjamin — very brave of you to be parsing the SL (if I understand what you’re doing correctly), I guess you’ve found a reasonably high-performance way of doing it? BTW, is there a good RSS feed or other way I can keep track on WebGLU changes? I see you’re checking stuff in, but I’ve not seen any good way of tracking things. [EDIT: just spotted your blog post, I guess that answers my question :-) ]

    @Paul — that sounds like an excellent optimisation, particularly for the perspective matrix; presumably you clear the glarray attribute when it changes in your perspective-setting function so you only create the float array once per redraw on average? Hope you don’t mind if I sneak something like that into my lessons sometime :-)

  5. @Giles – Actually I do not parse the shader sources, but rather ask GL to tell me what are the active attributes and uniforms.
    That is, after linking the program, you can ask for them with
    gl.getActiveUniform(programHandle, uniformIndex) or
    gl.getActiveAttrib(programHandle, attribIndex), where
    uniformIndex ranges from 0 to gl.getProgramParameter(programHandle, gl.ACTIVE_UNIFORMS) – 1,
    and attribIndex from 0 to gl.getProgramParameter(programHandle, gl.ACTIVE_ATTRIBUTES) -1.
    Both gl.getActive* gives you a WebGLActiveInfo object which contains the name, native type and size of the attribute/uniform.

    Cheers!

  6. giles says:

    Thanks, Marco!

  7. titan says:

    And next Speed-Up with the WebGL*Array.Set command or?

    Greets

  8. gero3 says:

    titan
    not likely becuase minefield still doesn’t support it.

    Btw the next speedup suggested in ewgl will be matrix calculations speedup

  9. giles says:

    Improving the speed of matrix multiplications definitely sounds like a good idea. I need to add an improvement that Coolcat suggested a while back, reducing the size of the normal matrix to 3×3 — more here: http://learningwebgl.com/blog/?p=684&cpage=1#comment-225

    WebGL*Array.Set sounds like it could help speed up some WebGL apps, but I don’t think there’s any way I could use it for these tutorials — or am I missing something?

  10. gero3 says:

    no it won’t work just yet.
    I am still searching something to increase that without troubles because that is still something that decreases the performance significant

Leave a Reply

Subscribe to RSS Feed Follow Learning WebGL on Twitter