From 6d660851509fa0054c8db96ca7559c86042c57e8 Mon Sep 17 00:00:00 2001 From: "H. Verbeet" Date: Fri, 17 Nov 2006 13:24:00 +0100 Subject: [PATCH] wined3d: Add FBO support for offscreen rendering. --- dlls/wined3d/device.c | 69 +++++++++++++++++++++++++++++++++- dlls/wined3d/directx.c | 6 +++ dlls/wined3d/drawprim.c | 14 +++++++ dlls/wined3d/wined3d_main.c | 5 +++ dlls/wined3d/wined3d_private.h | 2 + 5 files changed, 95 insertions(+), 1 deletion(-) diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c index 239d15df2f5..ef0e0469d32 100644 --- a/dlls/wined3d/device.c +++ b/dlls/wined3d/device.c @@ -5985,7 +5985,8 @@ static HRESULT WINAPI IWineD3DDeviceImpl_EndScene(IWineD3DDevice *iface) { checkGLcall("glFlush"); TRACE("End Scene\n"); - if(This->renderTarget != NULL) { + /* If we're using FBOs this isn't needed */ + if (wined3d_settings.offscreen_rendering_mode != ORM_FBO && This->renderTarget != NULL) { /* If the container of the rendertarget is a texture then we need to save the data from the pbuffer */ IUnknown *targetContainer = NULL; @@ -6989,6 +6990,66 @@ static HRESULT WINAPI IWineD3DDeviceImpl_GetDepthStencilSurface(IWineD3DDevice return WINED3D_OK; } +static void bind_fbo(IWineD3DDevice *iface) { + IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; + + if (!This->fbo) { + GL_EXTCALL(glGenFramebuffersEXT(1, &This->fbo)); + checkGLcall("glGenFramebuffersEXT()"); + } + GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, This->fbo)); + checkGLcall("glBindFramebuffer()"); +} + +/* TODO: Handle stencil attachments */ +static void set_depth_stencil_fbo(IWineD3DDevice *iface, IWineD3DSurface *depth_stencil) { + IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; + IWineD3DSurfaceImpl *depth_stencil_impl = (IWineD3DSurfaceImpl *)depth_stencil; + + bind_fbo(iface); + + if (depth_stencil_impl) { + IWineD3DSurface_PreLoad(depth_stencil); + glBindTexture (GL_TEXTURE_2D, depth_stencil_impl->glDescription.textureName); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE); + + GL_EXTCALL(glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depth_stencil_impl->glDescription.textureName, 0)); + checkGLcall("glFramebufferTexture2DEXT()"); + } else { + GL_EXTCALL(glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0)); + checkGLcall("glFramebufferTexture2DEXT()"); + } + + if (!This->render_offscreen) { + GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)); + checkGLcall("glBindFramebuffer()"); + } +} + +static void set_render_target_fbo(IWineD3DDevice *iface, IWineD3DSurface *render_target) { + IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; + IWineD3DSurfaceImpl *rtimpl = (IWineD3DSurfaceImpl *)render_target; + + if (This->render_offscreen) { + bind_fbo(iface); + + IWineD3DSurface_PreLoad(render_target); + + glBindTexture (GL_TEXTURE_2D, rtimpl->glDescription.textureName); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + GL_EXTCALL(glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, rtimpl->glDescription.textureName, 0)); + checkGLcall("glFramebufferTexture2DEXT()"); + } else { + GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)); + checkGLcall("glBindFramebuffer()"); + } +} + /* internal static helper functions */ static HRESULT WINAPI IWineD3DDeviceImpl_ActiveRender(IWineD3DDevice* iface, IWineD3DSurface *RenderSurface); @@ -7040,6 +7101,9 @@ static HRESULT WINAPI IWineD3DDeviceImpl_SetRenderTarget(IWineD3DDevice *iface, implementations that use separate pbuffers for different swapchains or rendertargets will have to duplicate the stencil buffer and incure an extra memory overhead */ hr = IWineD3DDeviceImpl_ActiveRender(iface, pRenderTarget); + if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) { + set_render_target_fbo(iface, pRenderTarget); + } } if (SUCCEEDED(hr)) { @@ -7086,6 +7150,9 @@ static HRESULT WINAPI IWineD3DDeviceImpl_SetDepthStencilSurface(IWineD3DDevice * /** TODO: glEnable/glDisable on depth/stencil depending on * pNewZStencil is NULL and the depth/stencil is enabled in d3d **********************************************************/ + if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) { + set_depth_stencil_fbo(iface, pNewZStencil); + } } return hr; diff --git a/dlls/wined3d/directx.c b/dlls/wined3d/directx.c index 00c95cd102e..368f38477d3 100644 --- a/dlls/wined3d/directx.c +++ b/dlls/wined3d/directx.c @@ -791,6 +791,12 @@ BOOL IWineD3DImpl_FillGLCaps(IWineD3D *iface, Display* display) { wined3d_settings.nonpower2_mode = NP2_NONE; } + /* We can only use ORM_FBO when the hardware supports it. */ + if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !gl_info->supported[EXT_FRAMEBUFFER_OBJECT]) { + WARN_(d3d_caps)("GL_EXT_framebuffer_object not supported, falling back to PBuffer offscreen rendering mode.\n"); + wined3d_settings.offscreen_rendering_mode = ORM_PBUFFER; + } + /* Below is a list of Nvidia and ATI GPUs. Both vendors have dozens of different GPUs with roughly the same * features. In most cases GPUs from a certain family differ in clockspeeds, the amount of video memory and * in case of the latest videocards in the number of pixel/vertex pipelines. diff --git a/dlls/wined3d/drawprim.c b/dlls/wined3d/drawprim.c index 0bea61f52e3..4a1bd8e45a2 100644 --- a/dlls/wined3d/drawprim.c +++ b/dlls/wined3d/drawprim.c @@ -2078,6 +2078,16 @@ static void drawPrimitiveUploadTextures(IWineD3DDeviceImpl* This) { } } +static void check_fbo_status(IWineD3DDevice *iface) { + IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; + + GLenum status = GL_EXTCALL(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT)); + switch(status) { + case GL_FRAMEBUFFER_COMPLETE_EXT: TRACE("FBO complete.\n"); break; + default: TRACE("FBO status %#x.\n", status); break; + } +} + /* Routine common to the draw primitive and draw indexed primitive routines */ void drawPrimitive(IWineD3DDevice *iface, int PrimitiveType, @@ -2101,6 +2111,10 @@ void drawPrimitive(IWineD3DDevice *iface, BOOL lighting_changed, lighting_original = FALSE; + if (TRACE_ON(d3d_draw) && wined3d_settings.offscreen_rendering_mode == ORM_FBO) { + check_fbo_status(iface); + } + /* Shaders can be implemented using ARB_PROGRAM, GLSL, or software - * here simply check whether a shader was set, or the user disabled shaders */ if (This->vs_selected_mode != SHADER_NONE && This->stateBlock->vertexShader && diff --git a/dlls/wined3d/wined3d_main.c b/dlls/wined3d/wined3d_main.c index e339815bed7..a3fc80b94f1 100644 --- a/dlls/wined3d/wined3d_main.c +++ b/dlls/wined3d/wined3d_main.c @@ -215,6 +215,11 @@ BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv) TRACE("Using PBuffers for offscreen rendering\n"); wined3d_settings.offscreen_rendering_mode = ORM_PBUFFER; } + else if (!strcmp(buffer,"fbo")) + { + TRACE("Using FBOs for offscreen rendering\n"); + wined3d_settings.offscreen_rendering_mode = ORM_FBO; + } } if ( !get_config_key( hkey, appkey, "RenderTargetLockMode", buffer, size) ) { diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index b3465a83917..6a4894c1d1a 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -137,6 +137,7 @@ static WINED3DGLTYPE const glTypeLookup[WINED3DDECLTYPE_UNUSED] = { #define ORM_BACKBUFFER 0 #define ORM_PBUFFER 1 +#define ORM_FBO 2 #define SHADER_SW 0 #define SHADER_ARB 1 @@ -550,6 +551,7 @@ typedef struct IWineD3DDeviceImpl /* For rendering to a texture using glCopyTexImage */ BOOL render_offscreen; + GLuint fbo; /* Cursor management */ BOOL bCursorVisible;