Well, we discussed a few different ways of handling this, and here are the results:
1) We can flip the texture we get from the framebuffer in Device_LoadTexture. Getting images from the back framebuffer always are upside down, so we'll need to manually flip it before setting it as the next render target. a) Use glCopyTexSubImage2D() to swap each row b) Use glReadPixels() to copy to a buffer, flip the image, then use glTexImage2D() to write it back. Both of these methods fixed the upside down rendering, but the issue of the disconnected/broken models is still there, although less prevalent now that things look more normal to begin with. I've attached both patches. However, performance takes a pretty serious hit: Max Payne 2 with pixel shaders enabled in High detail mode (there are still some lighting bugs, though): Current git (upside-down): ~65 fps 1a: ~35 fps 1b: ~15 fps Civ4 with pixel shaders enabled Current git (some upside-down stuff and very broken models): 30-50 fps 1a (still some broken models): ~35 fps 1b (still some broken modles): 25-35 fps 2) Try to do some kind of fixup in the shader itself. I'm not entirely sure how to do this other than being creative with the small hack I posted originally. There are no noticable performance losses, but visually, some things are broken (the sky in Tomb Raider Legends as well as Half Life 2 are completely wrong, but Max Payne is about 99% right). Any other suggestions? Also, the models are still broke, so that may be a separate topic and not related like I thought it was.
diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c index 323a2f5..f0fe2ec 100644 --- a/dlls/wined3d/device.c +++ b/dlls/wined3d/device.c @@ -3463,25 +3463,15 @@ static HRESULT WINAPI IWineD3DDeviceImpl case D3DCULL_CW: glEnable(GL_CULL_FACE); checkGLcall("glEnable GL_CULL_FACE"); - if (This->renderUpsideDown) { - glFrontFace(GL_CW); - checkGLcall("glFrontFace GL_CW"); - } else { - glFrontFace(GL_CCW); - checkGLcall("glFrontFace GL_CCW"); - } + glFrontFace(GL_CCW); + checkGLcall("glFrontFace GL_CCW"); glCullFace(GL_BACK); break; case D3DCULL_CCW: glEnable(GL_CULL_FACE); checkGLcall("glEnable GL_CULL_FACE"); - if (This->renderUpsideDown) { - glFrontFace(GL_CCW); - checkGLcall("glFrontFace GL_CCW"); - } else { - glFrontFace(GL_CW); - checkGLcall("glFrontFace GL_CW"); - } + glFrontFace(GL_CW); + checkGLcall("glFrontFace GL_CW"); glCullFace(GL_BACK); break; default: @@ -7385,28 +7375,9 @@ #endif IWineD3DSurface_AddRef(This->renderTarget); IWineD3DSurface_Release(tmp); - { - DWORD value; - - /* The surface must be rendered upside down to cancel the flip produce by glCopyTexImage */ - /* Check that the container is not a swapchain member */ - - IWineD3DSwapChain *tmpSwapChain; - if (WINED3D_OK != IWineD3DSurface_GetContainer(This->renderTarget, &IID_IWineD3DSwapChain, (void **)&tmpSwapChain)) { - This->renderUpsideDown = TRUE; - }else{ - This->renderUpsideDown = FALSE; - IWineD3DSwapChain_Release(tmpSwapChain); - } - /* Force updating the cull mode */ - TRACE("setting render state\n"); - IWineD3DDevice_GetRenderState(iface, WINED3DRS_CULLMODE, &value); - IWineD3DDevice_SetRenderState(iface, WINED3DRS_CULLMODE, value); - - /* Force updating projection matrix */ - This->last_was_rhw = FALSE; - This->proj_valid = FALSE; - } + /* Force updating projection matrix */ + This->last_was_rhw = FALSE; + This->proj_valid = FALSE; /* Restore recording state */ This->isRecordingState = oldRecording; diff --git a/dlls/wined3d/drawprim.c b/dlls/wined3d/drawprim.c index bb48982..485e0fe 100644 --- a/dlls/wined3d/drawprim.c +++ b/dlls/wined3d/drawprim.c @@ -148,12 +148,6 @@ static void init_materials(IWineD3DDevic } -static GLfloat invymat[16] = { - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, -1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f}; - void d3ddevice_set_ortho(IWineD3DDeviceImpl *This) { /* If the last draw was transformed as well, no need to reapply all the matrixes */ if ( (!This->last_was_rhw) || (This->viewport_changed) ) { @@ -190,10 +184,6 @@ void d3ddevice_set_ortho(IWineD3DDeviceI a pixel (See comment above glTranslate below) */ glTranslatef(0.375, 0.375, 0); checkGLcall("glTranslatef(0.375, 0.375, 0)"); - if (This->renderUpsideDown) { - glMultMatrixf(invymat); - checkGLcall("glMultMatrixf(invymat)"); - } /* Vertex fog on transformed vertices? Use the calculated fog factor stored in the specular color */ if(This->stateBlock->renderState[WINED3DRS_FOGENABLE] && This->stateBlock->renderState[WINED3DRS_FOGVERTEXMODE] != D3DFOG_NONE) { @@ -295,10 +285,6 @@ static void primitiveInitState( glTranslatef(0.9 / This->stateBlock->viewport.Width, -0.9 / This->stateBlock->viewport.Height, 0); checkGLcall("glTranslatef (0.9 / width, -0.9 / height, 0)"); - if (This->renderUpsideDown) { - glMultMatrixf(invymat); - checkGLcall("glMultMatrixf(invymat)"); - } glMultMatrixf((float *) &This->stateBlock->transforms[WINED3DTS_PROJECTION].u.m[0][0]); checkGLcall("glLoadMatrixf"); } @@ -318,10 +304,6 @@ static void primitiveInitState( a pixel (See comment above glTranslate above) */ glTranslatef(0.9 / This->stateBlock->viewport.Width, -0.9 / This->stateBlock->viewport.Height, 0); checkGLcall("glTranslatef (0.9 / width, -0.9 / height, 0)"); - if (This->renderUpsideDown) { - glMultMatrixf(invymat); - checkGLcall("glMultMatrixf(invymat)"); - } This->modelview_valid = FALSE; This->proj_valid = FALSE; } diff --git a/dlls/wined3d/surface.c b/dlls/wined3d/surface.c index 9c2ed60..0017804 100644 --- a/dlls/wined3d/surface.c +++ b/dlls/wined3d/surface.c @@ -1656,11 +1656,14 @@ static HRESULT WINAPI IWineD3DSurfaceImp FIXME("Format %d not supported\n", This->resource.format); else { GLint prevRead; + int i; + glGetIntegerv(GL_READ_BUFFER, &prevRead); vcheckGLcall("glGetIntegerv"); glReadBuffer(GL_BACK); vcheckGLcall("glReadBuffer"); + /* Setup the initial target texture array */ glCopyTexImage2D(This->glDescription.target, This->glDescription.level, This->glDescription.glFormatInternal, @@ -1669,8 +1672,21 @@ static HRESULT WINAPI IWineD3DSurfaceImp This->currentDesc.Width, This->currentDesc.Height, 0); - checkGLcall("glCopyTexImage2D"); + + /* Now, we need to flip this texture because we are taking it from the backbuffer */ + for (i=0; i<This->currentDesc.Height; i++) { + glCopyTexSubImage2D(This->glDescription.target, + This->glDescription.level, + 0, + i, + 0, + This->currentDesc.Height -i - 1, + This->currentDesc.Width, + 1); + } + checkGLcall("glCopyTexSubImage2D"); + glReadBuffer(prevRead); vcheckGLcall("glReadBuffer"); TRACE("Updating target %d\n", This->glDescription.target); diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index 1f51e3b..7a9b8ac 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -531,9 +531,6 @@ #define NEEDS_DI PALETTEENTRY palettes[MAX_PALETTES][256]; UINT currentPalette; - /* For rendering to a texture using glCopyTexImage */ - BOOL renderUpsideDown; - /* Cursor management */ BOOL bCursorVisible; UINT xHotSpot;
diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c index 323a2f5..17b3a9a 100644 --- a/dlls/wined3d/device.c +++ b/dlls/wined3d/device.c @@ -3463,25 +3463,15 @@ static HRESULT WINAPI IWineD3DDeviceImpl case D3DCULL_CW: glEnable(GL_CULL_FACE); checkGLcall("glEnable GL_CULL_FACE"); - if (This->renderUpsideDown) { - glFrontFace(GL_CW); - checkGLcall("glFrontFace GL_CW"); - } else { - glFrontFace(GL_CCW); - checkGLcall("glFrontFace GL_CCW"); - } + glFrontFace(GL_CCW); + checkGLcall("glFrontFace GL_CCW"); glCullFace(GL_BACK); break; case D3DCULL_CCW: glEnable(GL_CULL_FACE); checkGLcall("glEnable GL_CULL_FACE"); - if (This->renderUpsideDown) { - glFrontFace(GL_CCW); - checkGLcall("glFrontFace GL_CCW"); - } else { - glFrontFace(GL_CW); - checkGLcall("glFrontFace GL_CW"); - } + glFrontFace(GL_CW); + checkGLcall("glFrontFace GL_CW"); glCullFace(GL_BACK); break; default: @@ -7385,28 +7375,9 @@ #endif IWineD3DSurface_AddRef(This->renderTarget); IWineD3DSurface_Release(tmp); - { - DWORD value; - - /* The surface must be rendered upside down to cancel the flip produce by glCopyTexImage */ - /* Check that the container is not a swapchain member */ - - IWineD3DSwapChain *tmpSwapChain; - if (WINED3D_OK != IWineD3DSurface_GetContainer(This->renderTarget, &IID_IWineD3DSwapChain, (void **)&tmpSwapChain)) { - This->renderUpsideDown = TRUE; - }else{ - This->renderUpsideDown = FALSE; - IWineD3DSwapChain_Release(tmpSwapChain); - } - /* Force updating the cull mode */ - TRACE("setting render state\n"); - IWineD3DDevice_GetRenderState(iface, WINED3DRS_CULLMODE, &value); - IWineD3DDevice_SetRenderState(iface, WINED3DRS_CULLMODE, value); - - /* Force updating projection matrix */ - This->last_was_rhw = FALSE; - This->proj_valid = FALSE; - } + /* Force updating projection matrix */ + This->last_was_rhw = FALSE; + This->proj_valid = FALSE; /* Restore recording state */ This->isRecordingState = oldRecording; diff --git a/dlls/wined3d/drawprim.c b/dlls/wined3d/drawprim.c index bb48982..485e0fe 100644 --- a/dlls/wined3d/drawprim.c +++ b/dlls/wined3d/drawprim.c @@ -148,12 +148,6 @@ static void init_materials(IWineD3DDevic } -static GLfloat invymat[16] = { - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, -1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f}; - void d3ddevice_set_ortho(IWineD3DDeviceImpl *This) { /* If the last draw was transformed as well, no need to reapply all the matrixes */ if ( (!This->last_was_rhw) || (This->viewport_changed) ) { @@ -190,10 +184,6 @@ void d3ddevice_set_ortho(IWineD3DDeviceI a pixel (See comment above glTranslate below) */ glTranslatef(0.375, 0.375, 0); checkGLcall("glTranslatef(0.375, 0.375, 0)"); - if (This->renderUpsideDown) { - glMultMatrixf(invymat); - checkGLcall("glMultMatrixf(invymat)"); - } /* Vertex fog on transformed vertices? Use the calculated fog factor stored in the specular color */ if(This->stateBlock->renderState[WINED3DRS_FOGENABLE] && This->stateBlock->renderState[WINED3DRS_FOGVERTEXMODE] != D3DFOG_NONE) { @@ -295,10 +285,6 @@ static void primitiveInitState( glTranslatef(0.9 / This->stateBlock->viewport.Width, -0.9 / This->stateBlock->viewport.Height, 0); checkGLcall("glTranslatef (0.9 / width, -0.9 / height, 0)"); - if (This->renderUpsideDown) { - glMultMatrixf(invymat); - checkGLcall("glMultMatrixf(invymat)"); - } glMultMatrixf((float *) &This->stateBlock->transforms[WINED3DTS_PROJECTION].u.m[0][0]); checkGLcall("glLoadMatrixf"); } @@ -318,10 +304,6 @@ static void primitiveInitState( a pixel (See comment above glTranslate above) */ glTranslatef(0.9 / This->stateBlock->viewport.Width, -0.9 / This->stateBlock->viewport.Height, 0); checkGLcall("glTranslatef (0.9 / width, -0.9 / height, 0)"); - if (This->renderUpsideDown) { - glMultMatrixf(invymat); - checkGLcall("glMultMatrixf(invymat)"); - } This->modelview_valid = FALSE; This->proj_valid = FALSE; } diff --git a/dlls/wined3d/surface.c b/dlls/wined3d/surface.c index 9c2ed60..25e6e12 100644 --- a/dlls/wined3d/surface.c +++ b/dlls/wined3d/surface.c @@ -1656,21 +1656,45 @@ static HRESULT WINAPI IWineD3DSurfaceImp FIXME("Format %d not supported\n", This->resource.format); else { GLint prevRead; + int i; + unsigned pitch; + BYTE* mem; + BYTE *top, *bottom; + glGetIntegerv(GL_READ_BUFFER, &prevRead); vcheckGLcall("glGetIntegerv"); glReadBuffer(GL_BACK); vcheckGLcall("glReadBuffer"); - glCopyTexImage2D(This->glDescription.target, - This->glDescription.level, - This->glDescription.glFormatInternal, - 0, - 0, - This->currentDesc.Width, - This->currentDesc.Height, - 0); + mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * sizeof(BYTE)); + + glReadPixels(0, 0, This->currentDesc.Width, This->currentDesc.Height, + This->glDescription.glFormat, This->glDescription.glType, mem); + + top = mem; + pitch = This->resource.size / (This->currentDesc.Height); + bottom = mem + (pitch * (This->currentDesc.Height - 1)); + for(i = 0; i < This->currentDesc.Height / 2; i++) { + memcpy(mem, top, pitch); + memcpy(top, bottom, pitch); + memcpy(bottom, mem, pitch); + top += pitch; + bottom -= pitch; + } + + glTexImage2D(This->glDescription.target, + This->glDescription.level, + This->glDescription.glFormatInternal, + This->currentDesc.Width, + This->currentDesc.Height, + 0, + This->glDescription.glFormat, + This->glDescription.glType, + mem); + checkGLcall("glTexImage2D"); - checkGLcall("glCopyTexImage2D"); + HeapFree(GetProcessHeap(), 0, mem); + glReadBuffer(prevRead); vcheckGLcall("glReadBuffer"); TRACE("Updating target %d\n", This->glDescription.target); diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index 1f51e3b..7a9b8ac 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -531,9 +531,6 @@ #define NEEDS_DI PALETTEENTRY palettes[MAX_PALETTES][256]; UINT currentPalette; - /* For rendering to a texture using glCopyTexImage */ - BOOL renderUpsideDown; - /* Cursor management */ BOOL bCursorVisible; UINT xHotSpot;