Reusing renderstates

Hi all,



I am a bit confused about renderstates usage.



Should I create them only once and reuse them (like "renderstate for transparent things") or can I create as many RenderStates without worring about performance?



Even more, should each node have it separate copy of each RenderState it uses or may I share them?



Thank you!

RenderStates are automatically inherited by the SceneGraph parent of a Node (whenever it is set to do so… by default it is disabled in TextureState, etc.)



In general, you should only set RenderStates whenever you want to change a property of the node and its children. If you set the same state to two nodes, then when changing one, the other will also change, yielding some potential weird results, specially while updating if you don't update the top of the hierarchy.



Hope it helps

Ok but what about renderstates that many nodes share at different hierarchyh levels, like ZBuffer, Cull and Alpha for transparent materials, and which state is not going to change ever?



Should I create those RenderStates and apply to all transparent nodes or may I afford to create those 3 for each transparent node?

You should reuse renderstates as much as possible. Best is to create your renderstates, and dont change them during rendering, only switch to others if needed. Not all renderstates can be handled the same, texturestate is closely tied with a specific object, but an alpha-test/blend state can be (should be) reused.

You're allowed to reuse most… for some I wouldn't know what would happen from the top of my head (eg LightState).



Reusing them will give you a little bonus because using less memory, and makes it easier for you to change properties of many objects at once (eg an alphastate), even if they are not in the same branch.



I think for must of these you shouldn't overestimate the performance gains though… eventually they're all "interpretted" into OpenGL calls. (FPS) Perfomance wise you'll only have very minimal theoretical gains for reusing the same object (L1/L2 cache hit rates and such)



Edit: forgot about state tracking though… look in RenderState.java and you see some RenderStates can be tracked by object reference (search for QUICK_COMPARE). So for these it might give you an extra little boost.

reuse = less memory consumption. my main and only reason to do this.

Yes, that's the state tracking by object reference that's used for some states (again, search for QUICK_COMPARE and you'll see it's not all them).



Memory usage is not that big for most states, so I'd focus on making my game rather than little things like that…

but then again  HamsterofDeath has half a game done already, and what do I have?? :stuck_out_tongue:


Thank you all for your very enlightening answers!  :)

So I am creating all common render states at level building time, and storing them on a Map (I should use integers as the map key but that's another matter).

I am sorry I have no metrics on performance change. I don't have a FPS or profiling framework in place, and my map is changing constantly so I cannot retrieve metrics for these things.

Anyway, I think this naming and reusing of render states also improves code readability (to set a RenderState I just need a get call to the map).

I case someone want's an example, my simplistic approach looks like this:


   /**
    * Creates some typical render states
    */
   public static void initializeCommonRenderStates(Level level) {

      // Create ZBuffer for depth
      ZBufferState zb = DisplaySystem.getDisplaySystem().getRenderer().createZBufferState();
      zb.setEnabled(true);
      zb.setFunction(ZBufferState.CF_LEQUAL);
      level.getCommonRenderStates().put("zBuffer", zb);

      // ZBuffer read only
      ZBufferState zs = DisplaySystem.getDisplaySystem().getRenderer().createZBufferState();
      zs.setWritable(false);
      zb.setFunction(ZBufferState.CF_LEQUAL);
      level.getCommonRenderStates().put("zBufferReadOnly", zb);

      // Cull back sides
      CullState cs = DisplaySystem.getDisplaySystem().getRenderer().createCullState();
      cs.setCullMode(CullState.CS_BACK);
      level.getCommonRenderStates().put("cullBack", cs);

      // Cull back sides
      CullState csn = DisplaySystem.getDisplaySystem().getRenderer().createCullState();
      csn.setCullMode(CullState.CS_NONE);
      level.getCommonRenderStates().put("cullBone", csn);   

      // Alpha state
      AlphaState as = DisplaySystem.getDisplaySystem().getRenderer().createAlphaState();
      as.setBlendEnabled(true);
      level.getCommonRenderStates().put("alpha", as); 

      // Fog state
      FogState fs = DisplaySystem.getDisplaySystem().getRenderer().createFogState();
      fs.setEnabled(level.getLevelData().isFogEnabled());
      fs.setColor(level.getLevelData().getFogColor());
      fs.setEnd(level.getLevelData().getFogEnd());
      fs.setStart(level.getLevelData().getFogStart());
      level.getCommonRenderStates().put("fog", fs);

      // Fog state disabled
      FogState fsd = DisplaySystem.getDisplaySystem().getRenderer().createFogState();
      fsd.setEnabled(false);
      level.getCommonRenderStates().put("fogDisabled", fsd);
   }