From 54fa7129816a4d20bbd3d0649be539707e68311a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20D=C3=B6singer?= Date: Thu, 6 Dec 2007 22:10:11 +0100 Subject: [PATCH] wined3d: Initialize output texcoord .w to 1.0 if needed. The GL_ARB_vertex_program extension does not define a standard value for output texture coordinates. This makes problems when using vertex shaders with fixed function fragment processing because fffp divides the texture coords by its .w component. This means that gl shaders have to write to the .w component of texture coords. Direct3D shaders however do not. --- dlls/wined3d/baseshader.c | 9 +++++++++ dlls/wined3d/directx.c | 14 ++++++++++++++ dlls/wined3d/glsl_shader.c | 28 +++++++++++++++++++++++----- dlls/wined3d/vertexshader.c | 10 ++++++++++ dlls/wined3d/wined3d_private.h | 5 +++++ include/wine/wined3d_gl.h | 1 + 6 files changed, 62 insertions(+), 5 deletions(-) diff --git a/dlls/wined3d/baseshader.c b/dlls/wined3d/baseshader.c index 5f94a29cbd6..0a0c10e333c 100644 --- a/dlls/wined3d/baseshader.c +++ b/dlls/wined3d/baseshader.c @@ -463,6 +463,15 @@ HRESULT shader_get_registers_used( reg_maps->usesrelconstF = TRUE; } } + + /* WINED3DSPR_TEXCRDOUT is the same as WINED3DSPR_OUTPUT. _OUTPUT can be > MAX_REG_TEXCRD and is used + * in >= 3.0 shaders. Filter 3.0 shaders to prevent overflows, and also filter pixel shaders because TECRDOUT + * isn't used in them, but future register types might cause issues + */ + else if(WINED3DSPR_TEXCRDOUT == regtype && i == 0 /* Only look at writes */ && + !pshader && WINED3DSHADER_VERSION_MAJOR(This->baseShader.hex_version) < 3) { + reg_maps->texcoord_mask[reg] |= shader_get_writemask(param); + } } } } diff --git a/dlls/wined3d/directx.c b/dlls/wined3d/directx.c index c712bb404c2..a2699885162 100644 --- a/dlls/wined3d/directx.c +++ b/dlls/wined3d/directx.c @@ -2860,6 +2860,20 @@ static void fixup_extensions(WineD3D_GL_Info *gl_info) { gl_info->supported[ARB_TEXTURE_RECTANGLE] = TRUE; } } + + /* The Intel GPUs on MacOS set the .w register of texcoords to 0.0 by default, which causes problems + * with fixed function fragment processing. Ideally this flag should be detected with a test shader + * and opengl feedback mode, but some GL implementations(MacOS ATI at least, propably all macos ones) + * do not like vertex shaders in feedback mode and return an error, even though it should be valid + * according to the spec. + * + * We don't want to enable this on all cards, as it adds an extra instruction per texcoord used. This + * makes the shader slower and eats instruction slots which should be available to the d3d app. + */ + if(gl_info->gl_vendor == VENDOR_INTEL) { + TRACE("Enabling vertex texture coord fixes in vertex shaders\n"); + gl_info->set_texcoord_w = TRUE; + } } } diff --git a/dlls/wined3d/glsl_shader.c b/dlls/wined3d/glsl_shader.c index 0b68c7803c9..80b8ea9af05 100644 --- a/dlls/wined3d/glsl_shader.c +++ b/dlls/wined3d/glsl_shader.c @@ -2870,7 +2870,7 @@ static GLhandleARB generate_param_reorder_function(IWineD3DVertexShader *vertexs SHADER_BUFFER buffer; DWORD usage_token; DWORD register_token; - DWORD usage, usage_idx; + DWORD usage, usage_idx, writemask; char reg_mask[6]; semantic *semantics_out, *semantics_in; @@ -2880,8 +2880,21 @@ static GLhandleARB generate_param_reorder_function(IWineD3DVertexShader *vertexs buffer.newline = TRUE; if(vs_major < 3 && ps_major < 3) { - /* That one is easy: The vertex shader writes to the builtin varyings, the pixel shader reads from them */ - shader_addline(&buffer, "void order_ps_input() { /* do nothing */ }\n"); + /* That one is easy: The vertex shader writes to the builtin varyings, the pixel shader reads from them. + * Take care about the texcoord .w fixup though if we're using the fixed function fragment pipeline + */ + if((GLINFO_LOCATION).set_texcoord_w && ps_major == 0 && vs_major > 0) { + shader_addline(&buffer, "void order_ps_input() {\n"); + for(i = 0; i < min(8, MAX_REG_TEXCRD); i++) { + if(vs->baseShader.reg_maps.texcoord_mask[i] != 0 && + vs->baseShader.reg_maps.texcoord_mask[i] != WINED3DSP_WRITEMASK_ALL) { + shader_addline(&buffer, "gl_TexCoord[%u].w = 1.0;\n", i); + } + } + shader_addline(&buffer, "}\n"); + } else { + shader_addline(&buffer, "void order_ps_input() { /* do nothing */ }\n"); + } } else if(ps_major < 3 && vs_major >= 3) { /* The vertex shader writes to its own varyings, the pixel shader needs them in the builtin ones */ semantics_out = vs->semantics_out; @@ -2894,7 +2907,7 @@ static GLhandleARB generate_param_reorder_function(IWineD3DVertexShader *vertexs usage = (usage_token & WINED3DSP_DCL_USAGE_MASK) >> WINED3DSP_DCL_USAGE_SHIFT; usage_idx = (usage_token & WINED3DSP_DCL_USAGEINDEX_MASK) >> WINED3DSP_DCL_USAGEINDEX_SHIFT; - shader_glsl_get_write_mask(register_token, reg_mask); + writemask = shader_glsl_get_write_mask(register_token, reg_mask); switch(usage) { case WINED3DDECLUSAGE_COLOR: @@ -2910,8 +2923,13 @@ static GLhandleARB generate_param_reorder_function(IWineD3DVertexShader *vertexs case WINED3DDECLUSAGE_TEXCOORD: if (usage_idx < 8) { + if(!(GLINFO_LOCATION).set_texcoord_w || ps_major > 0) writemask |= WINED3DSP_WRITEMASK_3; + shader_addline(&buffer, "gl_TexCoord[%u]%s = OUT[%u]%s;\n", - usage_idx, reg_mask, i, reg_mask); + usage_idx, reg_mask, i, reg_mask); + if(!(writemask & WINED3DSP_WRITEMASK_3)) { + shader_addline(&buffer, "gl_TexCoord[%u].w = 1.0;\n", usage_idx); + } } break; diff --git a/dlls/wined3d/vertexshader.c b/dlls/wined3d/vertexshader.c index f67cbae9254..13349de3cf9 100644 --- a/dlls/wined3d/vertexshader.c +++ b/dlls/wined3d/vertexshader.c @@ -455,6 +455,16 @@ static VOID IWineD3DVertexShaderImpl_GenerateShader( /* We need a constant to fixup the final position */ shader_addline(&buffer, "PARAM posFixup = program.env[%d];\n", ARB_SHADER_PRIVCONST_POS); + if((GLINFO_LOCATION).set_texcoord_w) { + int i; + for(i = 0; i < min(8, MAX_REG_TEXCRD); i++) { + if(This->baseShader.reg_maps.texcoord_mask[i] != 0 && + This->baseShader.reg_maps.texcoord_mask[i] != WINED3DSP_WRITEMASK_ALL) { + shader_addline(&buffer, "MOV result.texcoord[%u].w, -helper_const.y;\n", i); + } + } + } + /* Base Shader Body */ shader_generate_main( (IWineD3DBaseShader*) This, &buffer, reg_maps, pFunction); diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index c0e12652fa1..86a6bb82d05 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -1672,6 +1672,7 @@ typedef struct shader_reg_maps { char packed_output[MAX_REG_OUTPUT]; /* vertex >= 3.0 */ char attributes[MAX_ATTRIBS]; /* vertex */ char labels[MAX_LABELS]; /* pixel, vertex */ + DWORD texcoord_mask[MAX_REG_TEXCRD]; /* vertex < 3.0 */ /* Sampler usage tokens * Use 0 as default (bit 31 is always 1 on a valid token) */ @@ -2012,6 +2013,10 @@ static inline int shader_get_regtype(const DWORD param) { ((param & WINED3DSP_REGTYPE_MASK2) >> WINED3DSP_REGTYPE_SHIFT2)); } +static inline int shader_get_writemask(const DWORD param) { + return param & WINED3DSP_WRITEMASK_ALL; +} + extern unsigned int shader_get_float_offset(const DWORD reg); static inline BOOL shader_is_pshader_version(DWORD token) { diff --git a/include/wine/wined3d_gl.h b/include/wine/wined3d_gl.h index 86ad677e989..c364f09380a 100644 --- a/include/wine/wined3d_gl.h +++ b/include/wine/wined3d_gl.h @@ -3685,6 +3685,7 @@ typedef struct _WineD3D_GL_Info { GL_VSVersion vs_ati_version; BOOL arb_vs_offset_limit; + BOOL set_texcoord_w; BOOL supported[OPENGL_SUPPORTED_EXT_END + 1];