wined3d: SRGB write correction emulation.
diff --git a/dlls/wined3d/arb_program_shader.c b/dlls/wined3d/arb_program_shader.c
index 70017d0..2a006eb 100644
--- a/dlls/wined3d/arb_program_shader.c
+++ b/dlls/wined3d/arb_program_shader.c
@@ -154,6 +154,7 @@
     if (usePixelShader) {
 
         IWineD3DBaseShaderImpl* pshader = (IWineD3DBaseShaderImpl*) stateBlock->pixelShader;
+        IWineD3DPixelShaderImpl *psi = (IWineD3DPixelShaderImpl *) pshader;
 
         /* Load DirectX 9 float constants for pixel shader */
         shader_arb_load_constantsF(pshader, gl_info, GL_FRAGMENT_PROGRAM_ARB, 
@@ -165,10 +166,31 @@
              * number of the constant to load the matrix into.
              * The state manager takes care that this function is always called if the bump env matrix changes
              */
-            IWineD3DPixelShaderImpl *psi = (IWineD3DPixelShaderImpl *) pshader;
             float *data = (float *) &stateBlock->textureState[(int) psi->needsbumpmat][WINED3DTSS_BUMPENVMAT00];
             GL_EXTCALL(glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, psi->bumpenvmatconst, data));
         }
+        if(((IWineD3DPixelShaderImpl *) pshader)->srgb_enabled &&
+           !((IWineD3DPixelShaderImpl *) pshader)->srgb_mode_hardcoded) {
+            float comparison[4];
+            float mul_low[4];
+
+            if(stateBlock->renderState[WINED3DRS_SRGBWRITEENABLE]) {
+                comparison[0] = srgb_cmp; comparison[1] = srgb_cmp;
+                comparison[2] = srgb_cmp; comparison[3] = srgb_cmp;
+
+                mul_low[0] = srgb_mul_low; mul_low[1] = srgb_mul_low;
+                mul_low[2] = srgb_mul_low; mul_low[3] = srgb_mul_low;
+            } else {
+                comparison[0] = 1.0 / 0.0; comparison[1] = 1.0 / 0.0;
+                comparison[2] = 1.0 / 0.0; comparison[3] = 1.0 / 0.0;
+
+                mul_low[0] = 1.0; mul_low[1] = 1.0;
+                mul_low[2] = 1.0; mul_low[3] = 1.0;
+            }
+            GL_EXTCALL(glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, psi->srgb_cmp_const, comparison));
+            GL_EXTCALL(glProgramEnvParameter4fvARB(GL_FRAGMENT_PROGRAM_ARB, psi->srgb_low_const, mul_low));
+            checkGLcall("Load sRGB correction constants\n");
+        }
     }
 }
 
@@ -180,10 +202,12 @@
     WineD3D_GL_Info* gl_info) {
 
     IWineD3DBaseShaderImpl* This = (IWineD3DBaseShaderImpl*) iface;
+    IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) This->baseShader.device;
     DWORD i;
     char pshader = shader_is_pshader_version(This->baseShader.hex_version);
     unsigned max_constantsF = min(This->baseShader.limits.constant_float, 
             (pshader ? GL_LIMITS(pshader_constantsF) : GL_LIMITS(vshader_constantsF)));
+    UINT extra_constants_needed = 0;
 
     /* Temporary Output register */
     shader_addline(buffer, "TEMP TMP_OUT;\n");
@@ -220,6 +244,52 @@
         } else {
             FIXME("No free constant found to load environemnt bump mapping matrix into the shader. texbem instruction will not apply bump mapping\n");
         }
+        extra_constants_needed += 1;
+    }
+    if(device->stateBlock->renderState[WINED3DRS_SRGBWRITEENABLE] && pshader) {
+        IWineD3DPixelShaderImpl *ps_impl = (IWineD3DPixelShaderImpl *) This;
+        /* If there are 2 constants left to use, use them to pass the sRGB correction values in. This way
+         * srgb write correction can be turned on and off dynamically without recompilation. Otherwise
+         * hardcode them. The drawback of hardcoding is that the shader needs recompilation to turn sRGB
+         * off again
+         */
+        if(max_constantsF + extra_constants_needed + 1 < GL_LIMITS(pshader_constantsF) && FALSE) {
+            /* The idea is that if srgb is enabled, then disabled, the constant loading code
+             * can effectively disabling sRGB correction by passing 1.0 and INF as the multiplication
+             * and comparison constants. If it disables it that way, the shader won't be recompiled
+             * and the code will stay in, so sRGB writing can be turned on again by setting the
+             * constants from the spec
+             */
+            ps_impl->srgb_mode_hardcoded = 0;
+            ps_impl->srgb_low_const = GL_LIMITS(pshader_constantsF) - extra_constants_needed;
+            ps_impl->srgb_cmp_const = GL_LIMITS(pshader_constantsF) - extra_constants_needed - 1;
+            shader_addline(buffer, "PARAM srgb_mul_low = program.env[%d];\n", ps_impl->srgb_low_const);
+            shader_addline(buffer, "PARAM srgb_comparison = program.env[%d];\n", ps_impl->srgb_cmp_const);
+        } else {
+            shader_addline(buffer, "PARAM srgb_mul_low = {%f, %f, %f, 1.0};\n",
+                           srgb_mul_low, srgb_mul_low, srgb_mul_low);
+            shader_addline(buffer, "PARAM srgb_comparison =  {%f, %f, %f, %f};\n",
+                           srgb_cmp, srgb_cmp, srgb_cmp, srgb_cmp);
+            ps_impl->srgb_mode_hardcoded = 1;
+        }
+        /* These can be hardcoded, they do not cause any harm because no fragment will enter the high
+         * path if the comparison value is set to INF
+         */
+        shader_addline(buffer, "PARAM srgb_pow =  {%f, %f, %f, 1.0};\n",
+                       srgb_pow, srgb_pow, srgb_pow);
+        shader_addline(buffer, "PARAM srgb_mul_hi =  {%f, %f, %f, 1.0};\n",
+                       srgb_mul_high, srgb_mul_high, srgb_mul_high);
+        shader_addline(buffer, "PARAM srgb_sub_hi =  {%f, %f, %f, 0.0};\n",
+                       srgb_sub_high, srgb_sub_high, srgb_sub_high);
+        ps_impl->srgb_enabled = 1;
+    } else if(pshader) {
+        IWineD3DPixelShaderImpl *ps_impl = (IWineD3DPixelShaderImpl *) This;
+
+        /* Do not write any srgb fixup into the shader to save shader size and processing time.
+         * As a consequence, we can't toggle srgb write on without recompilation
+         */
+        ps_impl->srgb_enabled = 0;
+        ps_impl->srgb_mode_hardcoded = 1;
     }
 
     /* Need to PARAM the environment parameters (constants) so we can use relative addressing */
diff --git a/dlls/wined3d/glsl_shader.c b/dlls/wined3d/glsl_shader.c
index 3fbbace..57685a3 100644
--- a/dlls/wined3d/glsl_shader.c
+++ b/dlls/wined3d/glsl_shader.c
@@ -443,6 +443,31 @@
                 GL_EXTCALL(glUniform1fvARB(pos, 1, offset));
                 checkGLcall("glUniform1fvARB");
             }
+        } else if(((IWineD3DPixelShaderImpl *) pshader)->srgb_enabled &&
+                  !((IWineD3DPixelShaderImpl *) pshader)->srgb_mode_hardcoded) {
+            float comparison[4];
+            float mul_low[4];
+
+            if(stateBlock->renderState[WINED3DRS_SRGBWRITEENABLE]) {
+                comparison[0] = srgb_cmp; comparison[1] = srgb_cmp;
+                comparison[2] = srgb_cmp; comparison[3] = srgb_cmp;
+
+                mul_low[0] = srgb_mul_low; mul_low[1] = srgb_mul_low;
+                mul_low[2] = srgb_mul_low; mul_low[3] = srgb_mul_low;
+            } else {
+                comparison[0] = 1.0 / 0.0; comparison[1] = 1.0 / 0.0;
+                comparison[2] = 1.0 / 0.0; comparison[3] = 1.0 / 0.0;
+
+                mul_low[0] = 1.0; mul_low[1] = 1.0;
+                mul_low[2] = 1.0; mul_low[3] = 1.0;
+            }
+
+            pos = GL_EXTCALL(glGetUniformLocationARB(programId, "srgb_comparison"));
+            checkGLcall("glGetUniformLocationARB");
+            GL_EXTCALL(glUniform4fvARB(pos, 1, comparison));
+            pos = GL_EXTCALL(glGetUniformLocationARB(programId, "srgb_mul_low"));
+            checkGLcall("glGetUniformLocationARB");
+            GL_EXTCALL(glUniform4fvARB(pos, 1, mul_low));
         }
     }
 }
@@ -455,7 +480,9 @@
     WineD3D_GL_Info* gl_info) {
 
     IWineD3DBaseShaderImpl* This = (IWineD3DBaseShaderImpl*) iface;
+    IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) This->baseShader.device;
     int i;
+    unsigned int extra_constants_needed = 0;
 
     /* There are some minor differences between pixel and vertex shaders */
     char pshader = shader_is_pshader_version(This->baseShader.hex_version);
@@ -480,13 +507,42 @@
     if (This->baseShader.limits.constant_bool > 0)
         shader_addline(buffer, "uniform bool %cB[%u];\n", prefix, This->baseShader.limits.constant_bool);
 
-    if(!pshader)
+    if(!pshader) {
         shader_addline(buffer, "uniform vec4 posFixup;\n");
-    else if(reg_maps->bumpmat != -1) {
-        shader_addline(buffer, "uniform mat2 bumpenvmat;\n");
-        if(reg_maps->luminanceparams) {
-            shader_addline(buffer, "uniform float luminancescale;\n");
-            shader_addline(buffer, "uniform float luminanceoffset;\n");
+    } else {
+        IWineD3DPixelShaderImpl *ps_impl = (IWineD3DPixelShaderImpl *) This;
+
+        if(reg_maps->bumpmat != -1) {
+            shader_addline(buffer, "uniform mat2 bumpenvmat;\n");
+            if(reg_maps->luminanceparams) {
+                shader_addline(buffer, "uniform float luminancescale;\n");
+                shader_addline(buffer, "uniform float luminanceoffset;\n");
+                extra_constants_needed++;
+            }
+            extra_constants_needed++;
+        }
+
+        if(device->stateBlock->renderState[WINED3DRS_SRGBWRITEENABLE]) {
+            ps_impl->srgb_enabled = 1;
+            if(This->baseShader.limits.constant_float + extra_constants_needed + 1 < GL_LIMITS(pshader_constantsF)) {
+                shader_addline(buffer, "uniform vec4 srgb_mul_low;\n");
+                shader_addline(buffer, "uniform vec4 srgb_comparison;\n");
+                ps_impl->srgb_mode_hardcoded = 0;
+            } else {
+                ps_impl->srgb_mode_hardcoded = 1;
+                shader_addline(buffer, "const vec4 srgb_mul_low = {%f, %f, %f, %f};\n",
+                               srgb_mul_low, srgb_mul_low, srgb_mul_low, srgb_mul_low);
+                shader_addline(buffer, "const vec4 srgb_comparison = {%f, %f, %f, %f};\n",
+                               srgb_cmp, srgb_cmp, srgb_cmp, srgb_cmp);
+            }
+        } else {
+            IWineD3DPixelShaderImpl *ps_impl = (IWineD3DPixelShaderImpl *) This;
+
+            /* Do not write any srgb fixup into the shader to save shader size and processing time.
+             * As a consequence, we can't toggle srgb write on without recompilation
+             */
+            ps_impl->srgb_enabled = 0;
+            ps_impl->srgb_mode_hardcoded = 1;
         }
     }
 
diff --git a/dlls/wined3d/pixelshader.c b/dlls/wined3d/pixelshader.c
index 07f273e..02704d1 100644
--- a/dlls/wined3d/pixelshader.c
+++ b/dlls/wined3d/pixelshader.c
@@ -399,6 +399,23 @@
             else
                 shader_addline(&buffer, "gl_FragColor.xyz = mix(gl_Fog.color.xyz, gl_FragColor.xyz, Fog);\n");
         }
+        if(This->srgb_enabled) {
+            const char *fragcolor;
+
+            if(GL_SUPPORT(ARB_DRAW_BUFFERS)) {
+                fragcolor = "gl_FragData[0]";
+            } else {
+                fragcolor = "gl_FragColor";
+            }
+            shader_addline(&buffer, "tmp0.xyz = pow(%s.xyz, vec3(%f, %f, %f)) * vec3(%f, %f, %f) - vec3(%f, %f, %f);\n",
+                            fragcolor, srgb_pow, srgb_pow, srgb_pow, srgb_mul_high, srgb_mul_high, srgb_mul_high,
+                            srgb_sub_high, srgb_sub_high, srgb_sub_high);
+            shader_addline(&buffer, "tmp1.xyz = %s.xyz * srgb_mul_low.xyz;\n", fragcolor);
+            shader_addline(&buffer, "%s.x = %s.x < srgb_comparison.x ? tmp1.x : tmp0.x;\n", fragcolor, fragcolor);
+            shader_addline(&buffer, "%s.y = %s.y < srgb_comparison.y ? tmp1.y : tmp0.y;\n", fragcolor, fragcolor);
+            shader_addline(&buffer, "%s.z = %s.z < srgb_comparison.z ? tmp1.z : tmp0.z;\n", fragcolor, fragcolor);
+            shader_addline(&buffer, "%s = clamp(%s, 0.0, 1.0);\n", fragcolor, fragcolor);
+        }
 
         shader_addline(&buffer, "}\n");
 
@@ -440,12 +457,41 @@
          * -1/(e-s) and e/(e-s) respectively.
          */
         shader_addline(&buffer, "MAD_SAT TMP_FOG, fragment.fogcoord, state.fog.params.y, state.fog.params.z;\n");
-        if (This->baseShader.hex_version < WINED3DPS_VERSION(2,0)) {
-            shader_addline(&buffer, "LRP result.color.rgb, TMP_FOG.x, R0, state.fog.color;\n");
-            shader_addline(&buffer, "MOV result.color.a, R0.a;\n");
+
+        if(This->srgb_enabled) {
+            if (This->baseShader.hex_version < WINED3DPS_VERSION(2,0)) {
+                shader_addline(&buffer, "LRP TMP_COLOR.rgb, TMP_FOG.x, R0, state.fog.color;\n");
+                shader_addline(&buffer, "MOV result.color.a, R0.a;\n");
+            } else {
+                shader_addline(&buffer, "LRP TMP_COLOR.rgb, TMP_FOG.x, TMP_COLOR, state.fog.color;\n");
+                shader_addline(&buffer, "MOV result.color.a, TMP_COLOR.a;\n");
+            }
+            /* Perform sRGB write correction. See GLX_EXT_framebuffer_sRGB */
+
+            /* Calculate the > 0.0031308 case */
+            shader_addline(&buffer, "POW TMP.x, TMP_COLOR.x, srgb_pow.x;\n");
+            shader_addline(&buffer, "POW TMP.y, TMP_COLOR.y, srgb_pow.y;\n");
+            shader_addline(&buffer, "POW TMP.z, TMP_COLOR.z, srgb_pow.z;\n");
+            shader_addline(&buffer, "MUL TMP, TMP, srgb_mul_hi;\n");
+            shader_addline(&buffer, "SUB TMP, TMP, srgb_sub_hi;\n");
+            /* Calculate the < case */
+            shader_addline(&buffer, "MUL TMP2, srgb_mul_low, TMP_COLOR;\n");
+            /* Get 1.0 / 0.0 masks for > 0.0031308 and < 0.0031308 */
+            shader_addline(&buffer, "SLT TA, srgb_comparison, TMP_COLOR;\n");
+            shader_addline(&buffer, "SGE TB, srgb_comparison, TMP_COLOR;\n");
+            /* Store the components > 0.0031308 in the destination */
+            shader_addline(&buffer, "MUL TMP_COLOR, TMP, TA;\n");
+            /* Add the components that are < 0.0031308 */
+            shader_addline(&buffer, "MAD result.color.xyz, TMP2, TB, TMP_COLOR;\n");
+            /* [0.0;1.0] clamping. Not needed, this is done implicitly */
         } else {
-            shader_addline(&buffer, "LRP result.color.rgb, TMP_FOG.x, TMP_COLOR, state.fog.color;\n");
-            shader_addline(&buffer, "MOV result.color.a, TMP_COLOR.a;\n");
+            if (This->baseShader.hex_version < WINED3DPS_VERSION(2,0)) {
+                shader_addline(&buffer, "LRP result.color.rgb, TMP_FOG.x, R0, state.fog.color;\n");
+                shader_addline(&buffer, "MOV result.color.a, R0.a;\n");
+            } else {
+                shader_addline(&buffer, "LRP result.color.rgb, TMP_FOG.x, TMP_COLOR, state.fog.color;\n");
+                shader_addline(&buffer, "MOV result.color.a, TMP_COLOR.a;\n");
+            }
         }
 
         shader_addline(&buffer, "END\n"); 
@@ -536,6 +582,7 @@
      * changed.
      */
     if (This->baseShader.is_compiled) {
+        char srgbenabled = deviceImpl->stateBlock->renderState[WINED3DRS_SRGBWRITEENABLE] ? 1 : 0;
         for(i = 0; i < This->baseShader.num_sampled_samplers; i++) {
             sampler = This->baseShader.sampled_samplers[i];
             texture = (IWineD3DBaseTextureImpl *) deviceImpl->stateBlock->textures[sampler];
@@ -551,6 +598,11 @@
         /* TODO: Check projected textures */
         /* TODO: Check texture types(2D, Cube, 3D) */
 
+        if(srgbenabled != This->srgb_enabled && This->srgb_mode_hardcoded) {
+            WARN("Recompiling shader because srgb correction is different and hardcoded\n");
+            goto recompile;
+        }
+
         return WINED3D_OK;
 
         recompile:
diff --git a/dlls/wined3d/state.c b/dlls/wined3d/state.c
index fbd95ed..929260b 100644
--- a/dlls/wined3d/state.c
+++ b/dlls/wined3d/state.c
@@ -1559,12 +1559,6 @@
         FIXME("(WINED3DRS_ENABLEADAPTIVETESSELLATION,%d) not yet implemented\n", stateblock->renderState[WINED3DRS_ENABLEADAPTIVETESSELLATION]);
 }
 
-
-static void state_srgbwrite(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DContext *context) {
-    if(stateblock->renderState[WINED3DRS_SRGBWRITEENABLE])
-        FIXME("Render state WINED3DRS_SRGBWRITEENABLE not yet implemented\n");
-}
-
 static void state_separateblend(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DContext *context) {
     TRACE("Stub\n");
     if(stateblock->renderState[WINED3DRS_SEPARATEALPHABLENDENABLE])
@@ -3827,7 +3821,7 @@
     { /*191, WINED3DRS_COLORWRITEENABLE2            */      STATE_RENDER(WINED3DRS_COLORWRITEENABLE),           state_colorwrite    },
     { /*192, WINED3DRS_COLORWRITEENABLE3            */      STATE_RENDER(WINED3DRS_COLORWRITEENABLE),           state_colorwrite    },
     { /*193, WINED3DRS_BLENDFACTOR                  */      STATE_RENDER(WINED3DRS_BLENDFACTOR),                state_blendfactor   },
-    { /*194, WINED3DRS_SRGBWRITEENABLE              */      STATE_RENDER(WINED3DRS_SRGBWRITEENABLE),            state_srgbwrite     },
+    { /*194, WINED3DRS_SRGBWRITEENABLE              */      STATE_PIXELSHADER,                                  pixelshader         },
     { /*195, WINED3DRS_DEPTHBIAS                    */      STATE_RENDER(WINED3DRS_DEPTHBIAS),                  state_depthbias     },
     { /*196, undefined                              */      0,                                                  state_undefined     },
     { /*197, undefined                              */      0,                                                  state_undefined     },
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 9d261ef..286a61a 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -2081,6 +2081,10 @@
     /* Some information about the shader behavior */
     char                        needsbumpmat;
     UINT                        bumpenvmatconst;
+    char                        srgb_enabled;
+    char                        srgb_mode_hardcoded;
+    UINT                        srgb_low_const;
+    UINT                        srgb_cmp_const;
 
 #if 0 /* needs reworking */
     PSHADERINPUTDATA input;
@@ -2091,6 +2095,13 @@
 extern const SHADER_OPCODE IWineD3DPixelShaderImpl_shader_ins[];
 extern const IWineD3DPixelShaderVtbl IWineD3DPixelShader_Vtbl;
 
+/* sRGB correction constants */
+static const float srgb_cmp = 0.0031308;
+static const float srgb_mul_low = 12.92;
+static const float srgb_pow = 0.41666;
+static const float srgb_mul_high = 1.055;
+static const float srgb_sub_high = 0.055;
+
 /*****************************************************************************
  * IWineD3DPalette implementation structure
  */