This allows us to use IDirect3DDevice9_StretchRect and avoid GPU synchronization. It massively improves performance in Dead Space 1 which calls LoadSurfaceFromSurface every frame before presenting.
Signed-off-by: Robin Kertels robin.kertels@gmail.com --- dlls/d3dx9_36/surface.c | 100 +++++++++++++++++++++++++++++++--------- 1 file changed, 78 insertions(+), 22 deletions(-)
diff --git a/dlls/d3dx9_36/surface.c b/dlls/d3dx9_36/surface.c index a5143cbee4..c351fe58cd 100644 --- a/dlls/d3dx9_36/surface.c +++ b/dlls/d3dx9_36/surface.c @@ -2142,10 +2142,13 @@ HRESULT WINAPI D3DXLoadSurfaceFromSurface(IDirect3DSurface9 *dst_surface, const PALETTEENTRY *dst_palette, const RECT *dst_rect, IDirect3DSurface9 *src_surface, const PALETTEENTRY *src_palette, const RECT *src_rect, DWORD filter, D3DCOLOR color_key) { + const struct pixel_format_desc *srcformatdesc, *destformatdesc; + struct volume src_size, dst_size; + RECT dst_rect_temp; IDirect3DSurface9 *temp_surface; D3DTEXTUREFILTERTYPE d3d_filter; IDirect3DDevice9 *device; - D3DSURFACE_DESC src_desc; + D3DSURFACE_DESC src_desc, dst_desc; D3DLOCKED_RECT lock; HRESULT hr; RECT s; @@ -2158,25 +2161,86 @@ HRESULT WINAPI D3DXLoadSurfaceFromSurface(IDirect3DSurface9 *dst_surface, if (!dst_surface || !src_surface) return D3DERR_INVALIDCALL;
+ + IDirect3DSurface9_GetDesc(src_surface, &src_desc); + + if (!src_rect) + { + SetRect(&s, 0, 0, src_desc.Width, src_desc.Height); + src_rect = &s; + } else if (src_rect->left >= src_rect->right + || src_rect->top >= src_rect->bottom) + { + WARN("Invalid src_rect.\n"); + return E_FAIL; + } + if (!dst_palette && !src_palette && !color_key) { - switch (filter) + srcformatdesc = get_format_info(src_desc.Format); + + src_size.width = src_rect->right - src_rect->left; + src_size.height = src_rect->bottom - src_rect->top; + src_size.depth = 1; + + IDirect3DSurface9_GetDesc(dst_surface, &dst_desc); + destformatdesc = get_format_info(dst_desc.Format); + if (!dst_rect) { - case D3DX_FILTER_NONE: - d3d_filter = D3DTEXF_NONE; - break; + dst_rect = &dst_rect_temp; + dst_rect_temp.left = 0; + dst_rect_temp.top = 0; + dst_rect_temp.right = dst_desc.Width; + dst_rect_temp.bottom = dst_desc.Height; + } + else + { + if (dst_rect->left > dst_rect->right || dst_rect->right > dst_desc.Width + || dst_rect->top > dst_rect->bottom || dst_rect->bottom > dst_desc.Height + || dst_rect->left < 0 || dst_rect->top < 0) + { + WARN("Invalid dst_rect specified.\n"); + return D3DERR_INVALIDCALL; + } + if (dst_rect->left == dst_rect->right || dst_rect->top == dst_rect->bottom) + { + WARN("Empty dst_rect specified.\n"); + return D3D_OK; + } + } + + dst_size.width = dst_rect->right - dst_rect->left; + dst_size.height = dst_rect->bottom - dst_rect->top; + dst_size.depth = 1; + + if (src_desc.Format == dst_desc.Format + && dst_size.width == src_size.width + && dst_size.height == src_size.height + && color_key == 0 + && !(src_rect->left & (srcformatdesc->block_width - 1)) + && !(src_rect->top & (srcformatdesc->block_height - 1)) + && !(dst_rect->left & (destformatdesc->block_width - 1)) + && !(dst_rect->top & (destformatdesc->block_height - 1))) { + d3d_filter = D3DTEXF_NONE; + } else { + switch (filter) + { + case D3DX_FILTER_NONE: + d3d_filter = D3DTEXF_NONE; + break;
- case D3DX_FILTER_POINT: - d3d_filter = D3DTEXF_POINT; - break; + case D3DX_FILTER_POINT: + d3d_filter = D3DTEXF_POINT; + break;
- case D3DX_FILTER_LINEAR: - d3d_filter = D3DTEXF_LINEAR; - break; + case D3DX_FILTER_LINEAR: + d3d_filter = D3DTEXF_LINEAR; + break;
- default: - d3d_filter = ~0u; - break; + default: + d3d_filter = ~0u; + break; + } }
if (d3d_filter != ~0u) @@ -2189,14 +2253,6 @@ HRESULT WINAPI D3DXLoadSurfaceFromSurface(IDirect3DSurface9 *dst_surface, } }
- IDirect3DSurface9_GetDesc(src_surface, &src_desc); - - if (!src_rect) - { - SetRect(&s, 0, 0, src_desc.Width, src_desc.Height); - src_rect = &s; - } - if (FAILED(lock_surface(src_surface, NULL, &lock, &temp_surface, FALSE))) return D3DXERR_INVALIDDATA;
On Mon, Jul 6, 2020 at 3:53 PM Robin Kertels robin.kertels@gmail.com wrote:
This allows us to use IDirect3DDevice9_StretchRect and avoid GPU synchronization. It massively improves performance in Dead Space 1 which calls LoadSurfaceFromSurface every frame before presenting.
Signed-off-by: Robin Kertels robin.kertels@gmail.com
Nice! Do you know which surface it's copying around?
dlls/d3dx9_36/surface.c | 100 +++++++++++++++++++++++++++++++--------- 1 file changed, 78 insertions(+), 22 deletions(-)
Patch looks pretty good but there are a couple of edge cases that need tests. Comments inline.
diff --git a/dlls/d3dx9_36/surface.c b/dlls/d3dx9_36/surface.c index a5143cbee4..c351fe58cd 100644 --- a/dlls/d3dx9_36/surface.c +++ b/dlls/d3dx9_36/surface.c @@ -2142,10 +2142,13 @@ HRESULT WINAPI D3DXLoadSurfaceFromSurface(IDirect3DSurface9 *dst_surface, const PALETTEENTRY *dst_palette, const RECT *dst_rect, IDirect3DSurface9 *src_surface, const PALETTEENTRY *src_palette, const RECT *src_rect, DWORD filter, D3DCOLOR color_key) {
- const struct pixel_format_desc *srcformatdesc, *destformatdesc;
- struct volume src_size, dst_size;
- RECT dst_rect_temp; IDirect3DSurface9 *temp_surface; D3DTEXTUREFILTERTYPE d3d_filter; IDirect3DDevice9 *device;
- D3DSURFACE_DESC src_desc;
- D3DSURFACE_DESC src_desc, dst_desc; D3DLOCKED_RECT lock; HRESULT hr; RECT s;
@@ -2158,25 +2161,86 @@ HRESULT WINAPI D3DXLoadSurfaceFromSurface(IDirect3DSurface9 *dst_surface, if (!dst_surface || !src_surface) return D3DERR_INVALIDCALL;
- IDirect3DSurface9_GetDesc(src_surface, &src_desc);
- if (!src_rect)
- {
SetRect(&s, 0, 0, src_desc.Width, src_desc.Height);
src_rect = &s;
- } else if (src_rect->left >= src_rect->right
|| src_rect->top >= src_rect->bottom)
- {
WARN("Invalid src_rect.\n");
return E_FAIL;
- }
It looks like we don't have d3dx9 tests for this. Care to add one?
- if (!dst_palette && !src_palette && !color_key) {
switch (filter)
srcformatdesc = get_format_info(src_desc.Format);
src_size.width = src_rect->right - src_rect->left;
src_size.height = src_rect->bottom - src_rect->top;
src_size.depth = 1;
IDirect3DSurface9_GetDesc(dst_surface, &dst_desc);
destformatdesc = get_format_info(dst_desc.Format);
if (!dst_rect) {
case D3DX_FILTER_NONE:
d3d_filter = D3DTEXF_NONE;
break;
dst_rect = &dst_rect_temp;
dst_rect_temp.left = 0;
dst_rect_temp.top = 0;
dst_rect_temp.right = dst_desc.Width;
dst_rect_temp.bottom = dst_desc.Height;
}
else
{
if (dst_rect->left > dst_rect->right || dst_rect->right > dst_desc.Width
|| dst_rect->top > dst_rect->bottom || dst_rect->bottom > dst_desc.Height
|| dst_rect->left < 0 || dst_rect->top < 0)
{
WARN("Invalid dst_rect specified.\n");
return D3DERR_INVALIDCALL;
}
if (dst_rect->left == dst_rect->right || dst_rect->top == dst_rect->bottom)
{
WARN("Empty dst_rect specified.\n");
return D3D_OK;
}
Same for these.