On 11/04/2008, Stefan Dösinger stefan@codeweavers.com wrote:
- The state table should be selectable based on the available opengl features
and possibly registry settings. I think we all agree on that
Up to the level of being able to use different state handlers in different situations. I don't necessarily agree with copying and swapping the entire table in one piece.
- We want a fixed function vertex and fragment pipeline replacement with ARB
and GLSL
Only GLSL is a requirement for me. ARB could be nice, but is probably redundant.
- We want to be able to support pixel shaders with atifs and nvts. I don't
know if that will ever be implemented, but if we choose a design that makes this hard or impossible that's not going to help
This is not a hard requirement for me, although I certainly think we should be able to create a design that allows for this.
- Even if we have an ARB/GLSL replacement, it should be possible to use D3D
shaders but still use the opengl fixed function pipeline
Agreed.
- A nice to have is to be able to use the replacement pipelines together with
shaders, but that is not a hard requirement for me. We need an ARB and GLSL replacement anyway.
I assume you mean atifs & nvrc specifically here, in which case this is probably a bit more important than "nice to have". The most tricky situation to support here will be cards that support vertex shaders, but not fragment shaders. If we want to support pixel shaders using atifs / nvrc we have to allow mixing GLSL / ARB with nvrc / atifs for it to be of any use.
I understand Henri's suggestion this way: Don't put the state table into the shader backend(*), but select it separately. This way the ATIFS pipeline replacement doesn't have to be a shader model. That way we can choose the ARB shader backend and ATIFS state table on r200 cards, and use GLSL and ATIFS on newer cards.
That way we get full shader support and still the advantages of the pipeline replacement without using 3 inherited shader backends. Then the state table could have some backend to enable / disable ATIFS like in this patch. (The bottom line of the patch here is that we should enable the extension we *use*, and not what happens to be available).
The state table would report the fixed function caps it has, and the shader backend reports the programmable pipeline caps. GetDeviceCaps has to collect the caps from multiple places, but that isn't an issue IMO.
Is that correct, or did I missunderstand something?
My main point is that a fixed function replacement and the shader backend should be two different things, at least interface wise. My suggestion for an implementation would be somethingg like this:
- Split the state table in three pieces: vertex processing states (eg. lighting, materials, etc), fragment processing states (eg. texture stage stuff) and other states (eg. blending states). Aside from being clearer this allows you to swap these parts out independently from each other and possibly skip applying them as a whole in case a shader is active for that part of the pipeline (I imagine this could have some performance advantages as well, although I'm not sure how much). - Allow the state table more explicitly to have some state of its own. It currently has the list of dirty states of course, but it's more managed by the device than by the statetable as such. In effect this would introduce a state management object.
The basic setup would be something like this:
struct shader_backend_t { void (*shader_select)(void *data, BOOL usePS, BOOL useVS); void (*shader_load_constants)(void *data, char usePS, char useVS); ... };
struct state_management_t { void (*mark_state_dirty)(void *data, DWORD state); void (*apply_states)(void *data); ... };
struct IWineD3DDeviceImpl { ... struct state_management_t vertex_state_manager; struct state_management_t fragment_state_manager; struct state_management_t other_state_manager; struct shader_backend_t shader_backend;
void *vertex_private_data; void *fragment_private_data; void *other_private_data; void *shader_private_data; ... };
/* Usage */ device->vertex_state_manager->mark_state_dirty(device->vertex_private_data, state); device->fragment_state_manager->mark_state_dirty(device->fragment_private_data, state); etc. ... if (!use_vs) { device->vertex_state_manager->apply_states(device->vertex_private_data); } if (!use_ps) { device->fragment_state_manager->apply_states(device->fragment_private_data); } device->shader_backend->select_shader(device->shader_private_date, use_vs, use_ps);
Some example configurations:
GLSL FFP, GLSL shaders device = {glsl_vsm, glsl_fsm, ff_osm, glsl_shader_backend, glsl_private_data, glsl_private_data, ff_private_data, glsl_private_data};
Fixed function vertex, ATIFS fragment processing, GLSL shader backend device = {ff_vsm, atifs_fsm, ff_osm, glsl_shader_backend, ff_private_data, atifs_private_data, ff_private_data, glsl_private_data};
ARB vertex FFP, Fixed function fragment, ARB shaders device = arb_vsm, ff_fsm, ff_osm, arb_shader_backend, arb_ffp_private_data, ff_private_data, ff_private_data, arb_shader_private_data};
This doesn't support mixing eg. ARB vertex shaders with NVRC pixel shaders, but it would "simply" be a matter of splitting up the shader backend in a similar way to the state table. Important to note here is that the private data could be shared between fixed function replacements and the shader backend, like in the case of GLSL. I could imagine using a structure more similar to state_management_t for the shader backend as well.
Of course there are a number of variations you can make on this (perhaps most notably storing the call tables and private data in the same struct), but I think the important parts are the split between vertex/fragment/other/shader, and the state management having private data.
However, I see two issues with that:
*) In which place do we decide which program/shader to use in the end? If we have an ARB fixed function replacement program and an ARB program generated from a D3D shader? Currently this happens to work because ARB/GLSL override NVTS and NVRC, but this fails if we have a shader and ffp implementation using the same extension
The private data between the FFP replacement and the shader backend can be shared. That means our general GLSL management stuff can see you've got eg. a FFP vertex shader and a pixel shader and link them together.
*) What do we do if a shader implementation wants to overwrite some states for its programmable pipeline work? For example take my GLSL clipplane patch: The ARB shader backend disables clipplanes when a shader is used. This example is about a driver bug, but there are others as well: ATIFS and NVTS with a pixel shader implementation will have to override a part of the sampler setting(the stuff that is currently done in activate_dimensions)
How would you deal with this?
I guess this will mostly be an issue for states that end up being in the "other" category. In general I think that as long as it's only for a couple of states we can just keep doing more or less what we currently do and check if the relevant state management backend is used.