[PATCH 07/10] wined3d: Add a quirk to rebind FBOs when one of their attached textures is updated.

Henri Verbeet hverbeet at codeweavers.com
Wed Mar 17 15:59:51 CDT 2010


Updating a texture while it is attached to the currently bound FBO is
something GL implementations tend to get wrong. NVIDIA fails at
glTexSubImage2D(), fglrx and Mesa with glTexImage2D(). I'm afraid to try what
happens on OS X. Fortunately we never use glTexImage2D() while a texture is
attached to an FBO, so we only need to care about glTexSubImage2D().
---
 dlls/wined3d/context.c         |   31 ++++++++++++++++++++
 dlls/wined3d/directx.c         |   60 ++++++++++++++++++++++++++++++++++++++++
 dlls/wined3d/surface.c         |   11 +++++++
 dlls/wined3d/wined3d_private.h |    3 ++
 4 files changed, 105 insertions(+), 0 deletions(-)

diff --git a/dlls/wined3d/context.c b/dlls/wined3d/context.c
index 090f788..5d8aa0c 100644
--- a/dlls/wined3d/context.c
+++ b/dlls/wined3d/context.c
@@ -461,6 +461,12 @@ static void context_apply_fbo_state(struct wined3d_context *context)
         context_destroy_fbo_entry(context, entry);
     }
 
+    if (context->rebind_fbo)
+    {
+        context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
+        context->rebind_fbo = FALSE;
+    }
+
     if (context->render_offscreen)
     {
         context->current_fbo = context_find_fbo_entry(context);
@@ -652,6 +658,31 @@ void context_resource_released(IWineD3DDevice *iface, IWineD3DResource *resource
     }
 }
 
+void context_surface_update(struct wined3d_context *context, IWineD3DSurfaceImpl *surface)
+{
+    const struct wined3d_gl_info *gl_info = context->gl_info;
+    struct fbo_entry *entry = context->current_fbo;
+    unsigned int i;
+
+    if (!entry || context->rebind_fbo) return;
+
+    for (i = 0; i < gl_info->limits.buffers; ++i)
+    {
+        if (surface == (IWineD3DSurfaceImpl *)entry->render_targets[i])
+        {
+            TRACE("Updated surface %p is bound as color attachment %u to the current FBO.\n", surface, i);
+            context->rebind_fbo = TRUE;
+            return;
+        }
+    }
+
+    if (surface == (IWineD3DSurfaceImpl *)entry->depth_stencil)
+    {
+        TRACE("Updated surface %p is bound as depth attachment to the current FBO.\n", surface);
+        context->rebind_fbo = TRUE;
+    }
+}
+
 static BOOL context_set_pixel_format(const struct wined3d_gl_info *gl_info, HDC dc, int format)
 {
     int current = GetPixelFormat(dc);
diff --git a/dlls/wined3d/directx.c b/dlls/wined3d/directx.c
index a79eda6..f2de807 100644
--- a/dlls/wined3d/directx.c
+++ b/dlls/wined3d/directx.c
@@ -669,6 +669,56 @@ static BOOL match_broken_nv_clip(const struct wined3d_gl_info *gl_info, const ch
     return ret;
 }
 
+static BOOL match_fbo_tex_update(const struct wined3d_gl_info *gl_info, const char *gl_renderer,
+        enum wined3d_gl_vendor gl_vendor, enum wined3d_pci_vendor card_vendor, enum wined3d_pci_device device)
+{
+    char data[4 * 4 * 4];
+    GLuint tex, fbo;
+    GLenum status;
+
+    if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return FALSE;
+
+    memset(data, 0xcc, sizeof(data));
+
+    glGenTextures(1, &tex);
+    glBindTexture(GL_TEXTURE_2D, tex);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 4, 4, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
+    checkGLcall("glTexImage2D");
+
+    gl_info->fbo_ops.glGenFramebuffers(1, &fbo);
+    gl_info->fbo_ops.glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+    gl_info->fbo_ops.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
+    checkGLcall("glFramebufferTexture2D");
+
+    status = gl_info->fbo_ops.glCheckFramebufferStatus(GL_FRAMEBUFFER);
+    if (status != GL_FRAMEBUFFER_COMPLETE) ERR("FBO status %#x\n", status);
+    checkGLcall("glCheckFramebufferStatus");
+
+    memset(data, 0x11, sizeof(data));
+    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data);
+    checkGLcall("glTexSubImage2D");
+
+    glClearColor(0.996, 0.729, 0.745, 0.792);
+    glClear(GL_COLOR_BUFFER_BIT);
+    checkGLcall("glClear");
+
+    glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data);
+    checkGLcall("glGetTexImage");
+
+    gl_info->fbo_ops.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
+    gl_info->fbo_ops.glBindFramebuffer(GL_FRAMEBUFFER, 0);
+    glBindTexture(GL_TEXTURE_2D, 0);
+    checkGLcall("glBindTexture");
+
+    gl_info->fbo_ops.glDeleteFramebuffers(1, &fbo);
+    glDeleteTextures(1, &tex);
+    checkGLcall("glDeleteTextures");
+
+    return *(DWORD *)data == 0x11111111;
+}
+
 static void quirk_arb_constants(struct wined3d_gl_info *gl_info)
 {
     TRACE_(d3d_caps)("Using ARB vs constant limit(=%u) for GLSL.\n", gl_info->limits.arb_vs_native_constants);
@@ -797,6 +847,11 @@ static void quirk_disable_nvvp_clip(struct wined3d_gl_info *gl_info)
     gl_info->quirks |= WINED3D_QUIRK_NV_CLIP_BROKEN;
 }
 
+static void quirk_fbo_tex_update(struct wined3d_gl_info *gl_info)
+{
+    gl_info->quirks |= WINED3D_QUIRK_FBO_TEX_UPDATE;
+}
+
 struct driver_quirk
 {
     BOOL (*match)(const struct wined3d_gl_info *gl_info, const char *gl_renderer,
@@ -876,6 +931,11 @@ static const struct driver_quirk quirk_table[] =
         quirk_disable_nvvp_clip,
         "Apple NV_vertex_program clip bug quirk"
     },
+    {
+        match_fbo_tex_update,
+        quirk_fbo_tex_update,
+        "FBO rebind for attachment updates"
+    },
 };
 
 /* Certain applications (Steam) complain if we report an outdated driver version. In general,
diff --git a/dlls/wined3d/surface.c b/dlls/wined3d/surface.c
index 5cc0932..d55b73c 100644
--- a/dlls/wined3d/surface.c
+++ b/dlls/wined3d/surface.c
@@ -697,6 +697,17 @@ static void surface_upload_data(IWineD3DSurfaceImpl *This, const struct wined3d_
     }
 
     LEAVE_GL();
+
+    if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
+    {
+        IWineD3DDeviceImpl *device = This->resource.device;
+        unsigned int i;
+
+        for (i = 0; i < device->numContexts; ++i)
+        {
+            context_surface_update(device->contexts[i], This);
+        }
+    }
 }
 
 /* This call just allocates the texture, the caller is responsible for binding
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 3dae98e..5b5a592 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -50,6 +50,7 @@
 #define WINED3D_QUIRK_GLSL_CLIP_VARYING         0x00000004
 #define WINED3D_QUIRK_ALLOWS_SPECULAR_ALPHA     0x00000008
 #define WINED3D_QUIRK_NV_CLIP_BROKEN            0x00000010
+#define WINED3D_QUIRK_FBO_TEX_UPDATE            0x00000020
 
 /* Texture format fixups */
 
@@ -1100,6 +1101,7 @@ struct wined3d_context
     GLuint                  dst_fbo;
     GLuint                  fbo_read_binding;
     GLuint                  fbo_draw_binding;
+    BOOL rebind_fbo;
 
     /* Queries */
     GLuint *free_occlusion_queries;
@@ -1210,6 +1212,7 @@ void context_release(struct wined3d_context *context) DECLSPEC_HIDDEN;
 BOOL context_set_current(struct wined3d_context *ctx) DECLSPEC_HIDDEN;
 void context_set_draw_buffer(struct wined3d_context *context, GLenum buffer) DECLSPEC_HIDDEN;
 void context_set_tls_idx(DWORD idx) DECLSPEC_HIDDEN;
+void context_surface_update(struct wined3d_context *context, IWineD3DSurfaceImpl *surface) DECLSPEC_HIDDEN;
 
 void delete_opengl_contexts(IWineD3DDevice *iface, IWineD3DSwapChain *swapchain) DECLSPEC_HIDDEN;
 HRESULT create_primary_opengl_context(IWineD3DDevice *iface, IWineD3DSwapChain *swapchain) DECLSPEC_HIDDEN;
-- 
1.6.4.4




More information about the wine-patches mailing list