wined3d: SRGB write correction emulation.

This commit is contained in:
Stefan Dösinger 2007-09-14 13:02:59 +02:00 committed by Alexandre Julliard
parent 2b2c9199e8
commit 6313e0ffff
5 changed files with 202 additions and 19 deletions

View File

@ -154,6 +154,7 @@ void shader_arb_load_constants(
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 @@ void shader_arb_load_constants(
* 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 @@ void shader_generate_arb_declarations(
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 @@ void shader_generate_arb_declarations(
} 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 */

View File

@ -443,6 +443,31 @@ void shader_glsl_load_constants(
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 @@ void shader_generate_glsl_declarations(
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 @@ void shader_generate_glsl_declarations(
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;
}
}

View File

@ -399,6 +399,23 @@ static inline VOID IWineD3DPixelShaderImpl_GenerateShader(
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 @@ static inline VOID IWineD3DPixelShaderImpl_GenerateShader(
* -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 @@ static HRESULT WINAPI IWineD3DPixelShaderImpl_CompileShader(IWineD3DPixelShader
* 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 @@ static HRESULT WINAPI IWineD3DPixelShaderImpl_CompileShader(IWineD3DPixelShader
/* 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:

View File

@ -1559,12 +1559,6 @@ static void state_tessellation(DWORD state, IWineD3DStateBlockImpl *stateblock,
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 @@ const struct StateEntry StateTable[] =
{ /*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 },

View File

@ -2081,6 +2081,10 @@ typedef struct IWineD3DPixelShaderImpl {
/* 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 @@ typedef struct IWineD3DPixelShaderImpl {
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
*/