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
*/