[02/10] wined3d: Fixup FBO depth attachments when the depth attachment is larger than the render target

H. Verbeet hverbeet at gmail.com
Sun Apr 8 18:53:32 CDT 2007


In D3D the depth stencil dimensions have to be greater than or equal
to the render target dimensions. With FBOs, the dimensions have to be
an exact match. This patch makes it possible to use FBOs with HL2 and
CSS.

Changelog:
  - Fixup FBO depth attachments when the depth attachment is larger
than the render target
-------------- next part --------------
---

 dlls/wined3d/device.c          |   55 +++++++++++++++++++++++++++++----------
 dlls/wined3d/drawprim.c        |    5 ++++
 dlls/wined3d/surface.c         |   57 ++++++++++++++++++++++++++++++++++++++++
 dlls/wined3d/wined3d_private.h |   12 ++++++++
 4 files changed, 116 insertions(+), 13 deletions(-)

diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c
index 4e1c086..2b5a168 100644
--- a/dlls/wined3d/device.c
+++ b/dlls/wined3d/device.c
@@ -768,6 +768,8 @@ static HRESULT  WINAPI IWineD3DDeviceImpl_CreateSurface(IWineD3DDevice *iface, U
             return WINED3DERR_INVALIDCALL;
     }
 
+    list_init(&object->renderbuffers);
+
     /* Call the private setup routine */
     return IWineD3DSurface_PrivateSetup( (IWineD3DSurface *) object );
 
@@ -5110,22 +5112,27 @@ static void set_depth_stencil_fbo(IWineD3DDevice *iface, IWineD3DSurface *depth_
     TRACE("Set depth stencil to %p\n", depth_stencil);
 
     if (depth_stencil_impl) {
-        GLenum texttarget, target;
-        GLint old_binding = 0;
+        if (depth_stencil_impl->current_renderbuffer) {
+            GL_EXTCALL(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_stencil_impl->current_renderbuffer->id));
+            checkGLcall("glFramebufferRenderbufferEXT()");
+        } else {
+            GLenum texttarget, target;
+            GLint old_binding = 0;
 
-        texttarget = depth_stencil_impl->glDescription.target;
-        target = texttarget == GL_TEXTURE_2D ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP_ARB;
-        glGetIntegerv(texttarget == GL_TEXTURE_2D ? GL_TEXTURE_BINDING_2D : GL_TEXTURE_BINDING_CUBE_MAP_ARB, &old_binding);
+            texttarget = depth_stencil_impl->glDescription.target;
+            target = texttarget == GL_TEXTURE_2D ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP_ARB;
+            glGetIntegerv(texttarget == GL_TEXTURE_2D ? GL_TEXTURE_BINDING_2D : GL_TEXTURE_BINDING_CUBE_MAP_ARB, &old_binding);
 
-        IWineD3DSurface_PreLoad(depth_stencil);
+            IWineD3DSurface_PreLoad(depth_stencil);
 
-        glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-        glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-        glTexParameteri(target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
-        glBindTexture(target, old_binding);
+            glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+            glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+            glTexParameteri(target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
+            glBindTexture(target, old_binding);
 
-        GL_EXTCALL(glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, texttarget, depth_stencil_impl->glDescription.textureName, 0));
-        checkGLcall("glFramebufferTexture2DEXT()");
+            GL_EXTCALL(glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, texttarget, 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()");
@@ -5175,6 +5182,22 @@ static void check_fbo_status(IWineD3DDevice *iface) {
     }
 }
 
+static BOOL depth_mismatch_fbo(IWineD3DDevice *iface) {
+    IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+    IWineD3DSurfaceImpl *rt_impl = (IWineD3DSurfaceImpl *)This->render_targets[0];
+    IWineD3DSurfaceImpl *ds_impl = (IWineD3DSurfaceImpl *)This->stencilBufferTarget;
+
+    if (!ds_impl) return FALSE;
+
+    if (ds_impl->current_renderbuffer) {
+        return (rt_impl->pow2Width != ds_impl->current_renderbuffer->width ||
+                rt_impl->pow2Height != ds_impl->current_renderbuffer->height);
+    }
+
+    return (rt_impl->pow2Width != ds_impl->pow2Width ||
+            rt_impl->pow2Height != ds_impl->pow2Height);
+}
+
 void apply_fbo_state(IWineD3DDevice *iface) {
     IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
     unsigned int i;
@@ -5192,7 +5215,13 @@ void apply_fbo_state(IWineD3DDevice *iface) {
         }
 
         /* Apply depth targets */
-        if (This->fbo_depth_attachment != This->stencilBufferTarget) {
+        if (This->fbo_depth_attachment != This->stencilBufferTarget || depth_mismatch_fbo(iface)) {
+            unsigned int w = ((IWineD3DSurfaceImpl *)This->render_targets[0])->pow2Width;
+            unsigned int h = ((IWineD3DSurfaceImpl *)This->render_targets[0])->pow2Height;
+
+            if (This->stencilBufferTarget) {
+                surface_set_compatible_renderbuffer(This->stencilBufferTarget, w, h);
+            }
             set_depth_stencil_fbo(iface, This->stencilBufferTarget);
             This->fbo_depth_attachment = This->stencilBufferTarget;
         }
diff --git a/dlls/wined3d/drawprim.c b/dlls/wined3d/drawprim.c
index a9a719f..b8d0fcc 100644
--- a/dlls/wined3d/drawprim.c
+++ b/dlls/wined3d/drawprim.c
@@ -804,6 +804,11 @@ static void depth_copy(IWineD3DDevice *iface) {
     /* TODO: Make this work for modes other than FBO */
     if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
 
+    if (depth_stencil->current_renderbuffer) {
+        FIXME("Not supported with fixed up depth stencil\n");
+        return;
+    }
+
     if (This->render_offscreen) {
         static GLuint tmp_texture = 0;
         GLint old_binding = 0;
diff --git a/dlls/wined3d/surface.c b/dlls/wined3d/surface.c
index 1684d1a..8b7b47d 100644
--- a/dlls/wined3d/surface.c
+++ b/dlls/wined3d/surface.c
@@ -8,6 +8,7 @@
  * Copyright 2004 Christian Costa
  * Copyright 2005 Oliver Stieber
  * Copyright 2006 Stefan D?singer for CodeWeavers
+ * Copyright 2007 Henri Verbeet
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -237,6 +238,56 @@ static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal,
     LEAVE_GL();
 }
 
+/* In D3D the depth stencil dimensions have to be greater than or equal to the
+ * render target dimensions. With FBOs, the dimensions have to be an exact match. */
+/* TODO: We should synchronize the renderbuffer's content with the texture's content. */
+void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
+    IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
+    renderbuffer_entry_t *entry;
+    GLuint renderbuffer = 0;
+    unsigned int src_width, src_height;
+
+    src_width = This->pow2Width;
+    src_height = This->pow2Height;
+
+    /* A depth stencil smaller than the render target is not valid */
+    if (width > src_width || height > src_height) return;
+
+    /* Remove any renderbuffer set if the sizes match */
+    if (width == src_width && height == src_height) {
+        This->current_renderbuffer = NULL;
+        return;
+    }
+
+    /* Look if we've already got a renderbuffer of the correct dimensions */
+    LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
+        if (entry->width == width && entry->height == height) {
+            renderbuffer = entry->id;
+            This->current_renderbuffer = entry;
+            break;
+        }
+    }
+
+    if (!renderbuffer) {
+        const PixelFormatDesc *format_entry = getFormatDescEntry(This->resource.format);
+
+        GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
+        GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
+        GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, format_entry->glFormat, width, height));
+
+        entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
+        entry->width = width;
+        entry->height = height;
+        entry->id = renderbuffer;
+        list_add_head(&This->renderbuffers, &entry->entry);
+
+        This->current_renderbuffer = entry;
+    }
+
+    checkGLcall("set_compatible_renderbuffer");
+}
+
+
 /* *******************************************
    IWineD3DSurface IUnknown parts follow
    ******************************************* */
@@ -271,6 +322,7 @@ ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
     TRACE("(%p) : Releasing from %d\n", This, ref + 1);
     if (ref == 0) {
         IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) This->resource.wineD3DDevice;
+        renderbuffer_entry_t *entry, *entry2;
         TRACE("(%p) : cleaning up\n", This);
 
         if(iface == device->lastActiveRenderTarget) {
@@ -339,6 +391,11 @@ ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
         if(iface == device->ddraw_primary)
             device->ddraw_primary = NULL;
 
+        LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
+            GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
+            HeapFree(GetProcessHeap(), 0, entry);
+        }
+
         TRACE("(%p) Released\n", This);
         HeapFree(GetProcessHeap(), 0, This);
 
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index bed5cad..6b290c6 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -1003,6 +1003,13 @@ typedef struct wineD3DSurface_DIB {
     BOOL client_memory;
 } wineD3DSurface_DIB;
 
+typedef struct {
+    struct list entry;
+    GLuint id;
+    UINT width;
+    UINT height;
+} renderbuffer_entry_t;
+
 /*****************************************************************************
  * IWineD3DSurface implementation structure
  */
@@ -1054,6 +1061,9 @@ struct IWineD3DSurfaceImpl
     DWORD                     CKeyFlags;
 
     DDCOLORKEY                glCKey;
+
+    struct list               renderbuffers;
+    renderbuffer_entry_t      *current_renderbuffer;
 };
 
 extern const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl;
@@ -1398,6 +1408,8 @@ void   set_tex_op(IWineD3DDevice *iface, BOOL isAlpha, int Stage, WINED3DTEXTURE
 void   set_tex_op_nvrc(IWineD3DDevice *iface, BOOL is_alpha, int stage, WINED3DTEXTUREOP op, DWORD arg1, DWORD arg2, DWORD arg3, INT texture_idx);
 void   set_texture_matrix(const float *smat, DWORD flags, BOOL calculatedCoords);
 
+void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height);
+
 int D3DFmtMakeGlCfg(WINED3DFORMAT BackBufferFormat, WINED3DFORMAT StencilBufferFormat, int *attribs, int* nAttribs, BOOL alternate);
 
 /* Math utils */


More information about the wine-patches mailing list