diff --git a/dlls/wined3d/surface.c b/dlls/wined3d/surface.c index 3086e261df..801eb5760d 100644 --- a/dlls/wined3d/surface.c +++ b/dlls/wined3d/surface.c @@ -3459,6 +3459,98 @@ static void surface_blt_fbo(IWineD3DDeviceImpl *device, const WINED3DTEXTUREFILT context_release(context); } +static void wined3d_surface_depth_blt_fbo(IWineD3DDeviceImpl *device, IWineD3DSurfaceImpl *src_surface, + const RECT *src_rect, IWineD3DSurfaceImpl *dst_surface, const RECT *dst_rect) +{ + const struct wined3d_gl_info *gl_info; + struct wined3d_context *context; + DWORD src_mask, dst_mask; + GLbitfield gl_mask; + + TRACE("device %p, src_surface %p, src_rect %s, dst_surface %p, dst_rect %s.\n", + device, src_surface, wine_dbgstr_rect(src_rect), + dst_surface, wine_dbgstr_rect(dst_rect)); + + src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL); + dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL); + + if (src_mask != dst_mask) + { + ERR("Incompatible formats %s and %s.\n", + debug_d3dformat(src_surface->resource.format->id), + debug_d3dformat(dst_surface->resource.format->id)); + return; + } + + if (!src_mask) + { + ERR("Not a depth / stencil format: %s.\n", + debug_d3dformat(src_surface->resource.format->id)); + return; + } + + gl_mask = 0; + if (src_mask & WINED3DFMT_FLAG_DEPTH) + gl_mask |= GL_DEPTH_BUFFER_BIT; + if (src_mask & WINED3DFMT_FLAG_STENCIL) + gl_mask |= GL_STENCIL_BUFFER_BIT; + + /* Make sure the locations are up-to-date. Loading the destination + * surface isn't required if the entire surface is overwritten. */ + surface_load_location(src_surface, SFLAG_INTEXTURE, NULL); + if (!surface_is_full_rect(dst_surface, dst_rect)) + surface_load_location(dst_surface, SFLAG_INTEXTURE, NULL); + + context = context_acquire(device, NULL); + if (!context->valid) + { + context_release(context); + WARN("Invalid context, skipping blit.\n"); + return; + } + + gl_info = context->gl_info; + + ENTER_GL(); + + context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, SFLAG_INTEXTURE); + glReadBuffer(GL_NONE); + checkGLcall("glReadBuffer()"); + + context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, SFLAG_INTEXTURE); + context_set_draw_buffer(context, GL_NONE); + + if (gl_mask & GL_DEPTH_BUFFER_BIT) + { + glDepthMask(GL_TRUE); + IWineD3DDeviceImpl_MarkStateDirty(device, STATE_RENDER(WINED3DRS_ZWRITEENABLE)); + } + if (gl_mask & GL_STENCIL_BUFFER_BIT) + { + if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE]) + { + glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT); + IWineD3DDeviceImpl_MarkStateDirty(device, STATE_RENDER(WINED3DRS_TWOSIDEDSTENCILMODE)); + } + glStencilMask(~0U); + IWineD3DDeviceImpl_MarkStateDirty(device, STATE_RENDER(WINED3DRS_STENCILWRITEMASK)); + } + + glDisable(GL_SCISSOR_TEST); + IWineD3DDeviceImpl_MarkStateDirty(device, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE)); + + gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, + dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST); + checkGLcall("glBlitFramebuffer()"); + + LEAVE_GL(); + + if (wined3d_settings.strict_draw_ordering) + wglFlush(); /* Flush to ensure ordering across contexts. */ + + context_release(context); +} + static void surface_blt_to_drawable(IWineD3DDeviceImpl *device, WINED3DTEXTUREFILTERTYPE filter, BOOL color_key, IWineD3DSurfaceImpl *src_surface, const RECT *src_rect_in, @@ -3912,6 +4004,25 @@ static HRESULT wined3d_surface_depth_fill(IWineD3DSurfaceImpl *surface, const RE return blitter->depth_fill(device, surface, rect, depth); } +static HRESULT wined3d_surface_depth_blt(IWineD3DSurfaceImpl *src_surface, const RECT *src_rect, + IWineD3DSurfaceImpl *dst_surface, const RECT *dst_rect) +{ + IWineD3DDeviceImpl *device = src_surface->resource.device; + + if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT, + src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format, + dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format)) + return WINED3DERR_INVALIDCALL; + + wined3d_surface_depth_blt_fbo(device, src_surface, src_rect, dst_surface, dst_rect); + + surface_modify_ds_location(dst_surface, SFLAG_DS_OFFSCREEN, + dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy); + surface_modify_location(dst_surface, SFLAG_INDRAWABLE, TRUE); + + return WINED3D_OK; +} + /* Do not call while under the GL lock. */ static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *src_surface, const RECT *SrcRect, DWORD flags, @@ -3920,6 +4031,7 @@ static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface; IWineD3DSurfaceImpl *src = (IWineD3DSurfaceImpl *)src_surface; IWineD3DDeviceImpl *device = This->resource.device; + DWORD src_ds_flags, dst_ds_flags; TRACE("iface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n", iface, wine_dbgstr_rect(DestRect), src_surface, wine_dbgstr_rect(SrcRect), @@ -3932,8 +4044,13 @@ static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT return WINEDDERR_SURFACEBUSY; } - if (This->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL) - || (src && src->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))) + dst_ds_flags = This->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL); + if (src) + src_ds_flags = src->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL); + else + src_ds_flags = 0; + + if (src_ds_flags || dst_ds_flags) { if (flags & WINEDDBLT_DEPTHFILL) { @@ -3952,6 +4069,8 @@ static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT } else { + RECT src_rect, dst_rect; + /* Accessing depth / stencil surfaces is supposed to fail while in * a scene, except for fills, which seem to work. */ if (device->inScene) @@ -3960,8 +4079,42 @@ static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT return WINED3DERR_INVALIDCALL; } - FIXME("Depth blits not implemeted.\n"); - return WINED3DERR_INVALIDCALL; + if (src_ds_flags != dst_ds_flags) + { + WARN("Rejecting depth / stencil blit between incompatible formats.\n"); + return WINED3DERR_INVALIDCALL; + } + + if (SrcRect && (SrcRect->top || SrcRect->left + || SrcRect->bottom != src->resource.height + || SrcRect->right != src->resource.width)) + { + WARN("Rejecting depth / stencil blit with invalid source rect %s.\n", + wine_dbgstr_rect(SrcRect)); + return WINED3DERR_INVALIDCALL; + } + + if (DestRect && (DestRect->top || DestRect->left + || DestRect->bottom != This->resource.height + || DestRect->right != This->resource.width)) + { + WARN("Rejecting depth / stencil blit with invalid destination rect %s.\n", + wine_dbgstr_rect(SrcRect)); + return WINED3DERR_INVALIDCALL; + } + + if (src->resource.height != This->resource.height + || src->resource.width != This->resource.width) + { + WARN("Rejecting depth / stencil blit with mismatched surface sizes.\n"); + return WINED3DERR_INVALIDCALL; + } + + surface_get_rect(src, SrcRect, &src_rect); + surface_get_rect(This, DestRect, &dst_rect); + + if (SUCCEEDED(wined3d_surface_depth_blt(src, &src_rect, This, &dst_rect))) + return WINED3D_OK; } } @@ -5025,19 +5178,29 @@ static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer) return FALSE; - /* We only support blitting. Things like color keying / color fill should - * be handled by other blitters. - */ - if (blit_op != WINED3D_BLIT_OP_COLOR_BLIT) - return FALSE; - /* Source and/or destination need to be on the GL side */ if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM) return FALSE; - if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)) - || !((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET))) - return FALSE; + switch (blit_op) + { + case WINED3D_BLIT_OP_COLOR_BLIT: + if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET))) + return FALSE; + if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET))) + return FALSE; + break; + + case WINED3D_BLIT_OP_DEPTH_BLIT: + if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))) + return FALSE; + if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))) + return FALSE; + break; + + default: + return FALSE; + } if (!(src_format->id == dst_format->id || (is_identity_fixup(src_format->color_fixup) diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index f1ce5cfc98..992ede420e 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -1171,6 +1171,7 @@ enum wined3d_blit_op WINED3D_BLIT_OP_COLOR_BLIT, WINED3D_BLIT_OP_COLOR_FILL, WINED3D_BLIT_OP_DEPTH_FILL, + WINED3D_BLIT_OP_DEPTH_BLIT, }; /* Shaders for color conversions in blits. Do not do blit operations while