From b0c24aac7db951acb534084e7d14e69afa720d63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20D=C3=B6singer?= Date: Sun, 6 Dec 2009 17:39:47 +0100 Subject: [PATCH 04/20] WineD3D: Infrastructure to render swapchains to a FBO This will be required for a number of things: -> Presenting to a window with different sizes than the back buffer -> Presenting partial rectangles -> Performant support of changing destination override windows -> Work around bugs in OSX --- dlls/wined3d/context.c | 2 +- dlls/wined3d/device.c | 1 + dlls/wined3d/surface.c | 15 ++++- dlls/wined3d/swapchain.c | 135 +++++++++++++++++++++++++++++++++++++++- dlls/wined3d/wined3d_private.h | 1 + 5 files changed, 149 insertions(+), 5 deletions(-) diff --git a/dlls/wined3d/context.c b/dlls/wined3d/context.c index 5bd06be..b56890e 100644 --- a/dlls/wined3d/context.c +++ b/dlls/wined3d/context.c @@ -1893,7 +1893,7 @@ static inline struct wined3d_context *FindContext(IWineD3DDeviceImpl *This, IWin context = findThreadContextForSwapChain(swapchain, tid); old_render_offscreen = context->render_offscreen; - context->render_offscreen = FALSE; + context->render_offscreen = ((IWineD3DSwapChainImpl *)swapchain)->render_to_fbo; /* The context != This->activeContext will catch a NOP context change. This can occur * if we are switching back to swapchain rendering in case of FBO or Back Buffer offscreen * rendering. No context change is needed in that case diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c index 5c5f6ed..1f2cdac 100644 --- a/dlls/wined3d/device.c +++ b/dlls/wined3d/device.c @@ -6683,6 +6683,7 @@ HRESULT create_primary_opengl_context(IWineD3DDevice *iface, IWineD3DSwapChain * } swapchain->context[0] = context_create(This, target, swapchain->win_handle, FALSE, &swapchain->presentParms); swapchain->num_contexts = 1; + swapchain->context[0]->render_offscreen = swapchain->render_to_fbo; create_dummy_textures(This); diff --git a/dlls/wined3d/surface.c b/dlls/wined3d/surface.c index 7c4551b..bd283af 100644 --- a/dlls/wined3d/surface.c +++ b/dlls/wined3d/surface.c @@ -668,6 +668,10 @@ GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchai TRACE("(%p) : swapchain %p\n", This, swapchain); if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) { + if(swapchain_impl->render_to_fbo) { + TRACE("Returning GL_COLOR_ATTACHMENT0\n"); + return GL_COLOR_ATTACHMENT0; + } TRACE("Returning GL_BACK\n"); return GL_BACK; } else if (swapchain_impl->frontBuffer == iface) { @@ -5129,8 +5133,17 @@ static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) { BOOL surface_is_offscreen(IWineD3DSurface *iface) { IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface; + IWineD3DSwapChainImpl *swapchain = (IWineD3DSwapChainImpl *) This->container; + + /* Not on a swapchain - must be offscreen */ + if (!(This->Flags & SFLAG_SWAPCHAIN)) return TRUE; + + /* The front buffer is always onscreen */ + if(iface == swapchain->frontBuffer) return FALSE; - return !(This->Flags & SFLAG_SWAPCHAIN); + /* If the swapchain is rendered to an FBO, the backbuffer is + * offscreen, otherwise onscreen */ + return swapchain->render_to_fbo; } const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl = diff --git a/dlls/wined3d/swapchain.c b/dlls/wined3d/swapchain.c index a23166a..5b4075d 100644 --- a/dlls/wined3d/swapchain.c +++ b/dlls/wined3d/swapchain.c @@ -95,6 +95,116 @@ static void WINAPI IWineD3DSwapChainImpl_Destroy(IWineD3DSwapChain *iface) HeapFree(GetProcessHeap(), 0, This); } +/* A GL context is provided by the caller */ +static inline void swapchain_blit(IWineD3DSwapChainImpl *This, struct wined3d_context *context) +{ + RECT window; + IWineD3DDeviceImpl *device = This->wineD3DDevice; + IWineD3DSurfaceImpl *backbuffer = ((IWineD3DSurfaceImpl *) This->backBuffer[0]); + UINT w = backbuffer->currentDesc.Width; + UINT h = backbuffer->currentDesc.Height; + GLenum gl_filter; + const struct wined3d_gl_info *gl_info = context->gl_info; + + GetClientRect(This->win_handle, &window); + if(w == window.right && h == window.bottom) gl_filter = GL_NEAREST; + else gl_filter = GL_LINEAR; + + if(gl_info->supported[EXT_FRAMEBUFFER_BLIT]) + { + ENTER_GL(); + context_bind_fbo(context, GL_READ_FRAMEBUFFER, &context->src_fbo); + context_attach_surface_fbo(context, GL_READ_FRAMEBUFFER, 0, This->backBuffer[0]); + context_attach_depth_stencil_fbo(context, GL_READ_FRAMEBUFFER, NULL, FALSE); + + context_bind_fbo(context, GL_DRAW_FRAMEBUFFER, NULL); + glDrawBuffer(GL_BACK); + + glDisable(GL_SCISSOR_TEST); + IWineD3DDeviceImpl_MarkStateDirty(This->wineD3DDevice, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE)); + + /* Note that the texture is upside down */ + gl_info->fbo_ops.glBlitFramebuffer(0, 0, w, h, + window.left, window.bottom, window.right, window.top, + GL_COLOR_BUFFER_BIT, gl_filter); + checkGLcall("Swapchain present blit(EXT_framebuffer_blit)\n"); + LEAVE_GL(); + } + else + { + struct wined3d_context *context2; + float tex_left = 0; + float tex_top = 0; + float tex_right = w; + float tex_bottom = h; + + context2 = context_acquire(This->wineD3DDevice, This->backBuffer[0], CTXUSAGE_BLIT); + + if(backbuffer->Flags & SFLAG_NORMCOORD) + { + tex_left /= w; + tex_right /= w; + tex_top /= h; + tex_bottom /= h; + } + + ENTER_GL(); + context_bind_fbo(context2, GL_DRAW_FRAMEBUFFER, NULL); + + /* Set up the texture. The surface is not in a IWineD3D*Texture container, + * so there are no d3d texture settings to dirtify + */ + device->blitter->set_shader((IWineD3DDevice *) device, backbuffer->resource.format_desc, + backbuffer->texture_target, backbuffer->pow2Width, + backbuffer->pow2Height); + glTexParameteri(backbuffer->texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(backbuffer->texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glDrawBuffer(GL_BACK); + + /* Set the viewport to the destination rectandle, disable any projection + * transformation set up by CTXUSAGE_BLIT, and draw a (-1,-1)-(1,1) quad. + * + * Back up viewport and matrix to avoid breaking last_was_blit + * + * Note that CTXUSAGE_BLIT set up viewport and ortho to match the surface + * size - we want the GL drawable(=window) size. + */ + glPushAttrib(GL_VIEWPORT_BIT); + glViewport(window.left, window.top, window.right, window.bottom); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + + glBegin(GL_QUADS); + /* bottom left */ + glTexCoord2f(tex_left, tex_bottom); + glVertex2i(-1, -1); + + /* top left */ + glTexCoord2f(tex_left, tex_top); + glVertex2i(-1, 1); + + /* top right */ + glTexCoord2f(tex_right, tex_top); + glVertex2i(1, 1); + + /* bottom right */ + glTexCoord2f(tex_right, tex_bottom); + glVertex2i(1, -1); + glEnd(); + + glPopMatrix(); + glPopAttrib(); + + device->blitter->unset_shader((IWineD3DDevice *) device); + checkGLcall("Swapchain present blit(manual)\n"); + LEAVE_GL(); + + context_release(context2); + } +} + static HRESULT WINAPI IWineD3DSwapChainImpl_Present(IWineD3DSwapChain *iface, CONST RECT *pSourceRect, CONST RECT *pDestRect, HWND hDestWindowOverride, CONST RGNDATA *pDirtyRegion, DWORD dwFlags) { IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface; struct wined3d_context *context; @@ -160,6 +270,22 @@ static HRESULT WINAPI IWineD3DSwapChainImpl_Present(IWineD3DSwapChain *iface, CO IWineD3DSwapChain_SetDestWindowOverride(iface, hDestWindowOverride); } + if(This->render_to_fbo) + { + /* This codepath should only be hit with the COPY swapeffect. Otherwise a backbuffer- + * window size mismatch is impossible(fullscreen) and src and dst rectangles are + * not allowed(they need the COPY swapeffect) + * + * The DISCARD swap effect is ok as well since any backbuffer content is allowed after + * the swap + */ + if(This->presentParms.SwapEffect == WINED3DSWAPEFFECT_FLIP ) + { + FIXME("Render-to-fbo with WINED3DSWAPEFFECT_FLIP\n"); + } + swapchain_blit(This, context); + } + SwapBuffers(This->context[0]->hdc); /* TODO: cycle through the swapchain buffers */ TRACE("SwapBuffers called, Starting new frame\n"); @@ -239,9 +365,12 @@ static HRESULT WINAPI IWineD3DSwapChainImpl_Present(IWineD3DSwapChain *iface, CO WINED3DCLEAR_TARGET, 0xff00ffff, 1.0f, 0); } - if(((IWineD3DSurfaceImpl *) This->frontBuffer)->Flags & SFLAG_INSYSMEM || - ((IWineD3DSurfaceImpl *) This->backBuffer[0])->Flags & SFLAG_INSYSMEM ) { - /* Both memory copies of the surfaces are ok, flip them around too instead of dirtifying */ + if(!This->render_to_fbo && + ( ((IWineD3DSurfaceImpl *) This->frontBuffer)->Flags & SFLAG_INSYSMEM || + ((IWineD3DSurfaceImpl *) This->backBuffer[0])->Flags & SFLAG_INSYSMEM ) ) { + /* Both memory copies of the surfaces are ok, flip them around too instead of dirtifying + * Doesn't work with render_to_fbo because we're not flipping + */ IWineD3DSurfaceImpl *front = (IWineD3DSurfaceImpl *) This->frontBuffer; IWineD3DSurfaceImpl *back = (IWineD3DSurfaceImpl *) This->backBuffer[0]; diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index 4fb556c..3173d12 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -2413,6 +2413,7 @@ typedef struct IWineD3DSwapChainImpl DWORD orig_width, orig_height; WINED3DFORMAT orig_fmt; WINED3DGAMMARAMP orig_gamma; + BOOL render_to_fbo; long prev_time, frames; /* Performance tracking */ unsigned int vSyncCounter; -- 1.6.4.4