Resolving shader attributes

Mar 9, 2009 at 12:58 PM
I think I answered my own question while writing it up. I'll leave it here for future reference anyway. I'm currently resolving my shaders' textures to ids within the draw via drawState.GetShaderAttributeNameUniqueID(), right before setting them on the shader via SetTexture().

I'm creating my shaders via reflection so I don't have direct access to the particular shader instances' fields when drawing. I'm assuming/hoping that I can set the texture once at load time, so I should be able to inspect the fields, look for my texture slots and set them once at load time.
Coordinator
Mar 9, 2009 at 1:04 PM
Edited Mar 9, 2009 at 1:06 PM

I'd recommend not using reflection to inspect the shader class. Using GetShaderAttributeNameUniqueID and using the overloaded SetTexture(id) would be the way to go, however it does assume you know the name of the texture ahead of time. (SetTexture will return true if the value was set correctly).

If you create an instance of the shader, you can use it as an IShader (as all shaders implement IShader), which allows calling SetTexture. Set the texture once and everything will be fine :-)
Shaders share the PixelShader / VertexShader instances statically, but keep their own copy of shader constants, textures, etc. The only bit you need to watch out for is potentially keeping resources alive, and the like.

Coordinator
Mar 9, 2009 at 1:13 PM

On second thought.

If you are creating multiple instances of the same shader, to change the texture - then the better option is just to reuse a single instance of the shader (and set the texture each time you draw with it). This is potentially more efficient (it won't be less so) and will save memory. You don't even need to call Bind() again :-)

Mar 9, 2009 at 1:13 PM
Edited Mar 9, 2009 at 1:18 PM
So I should keep doing what I was already doing, but perhaps cache the result and only ever set the texture on the first draw? I want to avoid the id lookup each draw. Currently I'm looking up the id and setting the texture each frame.

edit: woops, will always need to set the texture each frame, but the id could be cached first draw.
Coordinator
Mar 9, 2009 at 1:17 PM
Edited Mar 9, 2009 at 1:18 PM

The ID will never change for the life cycle of the application, so you do not need to call GetID every frame. When you call SetTexture - internally it simply runs through a switch statement of all the IDs of the textures uses, if it matches, it assigns the matching property of the class. So if you can, by far the best way is just to directly assign the property.

Let me know how you get on.

Mar 9, 2009 at 1:36 PM
Had tunnel vision due to only having one model, with one material using 1 shader and 1 diffuse map in my scene :)
Simple solution in the end.

      for (int i=0; i < TETextureMapCount; i++)
      {
        if (m_textures[i].texture != null)
        {
          // cache the shader attribute ID on first lookup
          if (m_textures[i].attributeID < 0)
            m_textures[i].attributeID = a_state.GetShaderAttributeNameUniqueID(TETextureMapNames[i]);
          m_shader.SetTexture(a_state, m_textures[i].attributeID, m_textures[i].texture.Texture2D);
        }
      }