[5/5] wined3d: Allow the depth buffer to be shared between onscreen and offscreen rendering modes

H. Verbeet hverbeet at gmail.com
Fri Nov 17 06:24:04 CST 2006


The current implementation is for FBOs, but it should be possible to
make this work for PBuffers as well.

Changelog:
  - Allow the depth buffer to be shared between onscreen and offscreen
rendering modes
-------------- next part --------------
---

 dlls/wined3d/device.c                |    8 +
 dlls/wined3d/drawprim.c              |  196 ++++++++++++++++++++++++++++++++++
 dlls/wined3d/wined3d_private.h       |    1 
 dlls/wined3d/wined3d_private_types.h |    7 +
 4 files changed, 212 insertions(+), 0 deletions(-)

diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c
index ef0e046..ab7ab10 100644
--- a/dlls/wined3d/device.c
+++ b/dlls/wined3d/device.c
@@ -7006,6 +7006,8 @@ static void set_depth_stencil_fbo(IWineD
     IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
     IWineD3DSurfaceImpl *depth_stencil_impl = (IWineD3DSurfaceImpl *)depth_stencil;
 
+    This->depth_copy_state = WINED3D_DCS_NO_COPY;
+
     bind_fbo(iface);
 
     if (depth_stencil_impl) {
@@ -7275,6 +7277,9 @@ static void device_render_to_texture(IWi
     BOOL oldRecording;
     IWineD3DStateBlockImpl *oldUpdateStateBlock;
 
+    /* Nothing to update, return. */
+    if (This->render_offscreen == isTexture) return;
+
     /* Disable recording */
     oldUpdateStateBlock = This->updateStateBlock;
     oldRecording= This->isRecordingState;
@@ -7282,6 +7287,9 @@ static void device_render_to_texture(IWi
     This->updateStateBlock = This->stateBlock;
 
     This->render_offscreen = isTexture;
+    if (This->depth_copy_state != WINED3D_DCS_NO_COPY) {
+        This->depth_copy_state = WINED3D_DCS_COPY;
+    }
     This->last_was_rhw = FALSE;
     This->proj_valid = FALSE;
     IWineD3DDevice_GetRenderState((IWineD3DDevice*) This, WINED3DRS_CULLMODE, &cullMode);
diff --git a/dlls/wined3d/drawprim.c b/dlls/wined3d/drawprim.c
index 4a1bd8e..8ddb017 100644
--- a/dlls/wined3d/drawprim.c
+++ b/dlls/wined3d/drawprim.c
@@ -5,6 +5,7 @@
  * Copyright 2002-2004 Raphael Junqueira
  * Copyright 2004 Christian Costa
  * Copyright 2005 Oliver Stieber
+ * Copyright 2006 Henri Verbeet
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -2088,6 +2089,196 @@ static void check_fbo_status(IWineD3DDev
     }
 }
 
+static GLuint create_arb_blt_vertex_program(IWineD3DDevice *iface) {
+    IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+
+    GLuint program_id = 0;
+    const char *blt_vprogram =
+        "!!ARBvp1.0\n"
+        "PARAM c[1] = { { 1, 0.5 } };\n"
+        "MOV result.position, vertex.position;\n"
+        "MOV result.color, c[0].x;\n"
+        "MAD result.texcoord[0].y, -vertex.position, c[0], c[0];\n"
+        "MAD result.texcoord[0].x, vertex.position, c[0].y, c[0].y;\n"
+        "END\n";
+
+    GL_EXTCALL(glGenProgramsARB(1, &program_id));
+    GL_EXTCALL(glBindProgramARB(GL_VERTEX_PROGRAM_ARB, program_id));
+    GL_EXTCALL(glProgramStringARB(GL_VERTEX_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(blt_vprogram), blt_vprogram));
+
+    if (glGetError() == GL_INVALID_OPERATION) {
+        GLint pos;
+        glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &pos);
+        FIXME("Vertex program error at position %d: %s\n", pos,
+            debugstr_a((const char *)glGetString(GL_PROGRAM_ERROR_STRING_ARB)));
+    }
+
+    return program_id;
+}
+
+static GLuint create_arb_blt_fragment_program(IWineD3DDevice *iface) {
+    IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+
+    GLuint program_id = 0;
+    const char *blt_fprogram =
+        "!!ARBfp1.0\n"
+        "TEMP R0;\n"
+        "TEX R0.x, fragment.texcoord[0], texture[0], 2D;\n"
+        "MOV result.depth.z, R0.x;\n"
+        "END\n";
+
+    GL_EXTCALL(glGenProgramsARB(1, &program_id));
+    GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, program_id));
+    GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(blt_fprogram), blt_fprogram));
+
+    if (glGetError() == GL_INVALID_OPERATION) {
+        GLint pos;
+        glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &pos);
+        FIXME("Fragment program error at position %d: %s\n", pos,
+            debugstr_a((const char *)glGetString(GL_PROGRAM_ERROR_STRING_ARB)));
+    }
+
+    return program_id;
+}
+
+static GLhandleARB create_glsl_blt_shader(IWineD3DDevice *iface) {
+    IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+
+    GLhandleARB program_id;
+    GLhandleARB vshader_id, pshader_id;
+    const char *blt_vshader[] = {
+        "void main(void)\n"
+        "{\n"
+        "    gl_Position = gl_Vertex;\n"
+        "    gl_FrontColor = vec4(1.0);\n"
+        "    gl_TexCoord[0].x = (gl_Vertex.x * 0.5) + 0.5;\n"
+        "    gl_TexCoord[0].y = (-gl_Vertex.y * 0.5) + 0.5;\n"
+        "}\n"
+    };
+
+    const char *blt_pshader[] = {
+        "uniform sampler2D sampler;\n"
+        "void main(void)\n"
+        "{\n"
+        "    gl_FragDepth = texture2D(sampler, gl_TexCoord[0].xy).x;\n"
+        "}\n"
+    };
+
+    vshader_id = GL_EXTCALL(glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB));
+    GL_EXTCALL(glShaderSourceARB(vshader_id, 1, blt_vshader, NULL));
+    GL_EXTCALL(glCompileShaderARB(vshader_id));
+
+    pshader_id = GL_EXTCALL(glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB));
+    GL_EXTCALL(glShaderSourceARB(pshader_id, 1, blt_pshader, NULL));
+    GL_EXTCALL(glCompileShaderARB(pshader_id));
+
+    program_id = GL_EXTCALL(glCreateProgramObjectARB());
+    GL_EXTCALL(glAttachObjectARB(program_id, vshader_id));
+    GL_EXTCALL(glAttachObjectARB(program_id, pshader_id));
+    GL_EXTCALL(glLinkProgramARB(program_id));
+
+    print_glsl_info_log(&GLINFO_LOCATION, program_id);
+
+    return program_id;
+}
+
+static void depth_blt(IWineD3DDevice *iface, GLuint texture) {
+    IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+    BOOL glsl_mode = This->vs_selected_mode == SHADER_GLSL || This->ps_selected_mode == SHADER_GLSL;
+
+    glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT);
+
+    glDisable(GL_CULL_FACE);
+    glDisable(GL_BLEND);
+    glDisable(GL_ALPHA_TEST);
+    glDisable(GL_STENCIL_TEST);
+    glEnable(GL_DEPTH_TEST);
+    glDepthFunc(GL_ALWAYS);
+
+    GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
+    glBindTexture(GL_TEXTURE_2D, texture);
+    glEnable(GL_TEXTURE_2D);
+
+    if (glsl_mode) {
+        static GLhandleARB program_id = 0;
+        static GLhandleARB loc = -1;
+
+        if (!program_id) {
+            program_id = create_glsl_blt_shader(iface);
+            loc = GL_EXTCALL(glGetUniformLocationARB(program_id, "sampler"));
+        }
+
+        GL_EXTCALL(glUseProgramObjectARB(program_id));
+        GL_EXTCALL(glUniform1iARB(loc, 0));
+    } else {
+        static GLuint vprogram_id = 0;
+        static GLuint fprogram_id = 0;
+
+        if (!vprogram_id) vprogram_id = create_arb_blt_vertex_program(iface);
+        GL_EXTCALL(glBindProgramARB(GL_VERTEX_PROGRAM_ARB, vprogram_id));
+        glEnable(GL_VERTEX_PROGRAM_ARB);
+
+        if (!fprogram_id) fprogram_id = create_arb_blt_fragment_program(iface);
+        GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, fprogram_id));
+        glEnable(GL_FRAGMENT_PROGRAM_ARB);
+    }
+
+    glBegin(GL_TRIANGLE_STRIP);
+    glVertex2f(-1.0f, -1.0f);
+    glVertex2f(1.0f, -1.0f);
+    glVertex2f(-1.0f, 1.0f);
+    glVertex2f(1.0f, 1.0f);
+    glEnd();
+
+    glPopAttrib();
+}
+
+static void depth_copy(IWineD3DDevice *iface) {
+    IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
+    IWineD3DSurfaceImpl *depth_stencil = (IWineD3DSurfaceImpl *)This->depthStencilBuffer;
+
+    /* TODO: Make this work for modes other than FBO */
+    if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
+
+    if (This->render_offscreen) {
+        static GLuint tmp_texture = 0;
+
+        TRACE("Copying onscreen depth buffer to offscreen surface\n");
+
+        if (!tmp_texture) {
+            glGenTextures(1, &tmp_texture);
+        }
+
+        /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
+         * directly on the FBO texture. That's because we need to flip. */
+        GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
+        glBindTexture(GL_TEXTURE_2D, tmp_texture);
+        glCopyTexImage2D(depth_stencil->glDescription.target,
+                depth_stencil->glDescription.level,
+                depth_stencil->glDescription.glFormatInternal,
+                0,
+                0,
+                depth_stencil->currentDesc.Width,
+                depth_stencil->currentDesc.Height,
+                0);
+        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(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, This->fbo));
+        checkGLcall("glBindFramebuffer()");
+        depth_blt(iface, tmp_texture);
+        checkGLcall("depth_blt");
+    } else {
+        TRACE("Copying offscreen surface to onscreen depth buffer\n");
+
+        GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
+        checkGLcall("glBindFramebuffer()");
+        depth_blt(iface, depth_stencil->glDescription.textureName);
+        checkGLcall("depth_blt");
+    }
+}
+
 /* Routine common to the draw primitive and draw indexed primitive routines */
 void drawPrimitive(IWineD3DDevice *iface,
                    int PrimitiveType,
@@ -2115,6 +2306,11 @@ void drawPrimitive(IWineD3DDevice *iface
         check_fbo_status(iface);
     }
 
+    if (This->depth_copy_state == WINED3D_DCS_COPY) {
+        depth_copy(iface);
+    }
+    This->depth_copy_state = WINED3D_DCS_INITIAL;
+
     /* 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_private.h b/dlls/wined3d/wined3d_private.h
index 6a4894c..8b0a315 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -551,6 +551,7 @@ #define                         NEEDS_DI
 
     /* For rendering to a texture using glCopyTexImage */
     BOOL                    render_offscreen;
+    WINED3D_DEPTHCOPYSTATE  depth_copy_state;
     GLuint                  fbo;
 
     /* Cursor management */
diff --git a/dlls/wined3d/wined3d_private_types.h b/dlls/wined3d/wined3d_private_types.h
index f02e980..5531a80 100644
--- a/dlls/wined3d/wined3d_private_types.h
+++ b/dlls/wined3d/wined3d_private_types.h
@@ -23,6 +23,13 @@
 #ifndef __WINE_WINED3D_TYPES_INTERNAL_H
 #define __WINE_WINED3D_TYPES_INTERNAL_H
 
+/* Depth copy state */
+typedef enum {
+    WINED3D_DCS_INITIAL = 0,
+    WINED3D_DCS_COPY    = 1,
+    WINED3D_DCS_NO_COPY = 2
+} WINED3D_DEPTHCOPYSTATE;
+
 /** DCL usage masks **/
 #define WINED3DSP_DCL_USAGE_SHIFT 0
 #define WINED3DSP_DCL_USAGE_MASK  0x0000000f


More information about the wine-patches mailing list