Hi.
I found some problems with D3DRENDERSTATE_TEXTUREMAPBLEND state = D3DTBLEND_MODULATE in ddraw and handling the alpha channel. This old state is currently mapped to texture stage states, with alpha part done as
IWineD3DDevice_SetTextureStageState(This->wineD3DDevice, 0, WINED3DTSS_ALPHAARG1, WINED3DTA_TEXTURE); IWineD3DDevice_SetTextureStageState(This->wineD3DDevice, 0, WINED3DTSS_ALPHAOP, WINED3DTOP_SELECTARG1);
This isn't correct, I believe, because in some cases it should be taken from diffuse color (Aliens vs Predator 1 depends on it). But the latter behavior isn't correct in all cases either.
I found this description of this state on some random website via google:
"D3DTBLEND_MODULATE Modulate texture-blending is supported. In this mode, the RGB values of the texture are multiplied with the RGB values that would have been used with no texturing. Any alpha values in the texture replace the alpha values that would have been used with no texturing."
http://www.hi.is/~snorri/SDK-docs/x/exten203.htm
I guess, this means that it either takes alpha from texture or from diffuse color, depending on whether the current texture has alpha channel (which takes priority). I've submitted a test for this to wine-patches that includes checks with both kinds of textures. It passes on XP and thus supports that this is the way it is supposed to happen. Unfortunately, this doesn't easily translate to available texture stage states. So it looks like something more hacky is needed to make it work correctly in all cases in wine. There are two approaches I can think of:
1) introduce an internal D3DTOP_ value in wined3d that will map to texture alpha or diffuse alpha, depending on the pixel format of the currently selected texture.
2) move D3DRENDERSTATE_TEXTUREMAPBLEND handling to wined3d;
So it would be nice if Stefan Dösinger or maybe somebody else of d3d devs gave me some directions - which approach (if any) is ok and acceptable for wine project. I'll attach a diff with my current hacks that show how 1st approach is about to look.
BTW, this worked in wine around 0.9.15
http://source.winehq.org/git/wine.git/?a=blob;f=dlls/ddraw/opengl_utils.c;h=...
There it wasn't mapping to texture stage states, but instead was using such call:
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
From what I read about GL_MODULATE, I'm not sure it always does exactly the thing TEXTUREMAPBLEND=MODULATE is supposed to do, but with Aliens vs Predator it worked better.
diff --git a/dlls/ddraw/device.c b/dlls/ddraw/device.c index 135d6e9..e2cd23a 100644 --- a/dlls/ddraw/device.c +++ b/dlls/ddraw/device.c @@ -2534,10 +2534,9 @@ IDirect3DDeviceImpl_7_SetRenderState(IDirect3DDevice7 *iface, { case D3DTBLEND_MODULATE: IWineD3DDevice_SetTextureStageState(This->wineD3DDevice, 0, WINED3DTSS_COLORARG1, WINED3DTA_TEXTURE); - IWineD3DDevice_SetTextureStageState(This->wineD3DDevice, 0, WINED3DTSS_ALPHAARG1, WINED3DTA_TEXTURE); IWineD3DDevice_SetTextureStageState(This->wineD3DDevice, 0, WINED3DTSS_COLORARG2, WINED3DTA_CURRENT); IWineD3DDevice_SetTextureStageState(This->wineD3DDevice, 0, WINED3DTSS_COLOROP, WINED3DTOP_MODULATE); - IWineD3DDevice_SetTextureStageState(This->wineD3DDevice, 0, WINED3DTSS_ALPHAOP, WINED3DTOP_SELECTARG1); + IWineD3DDevice_SetTextureStageState(This->wineD3DDevice, 0, WINED3DTSS_ALPHAOP, WINED3DTOP_DX6MODULATE); break;
case D3DTBLEND_MODULATEALPHA: diff --git a/dlls/wined3d/state.c b/dlls/wined3d/state.c index 59ff3d4..9f11f73 100644 --- a/dlls/wined3d/state.c +++ b/dlls/wined3d/state.c @@ -351,6 +351,12 @@ static void state_blend(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3D TRACE("glBlendFunc src=%x, dst=%x\n", srcBlend, dstBlend); glBlendFunc(srcBlend, dstBlend); checkGLcall("glBlendFunc"); + + /* colorkey fixup for stage 0 alphaop depends on WINED3DRS_ALPHABLENDENABLE state, + so it may need updating */ + if (stateblock->renderState[WINED3DRS_COLORKEYENABLE]) { + StateTable[STATE_TEXTURESTAGE(0, WINED3DTSS_ALPHAOP)].apply(STATE_TEXTURESTAGE(0, WINED3DTSS_ALPHAOP), stateblock, context); + } }
static void state_blendfactor(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DContext *context) { @@ -1932,6 +1938,24 @@ static void tex_alphaop(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3D arg2 = stateblock->textureState[stage][WINED3DTSS_ALPHAARG2]; arg0 = stateblock->textureState[stage][WINED3DTSS_ALPHAARG0];
+ if (op == WINED3DTOP_DX6MODULATE) { + IWineD3DSurfaceImpl *surf = NULL; + + if (stage > 0) ERR("unexpected WINED3DTOP_DX6MODULATE at stage > 0\n"); + + if (stateblock->textures[0]) + surf = (IWineD3DSurfaceImpl *) ((IWineD3DTextureImpl *) stateblock->textures[0])->surfaces[0]; + + if (surf && getFormatDescEntry(surf->resource.format, NULL, NULL)->alphaMask) { + op = WINED3DTOP_SELECTARG1; + arg1 = WINED3DTA_TEXTURE; + } + else { + op = WINED3DTOP_SELECTARG1; + arg1 = WINED3DTA_CURRENT; + } + } + if(stateblock->renderState[WINED3DRS_COLORKEYENABLE] && stage == 0 && stateblock->textures[0] && (stateblock->textureDimensions[0] == GL_TEXTURE_2D || stateblock->textureDimensions[0] == GL_TEXTURE_RECTANGLE_ARB)) { @@ -1945,13 +1969,29 @@ static void tex_alphaop(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3D * cannot remove the texture's alpha channel entirely. * * The fixup is required for Prince of Persia 3D(prison bars), while Moto racer 2 requires D3DTOP_MODULATE to work - * on color keyed surfaces. + * on color keyed surfaces. Aliens vs Predator 1 uses color keyed textures and alpha component of diffuse color to + * draw things like translucent text and perform other blending effects. * * What to do with multitexturing? So far no app has been found that uses color keying with multitexturing */ - if(op == WINED3DTOP_DISABLE) op = WINED3DTOP_SELECTARG1; - if(op == WINED3DTOP_SELECTARG1) arg1 = WINED3DTA_TEXTURE; - else if(op == WINED3DTOP_SELECTARG2) arg2 = WINED3DTA_TEXTURE; + if(op == WINED3DTOP_DISABLE) { + arg1 = WINED3DTA_TEXTURE; + op = WINED3DTOP_SELECTARG1; + } + else if(op == WINED3DTOP_SELECTARG1 && arg1 != WINED3DTA_TEXTURE) { + if (stateblock->renderState[WINED3DRS_ALPHABLENDENABLE]) { + arg2 = WINED3DTA_TEXTURE; + op = WINED3DTOP_MODULATE; + } + else arg1 = WINED3DTA_TEXTURE; + } + else if(op == WINED3DTOP_SELECTARG2 && arg2 != WINED3DTA_TEXTURE) { + if (stateblock->renderState[WINED3DRS_ALPHABLENDENABLE]) { + arg1 = WINED3DTA_TEXTURE; + op = WINED3DTOP_MODULATE; + } + else arg2 = WINED3DTA_TEXTURE; + } } }
@@ -2432,6 +2472,11 @@ static void sampler(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DCont glBindTexture(GL_TEXTURE_2D, stateblock->wineD3DDevice->dummyTextureName[sampler]); checkGLcall("glBindTexture(GL_TEXTURE_2D, stateblock->wineD3DDevice->dummyTextureName[sampler])"); } + + if (sampler == 0 && (stateblock->textureState[0][WINED3DTSS_ALPHAOP] == WINED3DTOP_DX6MODULATE)) { + /* fixup in tex_alphaop may depend on currently set stage 0 texture */ + StateTable[STATE_TEXTURESTAGE(0, WINED3DTSS_ALPHAOP)].apply(STATE_TEXTURESTAGE(0, WINED3DTSS_ALPHAOP), stateblock, context); + } }
static void pixelshader(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DContext *context) { diff --git a/include/wine/wined3d_types.h b/include/wine/wined3d_types.h index 477df24..fa9a6e8 100644 --- a/include/wine/wined3d_types.h +++ b/include/wine/wined3d_types.h @@ -758,6 +758,7 @@ typedef enum _WINED3DTEXTUREOP { WINED3DTOP_DOTPRODUCT3 = 24, WINED3DTOP_MULTIPLYADD = 25, WINED3DTOP_LERP = 26, + WINED3DTOP_DX6MODULATE = 50,
WINED3DTOP_FORCE_DWORD = 0x7fffffff, } WINED3DTEXTUREOP;
Am Montag, 7. Januar 2008 07:55:08 schrieb Alexander Dorofeyev:
"D3DTBLEND_MODULATE Modulate texture-blending is supported. In this mode, the RGB values of the texture are multiplied with the RGB values that would have been used with no texturing. Any alpha values in the texture replace the alpha values that would have been used with no texturing."
D3DTA_TEXTURE has a somewhat strange behavior. If no texture is set, it behaves like D3DTA_PREVIOUS. Perhaps for alphaarg it behaves similarly if there is a texture, but it has no alpha. If that is the case, then I think it matches what you need for D3DTBLEND_MODULATE. Otherwise I think a private value like your D3DTOP_DX6MODULATE is the only way.
So I'd say test how D3DTA_TEXTURE should behave, maybe we implement it incorrectly at the moment.
I ran a little test, it didn't show any differences in Wine or XP in such case (D3DTA_TEXTURE alpha selected and no alpha in texture pixelformat). This seems to give alpha=1.0. I found nothing on this at msdn, but it more or less makes sense (no alpha = opaque image). Do you think there would be any use in adding such test to ddraw/tests/visual.c?
Stefan Dösinger wrote:
Am Montag, 7. Januar 2008 07:55:08 schrieb Alexander Dorofeyev:
"D3DTBLEND_MODULATE Modulate texture-blending is supported. In this mode, the RGB values of the texture are multiplied with the RGB values that would have been used with no texturing. Any alpha values in the texture replace the alpha values that would have been used with no texturing."
D3DTA_TEXTURE has a somewhat strange behavior. If no texture is set, it behaves like D3DTA_PREVIOUS. Perhaps for alphaarg it behaves similarly if there is a texture, but it has no alpha. If that is the case, then I think it matches what you need for D3DTBLEND_MODULATE. Otherwise I think a private value like your D3DTOP_DX6MODULATE is the only way.
So I'd say test how D3DTA_TEXTURE should behave, maybe we implement it incorrectly at the moment.
Am Mittwoch, 9. Januar 2008 10:54:28 schrieb Alexander Dorofeyev:
I ran a little test, it didn't show any differences in Wine or XP in such case (D3DTA_TEXTURE alpha selected and no alpha in texture pixelformat). This seems to give alpha=1.0. I found nothing on this at msdn, but it more or less makes sense (no alpha = opaque image). Do you think there would be any use in adding such test to ddraw/tests/visual.c?
Tests are always welcome!