cocos2d/cocos2d-objc

Uniforms should be part of the shader and not the sprite

Birkemose opened this issue · 7 comments

Uniforms should be part of the shader, and not the sprite.

What use case are you imagining here? The benefit of storing uniforms with the sprite is that multiple sprites can share one shader object but the inputs to the shader can vary from sprite to sprite.

@slembcke will probably have some thoughts on this too.

Adding mutable state to shaders means that you can't easily share them between different objects.

If you want to share uniforms between multiple objects, then you want to use the global uniform dictionary (CCDirector.globalShaderUniforms).

If you are doing custom rendering and want to set a shader + uniforms, then you want to to use CCRenderState. Uniforms aren't really part of nodes or sprites, they just track the uniforms so the user doesn't have to make a render state object themselves.

There currently seems to be no way to gracefully create a descendant of CCShader, and add custom uniforms.

What do you mean by adding custom uniforms? Normally I would think of that as defining them in the GLSL source, since there isn't any extra work to do in ObjC. Do you mean adding some of the missing GLSL types like ivec2 or mat3?

I am trying to make a CCShader descendant, with specialised functionality, including defining custom uniforms which the user can access. Maybe not the most common task, but none the less it should be doable.

I think I understand the mechanics of sprite, shaders, render states and batching, and while I also understand the node's need to hold states like render states, etc, these objects still have to be 100% self contained. If some of the functionality is moved to the node, it makes it impossible to override the objects for changed functionality. And yeah, it might not be (m)any doing that, but then at least the discussion can be for pure OOD reasons.
As it is now, 100 sprites will hold 100 empty shaderUniform dictionaries. If I set all 100 dictionaries to the same uniform, containing the same value, the renderer will break the 100 sprites into 100 batches, even if there is absolutely no need for it as long as it is the same shader the user is using. So I could argue that batching does not work for uniforms.
And in theory, you could just as well set the uniform for the shader, and not care if it was ever actually used. The user would have to change the shader code anyways if he wanted to use the uniform, and thus break the batch that way.

Now, this is getting a bit academic, and as english is not my native tongue, I tend to ramble a lot.
At the end of the day, it all boils down to, that if you have two seconds to answer where uniforms belong, you would answer "the shader" ... :)

Uniform dictionaries are only created when a custom uniform other than the main texture is set. There are very few places in Cocos2D that do this, so they are generally only created when users are creating custom shaders or using CCEffects.

If you want to have all of your sprites using a specific shader to share the same uniform, and be drawn batched, you can use the global uniform dictionary for that. Galactic Guardian uses this to draw the burning debris pieces for example. Since they don't need any unique uniform values, they are set using globals, and drawn in a single batch.

The uniforms are only set once at startup, and it's done here:
https://github.com/slembcke/GalacticGuardian.spritebuilder/blob/master/Source/BurnSprite.m#L34

If you override the renderState property, it's possible to draw more than one large batch using a single shader, but this is probably not very common. Storing the uniform state on the shader itself instead of in a separate state object would make this even more difficult.

At the end of the day, it all boils down to, that if you have two seconds to answer where uniforms belong, you would answer "the shader"

Uniform state being stored in the shader object is very strongly a GL 2 idea. In newer graphics APIs (GL ES 3 included) uniforms are stored in buffer objects. Even older versions of DirectX stored the uniforms (shader constants) as global state instead of associating them with the shader objects directly.

I really was not arguing if the uniforms should be a separate object or not. I think they should. I was arguing if they should be accessed through the shader, or through the node.
Enough on this from me :)