Hi,
Ok, so the main idea is to separate the applying of GL state from the tracking of D3D state. Looks like a good idea.
Fully agreed
What I would like to add to that is something BBrox mentioned on IRC a while back... grouping related states together and marking that group dirty / clean. That way we would get a tree like structure for the states, which would make checking what states changed and need to be applied somewhat faster. While it would be possible to add that afterwards, I think it would be easier to just take it into account when designing the new stateblock structure.
I think we should do the change quicky, even if we risk regressions. I do not think that we should add comments stating "if you add new gl stuff add it to <new file.c>". But I think none of you wants that :-) What we can do for sure is to move render states, sampler states, matriced and bound shaders seperately, which we should do to keep patches small :-)
I don't like the way things are done right now - Set* functions can do one of two things - record to a stateblock, or apply state. Then the stateblock calls the Set* functions itself when it's applied - seems very ugly to me [ and also in certain places we're forced to disable recording to get a state applied immediately using a Set* function ].
You always record to a stateblock, be it the main device stateblock or the update stateblock, but yes, it's pretty ugly.
Let me illustrate my idea:
* Move out the GL calls from Set*State. Set*State writes the values to the update stateblock and updates the refcounts(maybe we should kick internal refcounting from wined3d altogether)
* Keep the stateblock and update stateblock structure as they are now. I think for recording stateblocks the idea is quite good
* Keep a list of dirty states for each gl context in use: We don't need something as fancy as trees for that, a little array can do the job, like this(example for render states, but can be used for all other stuff too):
WINED3DRENDERSTATETYPE updatedStates[WINEHIGHEST_RENDER_STATE] DWORD numDirtyStates;
SetRenderState(device, state, newValue) sets updatedStates[numDirtyStates] = state for each context and increments numDirtyStates. It doesn't store the value of the state.
In drawprim we have a loop for(i = 0; i < numDirtyStates; i++) { set_render_state(updatedStates[i]); } numDirtyStates = 0;
set_render_state does the opengl stuff. We can put that function into drawprim.c or a new file, e.g. opengl_utils.c like in old ddraw.
This concept can be optimized a bit: To group common states we can do that:
static const WINED3DRENDERSTATETYPE stategroup[] = { /*0*/ 0, /*WINED3DRS_TEXTUREHANDLE*/ 0, /*WINED3DRS_ANTIALIAS*/ WINED3DRS_ANTIALIAS, ... /*WINED3DRS_TEXTUREMAPBLEND*/ 0, ... /*WINED3DRS_FOGENABLE*/ WINED3DRS_FOGENABLE, ... /*WINED3DRS_FOGCOLOR*/ WINED3DRS_FOGCOLOR, /*WINED3DRS_FOGTABLEMODE*/ WINED3DRS_FOGTABLEMODE /*WINED3DRS_FOGSTART*/ WINED3DRS_FOGTABLEMODE /*WINED3DRS_FOGEND*/ WINED3DRS_FOGTABLEMODE /*WINED3DRS_FOGDENSITY*/ WINED3DRS_FOGDENSITY, ... /*WINED3DRS_FOGVERTEXMODE*/ WINED3DRS_FOGTABLEMODE ... /*WINED3DRS_BLENDOPALPHA*/ WINED3DRS_BLENDOPALPHA }; The current code applies FOGVERTEXMODE and FOGTABLEMODE in the same code, because the resulting gl values depend on both states. Also the applied fog range is important for this, even if we do not have it in the same group right now. (How come? looks buggy to me. Well, I was the one who did that). WINED3DRS_TEXTUREHANDLE and WINED3DRS_TEXTUREMAPPEDBLEND are legacy states which are wrapped to SetTexture and SetTextureStageState in ddraw.dll, so wined3d doesn't have to deal with them. We set them to 0 and cry bloody murder if such a state is applied.
With this modification SetRenderState would set updatedStates[numDirtyStates] = stategroup[state]; The little drawback is that we have 209 DWORDs hanging around, with some of them beeing plain useless. Well, that are 836 Bytes, and we safe some of them because set_render_state needs less case WINED3DRS_FOO: marks.
The above optimization doesn't bring much yet. Instead of applying 4 different fog states we apply FOGENABLE 4 times. But we can change the .changed field for each state in the stateblock to a changed[num contexts] array(dynamically allocated preferably). It is set to true when the state is changed the first time, and set to 0 when set_render_state applies the gl state. When it is TRUE already for the context then SetRenderState doesn't have to put the state again onto the change list. This way we can limit the size of the changed state array to WINEHIGHEST_RENDER_STATE too :-)