Open mattdesl opened 10 years ago
I think we could also just have gl-shader cache the different program objects. That way, you would only pay a 1-time cost associated with recompiling the shader under a different permutation of attributes. This could also reuse the shader objects, so the overhead would be relatively lower than compiling a new program object from scratch.
I think there are a few issues with this:
Most game engines order their scene by shaders first. Allowing many meshes to take advantage of the same shader (and by that I mean same GL shader object, not gl-shader object) is the first step to efficient rendering.
On the topic of shader first rendering, I wholeheartedly agree and think that the current system enables applications to take advantage of this quite nicely, assuming that they are designed with it in mind.
require('gl-sphere')
. Example scenario: if gl-sphere
and gl-cube
have a different layout, not only will every shader double when applied to them, but you will never be able to render both with the same shader object. To me this is a massive problem and red flag for a game engine. And it prevents you from ordering your objects shader-first since the actual GL shader objects are muddled under a caching layer. So in edge cases, even if your gl-sphere
and gl-cube
meshes are sorted by their gl-shader
object, you might end up with rendering that looks like this in terms of shader switches: ABABABAB.
There is also the subject of uniforms. Let's say you do shader.uniforms.proj = mat4
, does that trigger a gl.uniform
call to each cached shader? Uniforms might be relatively cheap, but there's no point in doubling or tripling your uniform uploads per frame if you can avoid it.
Further; it becomes more complicated when somebody wants to use the raw webgl api alongside stackgl. Using gl.getUniform(shader.handle, foo)
etc could lead to tricky problems. It would have to be shader.handleForLayout( attribLayout )
or something.
bunny
or stanford-dragon
modules work). That way everyone could use their own application/shader specific vertex format which would be tuned to the problem that they are trying to solve, rather than trying to over engineer some one-size fits all solution for all casesObviously shader switching is bad, and the current system in no way promotes this or encourages it. It is up to the application to handle these details (as there are practically infinite ways to set this up).
Regarding uniforms, setting the uniform value shouldn't change the value for all shaders. If you do change a location using shader.attributes.blah.location = x
, the current behavior is that it will trigger a relink and clear out all the uniforms. Obviously doing this in a render loop is bad, and this behavior is discouraged.
Maybe the solution would be to just make it more clear in the documentation that changing attribute locations triggers relinking?
The current strategy for promoting reusability in graphics code is that users would still control vertex formats, buffers, and geometry and they would still write custom shader code. However, there would be plenty of tools to make this process as streamlined as possible, with reusable glsl subroutines (via glslify) and common geometry processing libraries via ndarray and others.
I agree sphere/cube should be in the form of cells/positions/normals, but I also think most users will want a higher-level wrapper so they don't need to manually set up buffers. Hooking up buffers and shaders manually is where things will crap out if you aren't really conscious of attribute locations (and who in their right mind wants to worry about those).
I ran into this in development of gl-quad-batcher
because I was using the same VAO with various different shaders, some of which I had no control over and thus couldn't predict their vertex layout. In this case the "jank" (just a single frame of shader recompilation) could happen at any point, i.e. when a sprite with a custom shader enters the scene for the first time. Since shader recompilation is fairly slow in WebGL/ANGLE I think we should not be designing around it.
So maybe the best approach is to have another module like gl-shader-vao
(or a better name) which handles the attribute mappings when you call vao.bind(shader)
. This way gl-vao
and gl-shader
will stay the same and have no ties to each other.
Then modules like gl-geometry
could be changed to use this instead of gl-vao
to also avoid shader recompilation.
I just ran into a tricky situation where the same shader compiled in Chrome with [ position, uv ]
, and in FF/Safari as [ uv, position ]
... :cry: The end result was that nothing was drawn correctly. Nasty stuff.
@mattdesl maybe the solution there is to make gl-shader-core sort the attributes lexicographically unless otherwise specified, so that there is no ambiguity.
@mattdesl , maybe I misunderstand the problem, but couldn't you guys use reflection over the shader program to get a mapping of uniform & attributes to locations? See: https://github.com/nickdesaulniers/webgl-shader-loader/blob/master/webGLShaderLoader.js#L123-L143
This uses gl.getProgramParameter
with gl.ACTIVE_ATTRIBUTES
and gl.ACTIVE_UNIFORMS
to get the number of attributes and uniforms, then packs an object where the keys are the attribute or uniform's identifier and the values are the location. It gets the identifier using gl.getActiveAttrib
and gl.getActiveUniform
and the locations using gl.getAttribLocation
and gl.getUniformLocation
.
That way, it doesn't matter what browser compiles your shader, you get webgl implementation non-specific shader locations.
leaving this here for future discussion.
The problem: let's say we have a utility mesh like
gl-sphere
and it expects attributes in the order[ position, colors, normals ]
. However, we want to give the user the ability to render it with a custom shader -- but their shader might have attributes in the order[position, normals, colors]
.The solution should not need to re-compile shaders since it's expensive and doesn't allow for shader re-use.
One idea is to use string aliases when defining the attributes of a VAO, and have the user pass a shader to
vao.bind()
. Then the VAO always knows which attribute locations to bind, assuming thegl-shader
has them listed in the correct order.