Am Sonntag 22 Oktober 2006 10:31 schrieb Ivan Gyurdiev:
I thought that my first reply was a bit off-topic, I think I should describe my idea with the table a bit better :-)
Each entry in the state table has 2 fields. They do not strictly depend on each other:
struct StateEntry { DWORD representative; APPLYSTATEFUNC func; };
The representative is a way to group states which depend on each other. For example WINED3DRS_ALPHATESTENABLED, WINED3DRS_ALPHAREF and WINED3DRS_COLORKEYENABLE go into the same gl state, so they are grouped. The reason for that is in the dirty list. SetRenderState pushes an element to the list instead of device->addDirtyState(This, State); /* Later per context */ it will do this: device->addDirtyState(This, States[State]->representative)
This way the code applying the states has to be called only once.
The entry func contains a pointer to the function handling setting the state. This is still a bunch of code, not a magic instruction how to call gl directly. To apply a state to gl
States[State]->func(State, stateblock);
is called. Basically this is equal to the current SetRenderState code:
switch(state) { case WINED3DRS_LIGHTING: <do something> break; case WINED3DRS_SOMETHINGELSE: <...> }
While I think that in that case inline functions containing the <do something> block look nicer than the current SetRenderState.
I think you agree that the state grouping is a bit inflexible. For example, the vertex declaration affects the fog settings. This way it would be consequent to group them. However, as other things depend on the vertex decl this will get a way to big group. The bigger the groups are the more likely it is that one of the states is changed, so this will eat performance.
While the vertex decl can change the fog, the fog settings will never affect the vertex declaration, so we have a sort of one-way dependency(compared to the alpha example). To overcome this we can still use some code in the function. For example state_fog(state, stateblock) can read the vertex declaration to decide what to do. In the same way it is perfectly fine for misc_vdecl(state, stateblock) to call state_fog() either unconditionally or when it thinks that fog needs to be verified. By doing that dirtifying the fog does not require all vertex data pointers to be updated, while the fog is still updated when the vertex decl is changed but the fog settings aren't.
One might argue that calling a foreign state function and reading a foreign state is not a nice design. Ok - But a switch statement(or bunch of if-blocks) wouldn't be better off. If you look at the current code, the fog things are scattered all over SetRenderState and drawprim.
Then there are other sorts of states, like sampler states and texture stage states. Texture stage states can go into the global state list just fine. One issue is that different stages depend on each other, due to D3DOP_DISABLE. To handle that properly we need to track the highest enabled texture stage and the lowest disabled texture stage per context, and some code in drawprim which cares for the stages in between(will be 0 in general, so just an if check).
Sampler states and bound textures are more difficult, as they are per-texture settings in gl and per device settings in d3d. There is no point in putting them into the main state list, instead there should be a dirty list per supported sampler. This list will contain the bound texture and the sampler states, and use a simmilar dirtification mechanism as the rest of the states. There is a one-way dependency too: A change of the texture may require a reapplication of the sampler states, but a change of a sampler state will not require a change of the texture.
A simmilar list will group sampler states together, and the function binding the texture will check the sampler states last used with the texture against the states in the stateblock, and if they are different call the sampler state specific functions to reapply them. The only way around this would be a per-texture dirty sampler state list which will make SetSamplerState way too expensive...
I hope that explained the whole plan a bit better than my last mail :-)
Stefan