From ef2c062c09fec6885c5165f6b282dff4f02cb490 Mon Sep 17 00:00:00 2001 From: "H. Verbeet" Date: Fri, 17 Nov 2006 13:24:04 +0100 Subject: [PATCH] wined3d: Allow the depth buffer to be shared between onscreen and offscreen rendering modes. --- 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(+) diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c index ef0e0469d32..ab7ab109bd5 100644 --- a/dlls/wined3d/device.c +++ b/dlls/wined3d/device.c @@ -7006,6 +7006,8 @@ static void set_depth_stencil_fbo(IWineD3DDevice *iface, IWineD3DSurface *depth_ 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(IWineD3DDeviceImpl* This, BOOL isTexture) { 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(IWineD3DDeviceImpl* This, BOOL isTexture) { 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 4a1bd8e45a2..8ddb017bfa6 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(IWineD3DDevice *iface) { } } +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 6a4894c1d1a..8b0a31500e1 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -551,6 +551,7 @@ typedef struct IWineD3DDeviceImpl /* 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 f02e980d9bb..5531a80252a 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