wined3d: Support clipplanes with GLSL.

This is the Nth attemt to make clipping work with GLSL shaders. The patch now
uses the GLSL quirk table to handle cards that need a custom varying for
gl_ClipPos, and the code is adapted to the changed state table and shader
backend system.
This commit is contained in:
Stefan Dösinger 2009-05-08 17:24:01 +02:00 committed by Alexandre Julliard
parent 45563979bd
commit 2cb8f42168
6 changed files with 83 additions and 24 deletions

View file

@ -2082,6 +2082,8 @@ static void shader_arb_get_caps(WINED3DDEVTYPE devtype, const WineD3D_GL_Info *g
TRACE_(d3d_caps)("Hardware pixel shader version 1.4 enabled (ARB_PROGRAM)\n");
pCaps->MaxPixelShaderConst = GL_LIMITS(pshader_constantsF) - ARB_SHADER_RESERVED_PS_CONSTS;
}
pCaps->VSClipping = FALSE; /* TODO: GL_NV_vertex_program2_option provides this */
}
static BOOL shader_arb_color_fixup_supported(struct color_fixup_desc fixup)

View file

@ -3799,6 +3799,7 @@ static HRESULT WINAPI IWineD3DImpl_CreateDevice(IWineD3D *iface, UINT Adapter,
object->shader_backend->shader_get_caps(DeviceType, &adapter->gl_info, &shader_caps);
object->d3d_vshader_constantF = shader_caps.MaxVertexShaderConst;
object->d3d_pshader_constantF = shader_caps.MaxPixelShaderConst;
object->vs_clipping = shader_caps.VSClipping;
memset(&ffp_caps, 0, sizeof(ffp_caps));
frag_pipeline = select_fragment_implementation(adapter, DeviceType);
@ -4058,6 +4059,18 @@ static BOOL match_fglrx(const WineD3D_GL_Info *gl_info) {
return TRUE;
}
static BOOL match_dx10_capable(const WineD3D_GL_Info *gl_info) {
/* DX9 cards support 40 single float varyings in hardware, most drivers report 32. ATI misreports
* 44 varyings. So assume that if we have more than 44 varyings we have a dx10 card.
* This detection is for the gl_ClipPos varying quirk. If a d3d9 card really supports more than 44
* varyings and we subtract one in dx9 shaders its not going to hurt us because the dx9 limit is
* hardcoded
*
* dx10 cards usually have 64 varyings
*/
return gl_info->max_glsl_varyings > 44;
}
static void quirk_arb_constants(WineD3D_GL_Info *gl_info) {
TRACE_(d3d_caps)("Using ARB vs constant limit(=%u) for GLSL\n", gl_info->vs_arb_constantsF);
gl_info->vs_glsl_constantsF = gl_info->vs_arb_constantsF;
@ -4161,6 +4174,10 @@ static void quirk_texcoord_w(WineD3D_GL_Info *gl_info) {
gl_info->set_texcoord_w = TRUE;
}
static void quirk_clip_varying(WineD3D_GL_Info *gl_info) {
gl_info->glsl_clip_varying = TRUE;
}
struct driver_quirk quirk_table[] = {
{
match_ati_r300_to_500,
@ -4198,6 +4215,11 @@ struct driver_quirk quirk_table[] = {
match_fglrx,
quirk_one_point_sprite,
"Fglrx point sprite crash workaround"
},
{
match_dx10_capable,
quirk_clip_varying,
"Reserved varying for gl_ClipPos"
}
};

View file

@ -704,6 +704,17 @@ static void shader_glsl_update_float_pixel_constants(IWineD3DDevice *iface, UINT
}
}
static int vec4_varyings(DWORD shader_major, const WineD3D_GL_Info *gl_info)
{
int ret = GL_LIMITS(glsl_varyings) / 4;
/* 4.0 shaders do not write clip coords because d3d10 does not support user clipplanes */
if(shader_major > 3) return ret;
/* 3.0 shaders may need an extra varying for the clip coord on some cards(mostly dx10 ones) */
if(gl_info->glsl_clip_varying) ret -= 1;
return ret;
}
/** Generate the variable & register declarations for the GLSL output target */
static void shader_generate_glsl_declarations(IWineD3DBaseShader *iface, const shader_reg_maps *reg_maps,
SHADER_BUFFER *buffer, const WineD3D_GL_Info *gl_info,
@ -748,9 +759,11 @@ static void shader_generate_glsl_declarations(IWineD3DBaseShader *iface, const s
/* Subtract the other potential uniforms from the max available (bools, ints, and 1 row of projection matrix).
* Subtract another uniform for immediate values, which have to be loaded via uniform by the driver as well.
* The shader code only uses 0.5, 2.0, 1.0, 128 and -128 in vertex shader code, so one vec4 should be enough
* (Unfortunately the Nvidia driver doesn't store 128 and -128 in one float
* (Unfortunately the Nvidia driver doesn't store 128 and -128 in one float).
*
* Writing gl_ClipPos requires one uniform for each clipplane as well.
*/
max_constantsF = GL_LIMITS(vshader_constantsF) - 3;
max_constantsF = GL_LIMITS(vshader_constantsF) - 3 - GL_LIMITS(clipplanes);
max_constantsF -= count_bits(This->baseShader.reg_maps.integer_constants);
/* Strictly speaking a bool only uses one scalar, but the nvidia(Linux) compiler doesn't pack them properly,
* so each scalar requires a full vec4. We could work around this by packing the booleans ourselves, but
@ -902,13 +915,13 @@ static void shader_generate_glsl_declarations(IWineD3DBaseShader *iface, const s
{
if (use_vs(device->stateBlock))
{
shader_addline(buffer, "varying vec4 IN[%u];\n", GL_LIMITS(glsl_varyings) / 4);
shader_addline(buffer, "varying vec4 IN[%u];\n", vec4_varyings(reg_maps->shader_version.major, gl_info));
} else {
/* TODO: Write a replacement shader for the fixed function vertex pipeline, so this isn't needed.
* For fixed function vertex processing + 3.0 pixel shader we need a separate function in the
* pixel shader that reads the fixed function color into the packed input registers.
*/
shader_addline(buffer, "vec4 IN[%u];\n", GL_LIMITS(glsl_varyings) / 4);
shader_addline(buffer, "vec4 IN[%u];\n", vec4_varyings(reg_maps->shader_version.major, gl_info));
}
}
@ -1085,7 +1098,7 @@ static void shader_glsl_get_register_name(const struct wined3d_shader_register *
if (This->baseShader.reg_maps.shader_version.major >= 3)
{
DWORD idx = ((IWineD3DPixelShaderImpl *)This)->input_reg_map[reg->idx];
DWORD in_count = GL_LIMITS(glsl_varyings) / 4;
DWORD in_count = vec4_varyings(This->baseShader.reg_maps.shader_version.major, gl_info);
if (reg->rel_addr)
{
@ -3237,7 +3250,7 @@ static void handle_ps3_input(SHADER_BUFFER *buffer, const WineD3D_GL_Info *gl_in
DWORD usage, usage_idx, usage_out, usage_idx_out;
DWORD *set;
DWORD in_idx;
DWORD in_count = GL_LIMITS(glsl_varyings) / 4;
DWORD in_count = vec4_varyings(3, gl_info);
char reg_mask[6], reg_mask_out[6];
char destination[50];
@ -3467,7 +3480,7 @@ static GLhandleARB generate_param_reorder_function(IWineD3DVertexShader *vertexs
semantics_out = vs->semantics_out;
/* This one is tricky: a 3.0 pixel shader reads from a 3.0 vertex shader */
shader_addline(&buffer, "varying vec4 IN[%u];\n", GL_LIMITS(glsl_varyings) / 4);
shader_addline(&buffer, "varying vec4 IN[%u];\n", vec4_varyings(3, gl_info));
shader_addline(&buffer, "void order_ps_input(in vec4 OUT[%u]) {\n", MAX_REG_OUTPUT);
/* First, sort out position and point size. Those are not passed to the pixel shader */
@ -3498,7 +3511,7 @@ static GLhandleARB generate_param_reorder_function(IWineD3DVertexShader *vertexs
shader_addline(&buffer, "}\n");
} else if(ps_major >= 3 && vs_major < 3) {
shader_addline(&buffer, "varying vec4 IN[%u];\n", GL_LIMITS(glsl_varyings) / 4);
shader_addline(&buffer, "varying vec4 IN[%u];\n", vec4_varyings(3, gl_info));
shader_addline(&buffer, "void order_ps_input() {\n");
/* The vertex shader wrote to the builtin varyings. There is no need to figure out position and
* point size, but we depend on the optimizers kindness to find out that the pixel shader doesn't
@ -3713,7 +3726,7 @@ static void set_glsl_shader_program(IWineD3DDevice *iface, BOOL use_ps, BOOL use
if (pshader
&& ((IWineD3DPixelShaderImpl *)pshader)->baseShader.reg_maps.shader_version.major >= 3
&& ((IWineD3DPixelShaderImpl *)pshader)->declared_in_count > GL_LIMITS(glsl_varyings) / 4)
&& ((IWineD3DPixelShaderImpl *)pshader)->declared_in_count > vec4_varyings(3, gl_info))
{
TRACE("Shader %d needs vertex color clamping disabled\n", programId);
entry->vertex_color_clamp = GL_FALSE;
@ -4242,6 +4255,7 @@ static GLuint shader_glsl_generate_vshader(IWineD3DVertexShader *iface,
*/
shader_addline(buffer, "gl_Position.y = gl_Position.y * posFixup.y;\n");
shader_addline(buffer, "gl_Position.xy += posFixup.zw * gl_Position.ww;\n");
shader_addline(buffer, "gl_ClipVertex = gl_Position;\n");
/* Z coord [0;1]->[-1;1] mapping, see comment in transform_projection in state.c
*
@ -4310,6 +4324,8 @@ static void shader_glsl_get_caps(WINED3DDEVTYPE devtype, const WineD3D_GL_Info *
*/
pCaps->PixelShader1xMaxValue = 8.0;
TRACE_(d3d_caps)("Hardware pixel shader version %d.%d enabled (GLSL)\n", (pCaps->PixelShaderVersion >> 8) & 0xff, pCaps->PixelShaderVersion & 0xff);
pCaps->VSClipping = TRUE;
}
static BOOL shader_glsl_color_fixup_supported(struct color_fixup_desc fixup)

View file

@ -525,7 +525,7 @@ static void state_clipping(DWORD state, IWineD3DStateBlockImpl *stateblock, Wine
DWORD enable = 0xFFFFFFFF;
DWORD disable = 0x00000000;
if (use_vs(stateblock))
if (!stateblock->wineD3DDevice->vs_clipping && use_vs(stateblock))
{
/* The spec says that opengl clipping planes are disabled when using shaders. Direct3D planes aren't,
* so that is an issue. The MacOS ATI driver keeps clipping planes activated with shaders in some
@ -3496,9 +3496,22 @@ static void clipplane(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DCo
}
/* Clip Plane settings are affected by the model view in OpenGL, the View transform in direct3d */
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadMatrixf(&stateblock->transforms[WINED3DTS_VIEW].u.m[0][0]);
if(!use_vs(stateblock)) {
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadMatrixf(&stateblock->transforms[WINED3DTS_VIEW].u.m[0][0]);
} else {
/* with vertex shaders, clip planes are not transformed in direct3d,
* in OpenGL they are still transformed by the model view.
* Use this to swap the y coordinate if necessary
*/
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
if(stateblock->wineD3DDevice->render_offscreen) {
glScalef(1.0, -1.0, 1.0);
}
}
TRACE("Clipplane [%f,%f,%f,%f]\n",
stateblock->clipplane[index][0],
@ -4403,7 +4416,7 @@ static void vertexdeclaration(DWORD state, IWineD3DStateBlockImpl *stateblock, W
if(context->last_was_vshader) {
updateFog = TRUE;
if(!isStateDirty(context, STATE_RENDER(WINED3DRS_CLIPPLANEENABLE))) {
if(!device->vs_clipping && !isStateDirty(context, STATE_RENDER(WINED3DRS_CLIPPLANEENABLE))) {
state_clipping(STATE_RENDER(WINED3DRS_CLIPPLANEENABLE), stateblock, context);
}
}
@ -4414,17 +4427,19 @@ static void vertexdeclaration(DWORD state, IWineD3DStateBlockImpl *stateblock, W
if(!context->last_was_vshader) {
unsigned int i;
static BOOL warned = FALSE;
/* Disable all clip planes to get defined results on all drivers. See comment in the
* state_clipping state handler
*/
for(i = 0; i < GL_LIMITS(clipplanes); i++) {
glDisable(GL_CLIP_PLANE0 + i);
checkGLcall("glDisable(GL_CLIP_PLANE0 + i)");
}
if(!device->vs_clipping) {
/* Disable all clip planes to get defined results on all drivers. See comment in the
* state_clipping state handler
*/
for(i = 0; i < GL_LIMITS(clipplanes); i++) {
glDisable(GL_CLIP_PLANE0 + i);
checkGLcall("glDisable(GL_CLIP_PLANE0 + i)");
}
if(!warned && stateblock->renderState[WINED3DRS_CLIPPLANEENABLE]) {
FIXME("Clipping not supported with vertex shaders\n");
warned = TRUE;
if(!warned && stateblock->renderState[WINED3DRS_CLIPPLANEENABLE]) {
FIXME("Clipping not supported with vertex shaders\n");
warned = TRUE;
}
}
if(wasrhw) {
/* Apply the transform matrices when switching from rhw drawing to vertex shaders. Vertex

View file

@ -3962,6 +3962,7 @@ typedef struct _WineD3D_GL_Info {
BOOL arb_vs_offset_limit;
BOOL set_texcoord_w;
DWORD reserved_glsl_constants;
BOOL glsl_clip_varying;
BOOL supported[OPENGL_SUPPORTED_EXT_END + 1];

View file

@ -730,6 +730,8 @@ struct shader_caps {
DWORD MaxPShaderInstructionsExecuted;
DWORD MaxVertexShader30InstructionSlots;
DWORD MaxPixelShader30InstructionSlots;
BOOL VSClipping;
};
enum tex_types
@ -1461,6 +1463,7 @@ struct IWineD3DDeviceImpl
unsigned int max_ffp_textures, max_ffp_texture_stages;
DWORD d3d_vshader_constantF, d3d_pshader_constantF; /* Advertised d3d caps, not GL ones */
DWORD vs_clipping;
WORD view_ident : 1; /* true iff view matrix is identity */
WORD untransformed : 1;