diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c index 30b80cd5ab6..b93bb335154 100644 --- a/dlls/wined3d/device.c +++ b/dlls/wined3d/device.c @@ -846,6 +846,8 @@ static HRESULT WINAPI IWineD3DDeviceImpl_CreateTexture(IWineD3DDevice *iface, U HRESULT hr; unsigned int pow2Width; unsigned int pow2Height; + const GlPixelFormatDesc *glDesc; + getFormatDescEntry(Format, &GLINFO_LOCATION, &glDesc); TRACE("(%p) : Width %d, Height %d, Levels %d, Usage %#x\n", This, Width, Height, Levels, Usage); @@ -928,6 +930,7 @@ static HRESULT WINAPI IWineD3DDeviceImpl_CreateTexture(IWineD3DDevice *iface, U tmpW = max(1, tmpW >> 1); tmpH = max(1, tmpH >> 1); } + object->baseTexture.shader_conversion_group = glDesc->conversion_group; TRACE("(%p) : Created texture %p\n", This, object); return WINED3D_OK; @@ -947,6 +950,8 @@ static HRESULT WINAPI IWineD3DDeviceImpl_CreateVolumeTexture(IWineD3DDevice *ifa UINT tmpW; UINT tmpH; UINT tmpD; + const GlPixelFormatDesc *glDesc; + getFormatDescEntry(Format, &GLINFO_LOCATION, &glDesc); /* TODO: It should only be possible to create textures for formats that are reported as supported */ @@ -1017,6 +1022,7 @@ static HRESULT WINAPI IWineD3DDeviceImpl_CreateVolumeTexture(IWineD3DDevice *ifa tmpH = max(1, tmpH >> 1); tmpD = max(1, tmpD >> 1); } + object->baseTexture.shader_conversion_group = glDesc->conversion_group; *ppVolumeTexture = (IWineD3DVolumeTexture *) object; TRACE("(%p) : Created volume texture %p\n", This, object); @@ -1066,6 +1072,8 @@ static HRESULT WINAPI IWineD3DDeviceImpl_CreateCubeTexture(IWineD3DDevice *iface UINT tmpW; HRESULT hr; unsigned int pow2EdgeLength = EdgeLength; + const GlPixelFormatDesc *glDesc; + getFormatDescEntry(Format, &GLINFO_LOCATION, &glDesc); /* TODO: It should only be possible to create textures for formats that are reported as supported */ @@ -1149,6 +1157,7 @@ static HRESULT WINAPI IWineD3DDeviceImpl_CreateCubeTexture(IWineD3DDevice *iface } tmpW = max(1, tmpW >> 1); } + object->baseTexture.shader_conversion_group = glDesc->conversion_group; TRACE("(%p) : Created Cube Texture %p\n", This, object); *ppCubeTexture = (IWineD3DCubeTexture *) object; diff --git a/dlls/wined3d/glsl_shader.c b/dlls/wined3d/glsl_shader.c index be30b2d59b6..2c6438c83f4 100644 --- a/dlls/wined3d/glsl_shader.c +++ b/dlls/wined3d/glsl_shader.c @@ -976,14 +976,34 @@ static void shader_glsl_add_color_correction(SHADER_OPCODE_ARG* arg, DWORD sampl glsl_dst_param_t dst_param; glsl_dst_param_t dst_param2; WINED3DFORMAT fmt; + WINED3DFORMAT conversion_group; IWineD3DBaseTextureImpl *texture; DWORD mask, mask_size; + UINT i; + BOOL recorded = FALSE; texture = (IWineD3DBaseTextureImpl *) deviceImpl->stateBlock->textures[sampler_idx]; if(texture) { fmt = texture->resource.format; + conversion_group = texture->baseTexture.shader_conversion_group; } else { fmt = WINED3DFMT_UNKNOWN; + conversion_group = WINED3DFMT_UNKNOWN; + } + + /* before doing anything, record the sampler with the format in the format conversion list, + * but check if it's not there already + */ + for(i = 0; i < shader->baseShader.num_sampled_samplers; i++) { + if(shader->baseShader.sampled_samplers[i] == sampler_idx) { + recorded = TRUE; + break; + } + } + if(!recorded) { + shader->baseShader.sampled_samplers[shader->baseShader.num_sampled_samplers] = sampler_idx; + shader->baseShader.num_sampled_samplers++; + shader->baseShader.sampled_format[sampler_idx] = conversion_group; } switch(fmt) { diff --git a/dlls/wined3d/pixelshader.c b/dlls/wined3d/pixelshader.c index 0bcbdce55a5..07f273e2fce 100644 --- a/dlls/wined3d/pixelshader.c +++ b/dlls/wined3d/pixelshader.c @@ -67,6 +67,22 @@ static ULONG WINAPI IWineD3DPixelShaderImpl_AddRef(IWineD3DPixelShader *iface) return InterlockedIncrement(&This->ref); } +static void destroy_glsl_pshader(IWineD3DPixelShaderImpl *This) { + struct list *linked_programs = &This->baseShader.linked_programs; + + TRACE("Deleting linked programs\n"); + if (linked_programs->next) { + struct glsl_shader_prog_link *entry, *entry2; + LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, linked_programs, struct glsl_shader_prog_link, pshader_entry) { + delete_glsl_program_entry(This->baseShader.device, entry); + } + } + + TRACE("Deleting shader object %u\n", This->baseShader.prgId); + GL_EXTCALL(glDeleteObjectARB(This->baseShader.prgId)); + checkGLcall("glDeleteObjectARB"); +} + static ULONG WINAPI IWineD3DPixelShaderImpl_Release(IWineD3DPixelShader *iface) { IWineD3DPixelShaderImpl *This = (IWineD3DPixelShaderImpl *)iface; ULONG ref; @@ -74,19 +90,7 @@ static ULONG WINAPI IWineD3DPixelShaderImpl_Release(IWineD3DPixelShader *iface) ref = InterlockedDecrement(&This->ref); if (ref == 0) { if (This->baseShader.shader_mode == SHADER_GLSL && This->baseShader.prgId != 0) { - struct list *linked_programs = &This->baseShader.linked_programs; - - TRACE("Deleting linked programs\n"); - if (linked_programs->next) { - struct glsl_shader_prog_link *entry, *entry2; - LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, linked_programs, struct glsl_shader_prog_link, pshader_entry) { - delete_glsl_program_entry(This->baseShader.device, entry); - } - } - - TRACE("Deleting shader object %u\n", This->baseShader.prgId); - GL_EXTCALL(glDeleteObjectARB(This->baseShader.prgId)); - checkGLcall("glDeleteObjectARB"); + destroy_glsl_pshader(This); } shader_delete_constant_list(&This->baseShader.constantsF); shader_delete_constant_list(&This->baseShader.constantsB); @@ -523,11 +527,43 @@ static HRESULT WINAPI IWineD3DPixelShaderImpl_CompileShader(IWineD3DPixelShader IWineD3DPixelShaderImpl *This =(IWineD3DPixelShaderImpl *)iface; IWineD3DDeviceImpl *deviceImpl = (IWineD3DDeviceImpl*) This->baseShader.device; CONST DWORD *function = This->baseShader.function; + UINT i, sampler; + IWineD3DBaseTextureImpl *texture; TRACE("(%p) : function %p\n", iface, function); - /* We're already compiled. */ - if (This->baseShader.is_compiled) return WINED3D_OK; + /* We're already compiled, but check if any of the hardcoded stateblock assumptions + * changed. + */ + if (This->baseShader.is_compiled) { + for(i = 0; i < This->baseShader.num_sampled_samplers; i++) { + sampler = This->baseShader.sampled_samplers[i]; + texture = (IWineD3DBaseTextureImpl *) deviceImpl->stateBlock->textures[sampler]; + if(texture && texture->baseTexture.shader_conversion_group != This->baseShader.sampled_format[sampler]) { + WARN("Recompiling shader %p due to format change on sampler %d\n", This, sampler); + WARN("Old format group %s, new is %s\n", + debug_d3dformat(This->baseShader.sampled_format[sampler]), + debug_d3dformat(texture->baseTexture.shader_conversion_group)); + goto recompile; + } + } + + /* TODO: Check projected textures */ + /* TODO: Check texture types(2D, Cube, 3D) */ + + return WINED3D_OK; + + recompile: + if(This->baseShader.recompile_count > 50) { + FIXME("Shader %p recompiled more than 50 times\n", This); + } else { + This->baseShader.recompile_count++; + } + + if (This->baseShader.shader_mode == SHADER_GLSL && This->baseShader.prgId != 0) { + destroy_glsl_pshader(This); + } + } /* We don't need to compile */ if (!function) { @@ -547,6 +583,9 @@ static HRESULT WINAPI IWineD3DPixelShaderImpl_CompileShader(IWineD3DPixelShader /* FIXME: validate reg_maps against OpenGL */ } + /* Reset fields tracking stateblock values beeing hardcoded in the shader */ + This->baseShader.num_sampled_samplers = 0; + /* Generate the HW shader */ TRACE("(%p) : Generating hardware program\n", This); IWineD3DPixelShaderImpl_GenerateShader(iface, &This->baseShader.reg_maps, function); diff --git a/dlls/wined3d/utils.c b/dlls/wined3d/utils.c index d5131e17b2a..4765ae5ee60 100644 --- a/dlls/wined3d/utils.c +++ b/dlls/wined3d/utils.c @@ -27,8 +27,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d); -#define GLINFO_LOCATION This->adapter->gl_info - /***************************************************************************** * Pixel format array */ @@ -210,9 +208,11 @@ static inline int getFmtIdx(WINED3DFORMAT fmt) { return -1; } +#define GLINFO_LOCATION (*gl_info) BOOL initPixelFormats(WineD3D_GL_Info *gl_info) { unsigned int src; + int dst; gl_info->gl_formats = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(formats) / sizeof(formats[0]) * sizeof(gl_info->gl_formats[0])); @@ -222,15 +222,51 @@ BOOL initPixelFormats(WineD3D_GL_Info *gl_info) * after this loop */ for(src = 0; src < sizeof(gl_formats_template) / sizeof(gl_formats_template[0]); src++) { - int dst = getFmtIdx(gl_formats_template[src].fmt); + dst = getFmtIdx(gl_formats_template[src].fmt); gl_info->gl_formats[dst].glInternal = gl_formats_template[src].glInternal; gl_info->gl_formats[dst].glGammaInternal = gl_formats_template[src].glGammaInternal; gl_info->gl_formats[dst].glFormat = gl_formats_template[src].glFormat; gl_info->gl_formats[dst].glType = gl_formats_template[src].glType; + gl_info->gl_formats[dst].conversion_group= WINED3DFMT_UNKNOWN; + } + + /* V8U8 and V16U16 are always tidied up in the pixel shader - blue is set to 1.0. + * They can't be switched with other formats, but they can be switched with each other, + * except if GL_ATI_envmap_bumpmap is supported. In this case, V8U8 uses the gl native format, + * but V16U16 is converted. + */ + dst = getFmtIdx(WINED3DFMT_V8U8); + gl_info->gl_formats[dst].conversion_group = WINED3DFMT_V8U8; + dst = getFmtIdx(WINED3DFMT_V16U16); + if(!GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) { + gl_info->gl_formats[dst].conversion_group = WINED3DFMT_V8U8; + } else { + gl_info->gl_formats[dst].conversion_group = WINED3DFMT_V16U16; + } + + if(!GL_SUPPORT(NV_TEXTURE_SHADER)) { + /* If GL_NV_texture_shader is not supported, those formats are converted, incompatibly + * with each other + */ + dst = getFmtIdx(WINED3DFMT_L6V5U5); + gl_info->gl_formats[dst].conversion_group = WINED3DFMT_L6V5U5; + dst = getFmtIdx(WINED3DFMT_X8L8V8U8); + gl_info->gl_formats[dst].conversion_group = WINED3DFMT_X8L8V8U8; + dst = getFmtIdx(WINED3DFMT_Q8W8V8U8); + gl_info->gl_formats[dst].conversion_group = WINED3DFMT_Q8W8V8U8; + } else { + /* If GL_NV_texture_shader is supported, WINED3DFMT_L6V5U5 and WINED3DFMT_X8L8V8U8 + * are converted at surface loading time, but they do not need any modification in + * the shader, thus they are compatible with all WINED3DFMT_UNKNOWN group formats. + * WINED3DFMT_Q8W8V8U8 doesn't even need load-time conversion + */ } return TRUE; } +#undef GLINFO_LOCATION + +#define GLINFO_LOCATION This->adapter->gl_info const StaticPixelFormatDesc *getFormatDescEntry(WINED3DFORMAT fmt, WineD3D_GL_Info *gl_info, const GlPixelFormatDesc **glDesc) { diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index b7e08fc4b35..eb52744ae75 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -932,6 +932,7 @@ typedef struct IWineD3DBaseTextureClass DWORD sampler; BOOL is_srgb; UINT srgb_mode_change_count; + WINED3DFORMAT shader_conversion_group; } IWineD3DBaseTextureClass; typedef struct IWineD3DBaseTextureImpl @@ -1898,6 +1899,17 @@ typedef struct IWineD3DBaseShaderClass struct list constantsI; shader_reg_maps reg_maps; + /* Pixel formats of sampled textures, for format conversion. This + * represents the formats found during compilation, it is not initialized + * on the first parser pass. It is needed to check if the shader + * needs recompilation to adjust the format conversion + */ + WINED3DFORMAT sampled_format[MAX_COMBINED_SAMPLERS]; + UINT sampled_samplers[MAX_COMBINED_SAMPLERS]; + UINT num_sampled_samplers; + + UINT recompile_count; + /* Pointer to the parent device */ IWineD3DDevice *device; diff --git a/include/wine/wined3d_gl.h b/include/wine/wined3d_gl.h index 1a04dd72b8e..f2d9dabcea7 100644 --- a/include/wine/wined3d_gl.h +++ b/include/wine/wined3d_gl.h @@ -3691,6 +3691,7 @@ typedef BOOL (WINAPI * WINED3D_PFNWGLQUERYPBUFFERARBPROC) (HPBUFFERARB hPbuffer, typedef struct { GLint glInternal, glGammaInternal, glFormat, glType; + WINED3DFORMAT conversion_group; } GlPixelFormatDesc; #define USE_GL_FUNC(type, pfn) type pfn;