diff --git a/dlls/d3dx9_36/render.c b/dlls/d3dx9_36/render.c index 9aa148786b1..35aed933192 100644 --- a/dlls/d3dx9_36/render.c +++ b/dlls/d3dx9_36/render.c @@ -29,6 +29,16 @@ struct render_to_surface IDirect3DDevice9 *device; D3DXRTS_DESC desc; + + IDirect3DSurface9 *dst_surface; + + IDirect3DSurface9 *render_target; + IDirect3DSurface9 *depth_stencil; + + DWORD num_render_targets; + D3DVIEWPORT9 previous_viewport; + IDirect3DSurface9 **previous_render_targets; + IDirect3DSurface9 *previous_depth_stencil; }; static inline struct render_to_surface *impl_from_ID3DXRenderToSurface(ID3DXRenderToSurface *iface) @@ -36,6 +46,28 @@ static inline struct render_to_surface *impl_from_ID3DXRenderToSurface(ID3DXRend return CONTAINING_RECORD(iface, struct render_to_surface, ID3DXRenderToSurface_iface); } +static void restore_previous_device_state(struct render_to_surface *render) +{ + unsigned int i; + + for (i = 0; i < render->num_render_targets; i++) + { + IDirect3DDevice9_SetRenderTarget(render->device, i, render->previous_render_targets[i]); + if (render->previous_render_targets[i]) + IDirect3DSurface9_Release(render->previous_render_targets[i]); + render->previous_render_targets[i] = NULL; + } + + IDirect3DDevice9_SetDepthStencilSurface(render->device, render->previous_depth_stencil); + if (render->previous_depth_stencil) + { + IDirect3DSurface9_Release(render->previous_depth_stencil); + render->previous_depth_stencil = NULL; + } + + IDirect3DDevice9_SetViewport(render->device, &render->previous_viewport); +} + static HRESULT WINAPI D3DXRenderToSurface_QueryInterface(ID3DXRenderToSurface *iface, REFIID riid, void **out) @@ -70,12 +102,29 @@ static ULONG WINAPI D3DXRenderToSurface_Release(ID3DXRenderToSurface *iface) { struct render_to_surface *render = impl_from_ID3DXRenderToSurface(iface); ULONG ref = InterlockedDecrement(&render->ref); + unsigned int i; TRACE("%p decreasing refcount to %u\n", iface, ref); if (!ref) { + if (render->dst_surface) IDirect3DSurface9_Release(render->dst_surface); + + if (render->render_target) IDirect3DSurface9_Release(render->render_target); + if (render->depth_stencil) IDirect3DSurface9_Release(render->depth_stencil); + + for (i = 0; i < render->num_render_targets; i++) + { + if (render->previous_render_targets[i]) + IDirect3DSurface9_Release(render->previous_render_targets[i]); + } + + HeapFree(GetProcessHeap(), 0, render->previous_render_targets); + + if (render->previous_depth_stencil) IDirect3DSurface9_Release(render->previous_depth_stencil); + IDirect3DDevice9_Release(render->device); + HeapFree(GetProcessHeap(), 0, render); } @@ -113,15 +162,145 @@ static HRESULT WINAPI D3DXRenderToSurface_BeginScene(ID3DXRenderToSurface *iface IDirect3DSurface9 *surface, const D3DVIEWPORT9 *viewport) { - FIXME("(%p)->(%p, %p): stub\n", iface, surface, viewport); - return E_NOTIMPL; + struct render_to_surface *render = impl_from_ID3DXRenderToSurface(iface); + unsigned int i; + IDirect3DDevice9 *device; + D3DSURFACE_DESC surface_desc; + HRESULT hr = D3DERR_INVALIDCALL; + D3DMULTISAMPLE_TYPE multi_sample_type = D3DMULTISAMPLE_NONE; + DWORD multi_sample_quality = 0; + + TRACE("(%p)->(%p, %p)\n", iface, surface, viewport); + + if (!surface || render->dst_surface) return D3DERR_INVALIDCALL; + + IDirect3DSurface9_GetDesc(surface, &surface_desc); + if (surface_desc.Format != render->desc.Format + || surface_desc.Width != render->desc.Width + || surface_desc.Height != render->desc.Height) + return D3DERR_INVALIDCALL; + + if (viewport) + { + if (viewport->X > render->desc.Width || viewport->Y > render->desc.Height + || viewport->X + viewport->Width > render->desc.Width + || viewport->Y + viewport->Height > render->desc.Height) + return D3DERR_INVALIDCALL; + + if (!(surface_desc.Usage & D3DUSAGE_RENDERTARGET) + && (viewport->X != 0 || viewport->Y != 0 + || viewport->Width != render->desc.Width + || viewport->Height != render->desc.Height)) + return D3DERR_INVALIDCALL; + } + + device = render->device; + + /* save device state */ + IDirect3DDevice9_GetViewport(device, &render->previous_viewport); + + for (i = 0; i < render->num_render_targets; i++) + { + hr = IDirect3DDevice9_GetRenderTarget(device, i, &render->previous_render_targets[i]); + if (FAILED(hr)) render->previous_render_targets[i] = NULL; + } + + hr = IDirect3DDevice9_GetDepthStencilSurface(device, &render->previous_depth_stencil); + if (FAILED(hr)) render->previous_depth_stencil = NULL; + + /* prepare for rendering to surface */ + for (i = 1; i < render->num_render_targets; i++) + IDirect3DDevice9_SetRenderTarget(device, i, NULL); + + if (surface_desc.Usage & D3DUSAGE_RENDERTARGET) + { + hr = IDirect3DDevice9_SetRenderTarget(device, 0, surface); + multi_sample_type = surface_desc.MultiSampleType; + multi_sample_quality = surface_desc.MultiSampleQuality; + } + else + { + hr = IDirect3DDevice9_CreateRenderTarget(device, render->desc.Width, render->desc.Height, + render->desc.Format, multi_sample_type, multi_sample_quality, FALSE, + &render->render_target, NULL); + if (FAILED(hr)) goto cleanup; + hr = IDirect3DDevice9_SetRenderTarget(device, 0, render->render_target); + } + + if (FAILED(hr)) goto cleanup; + + if (render->desc.DepthStencil) + { + hr = IDirect3DDevice9_CreateDepthStencilSurface(device, render->desc.Width, render->desc.Height, + render->desc.DepthStencilFormat, multi_sample_type, multi_sample_quality, TRUE, + &render->depth_stencil, NULL); + } + else render->depth_stencil = NULL; + + if (FAILED(hr)) goto cleanup; + + hr = IDirect3DDevice9_SetDepthStencilSurface(device, render->depth_stencil); + if (FAILED(hr)) goto cleanup; + + if (viewport) IDirect3DDevice9_SetViewport(device, viewport); + + IDirect3DSurface9_AddRef(surface); + render->dst_surface = surface; + return IDirect3DDevice9_BeginScene(device); + +cleanup: + restore_previous_device_state(render); + + if (render->dst_surface) IDirect3DSurface9_Release(render->dst_surface); + render->dst_surface = NULL; + + if (render->render_target) IDirect3DSurface9_Release(render->render_target); + render->render_target = NULL; + if (render->depth_stencil) IDirect3DSurface9_Release(render->depth_stencil); + render->depth_stencil = NULL; + + return hr; } static HRESULT WINAPI D3DXRenderToSurface_EndScene(ID3DXRenderToSurface *iface, - DWORD mip_filter) + DWORD filter) { - FIXME("(%p)->(%#x): stub\n", iface, mip_filter); - return E_NOTIMPL; + struct render_to_surface *render = impl_from_ID3DXRenderToSurface(iface); + HRESULT hr; + + TRACE("(%p)->(%#x)\n", iface, filter); + + if (!render->dst_surface) return D3DERR_INVALIDCALL; + + hr = IDirect3DDevice9_EndScene(render->device); + + /* copy render target data to destination surface, if needed */ + if (render->render_target) + { + hr = D3DXLoadSurfaceFromSurface(render->dst_surface, NULL, NULL, + render->render_target, NULL, NULL, filter, 0); + if (FAILED(hr)) ERR("Copying render target data to surface failed %#x\n", hr); + } + + restore_previous_device_state(render); + + /* release resources */ + if (render->render_target) + { + IDirect3DSurface9_Release(render->render_target); + render->render_target = NULL; + } + + if (render->depth_stencil) + { + IDirect3DSurface9_Release(render->depth_stencil); + render->depth_stencil = NULL; + } + + IDirect3DSurface9_Release(render->dst_surface); + render->dst_surface = NULL; + + return hr; } static HRESULT WINAPI D3DXRenderToSurface_OnLostDevice(ID3DXRenderToSurface *iface) @@ -159,28 +338,51 @@ HRESULT WINAPI D3DXCreateRenderToSurface(IDirect3DDevice9 *device, D3DFORMAT depth_stencil_format, ID3DXRenderToSurface **out) { + HRESULT hr; + D3DCAPS9 caps; struct render_to_surface *render; + unsigned int i; TRACE("(%p, %u, %u, %#x, %d, %#x, %p)\n", device, width, height, format, depth_stencil, depth_stencil_format, out); if (!device || !out) return D3DERR_INVALIDCALL; + hr = IDirect3DDevice9_GetDeviceCaps(device, &caps); + if (FAILED(hr)) return hr; + render = HeapAlloc(GetProcessHeap(), 0, sizeof(struct render_to_surface)); if (!render) return E_OUTOFMEMORY; render->ID3DXRenderToSurface_iface.lpVtbl = &d3dx_render_to_surface_vtbl; render->ref = 1; - IDirect3DDevice9_AddRef(device); - render->device = device; - render->desc.Width = width; render->desc.Height = height; render->desc.Format = format; render->desc.DepthStencil = depth_stencil; render->desc.DepthStencilFormat = depth_stencil_format; + render->dst_surface = NULL; + render->render_target = NULL; + render->depth_stencil = NULL; + + render->num_render_targets = caps.NumSimultaneousRTs; + render->previous_render_targets = HeapAlloc(GetProcessHeap(), 0, + render->num_render_targets * sizeof(IDirect3DSurface9 *)); + if (!render->previous_render_targets) + { + HeapFree(GetProcessHeap(), 0, render); + return E_OUTOFMEMORY; + } + + for (i = 0; i < render->num_render_targets; i++) + render->previous_render_targets[i] = NULL; + render->previous_depth_stencil = NULL; + + IDirect3DDevice9_AddRef(device); + render->device = device; + *out = &render->ID3DXRenderToSurface_iface; return D3D_OK; }