xref: /reactos/dll/directx/wine/d3dx9_36/render.c (revision c2c66aff)
1 /*
2  * Copyright (C) 2012 Józef Kucia
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  *
18  */
19 
20 #include "d3dx9_36_private.h"
21 
22 struct device_state
23 {
24     DWORD num_render_targets;
25     IDirect3DSurface9 **render_targets;
26     IDirect3DSurface9 *depth_stencil;
27     D3DVIEWPORT9 viewport;
28 };
29 
30 static HRESULT device_state_init(IDirect3DDevice9 *device, struct device_state *state)
31 {
32     HRESULT hr;
33     D3DCAPS9 caps;
34     unsigned int i;
35 
36     hr = IDirect3DDevice9_GetDeviceCaps(device, &caps);
37     if (FAILED(hr)) return hr;
38 
39     state->num_render_targets = caps.NumSimultaneousRTs;
40     state->render_targets = HeapAlloc(GetProcessHeap(), 0,
41         state->num_render_targets * sizeof(IDirect3DSurface9 *));
42     if (!state->render_targets)
43         return E_OUTOFMEMORY;
44 
45     for (i = 0; i < state->num_render_targets; i++)
46         state->render_targets[i] = NULL;
47     state->depth_stencil = NULL;
48     return D3D_OK;
49 }
50 
51 static void device_state_capture(IDirect3DDevice9 *device, struct device_state *state)
52 {
53     HRESULT hr;
54     unsigned int i;
55 
56     IDirect3DDevice9_GetViewport(device, &state->viewport);
57 
58     for (i = 0; i < state->num_render_targets; i++)
59     {
60         hr = IDirect3DDevice9_GetRenderTarget(device, i, &state->render_targets[i]);
61         if (FAILED(hr)) state->render_targets[i] = NULL;
62     }
63 
64     hr = IDirect3DDevice9_GetDepthStencilSurface(device, &state->depth_stencil);
65     if (FAILED(hr)) state->depth_stencil = NULL;
66 }
67 
68 static void device_state_restore(IDirect3DDevice9 *device, struct device_state *state)
69 {
70     unsigned int i;
71 
72     for (i = 0; i < state->num_render_targets; i++)
73     {
74         IDirect3DDevice9_SetRenderTarget(device, i, state->render_targets[i]);
75         if (state->render_targets[i])
76             IDirect3DSurface9_Release(state->render_targets[i]);
77         state->render_targets[i] = NULL;
78     }
79 
80     IDirect3DDevice9_SetDepthStencilSurface(device, state->depth_stencil);
81     if (state->depth_stencil)
82     {
83         IDirect3DSurface9_Release(state->depth_stencil);
84         state->depth_stencil = NULL;
85     }
86 
87     IDirect3DDevice9_SetViewport(device, &state->viewport);
88 }
89 
90 static void device_state_release(struct device_state *state)
91 {
92     unsigned int i;
93 
94     for (i = 0; i < state->num_render_targets; i++)
95     {
96         if (state->render_targets[i])
97             IDirect3DSurface9_Release(state->render_targets[i]);
98     }
99 
100     HeapFree(GetProcessHeap(), 0, state->render_targets);
101 
102     if (state->depth_stencil) IDirect3DSurface9_Release(state->depth_stencil);
103 }
104 
105 struct render_to_surface
106 {
107     ID3DXRenderToSurface ID3DXRenderToSurface_iface;
108     LONG ref;
109 
110     IDirect3DDevice9 *device;
111     D3DXRTS_DESC desc;
112 
113     IDirect3DSurface9 *dst_surface;
114 
115     IDirect3DSurface9 *render_target;
116     IDirect3DSurface9 *depth_stencil;
117 
118     struct device_state previous_state;
119 };
120 
121 static inline struct render_to_surface *impl_from_ID3DXRenderToSurface(ID3DXRenderToSurface *iface)
122 {
123     return CONTAINING_RECORD(iface, struct render_to_surface, ID3DXRenderToSurface_iface);
124 }
125 
126 static HRESULT WINAPI D3DXRenderToSurface_QueryInterface(ID3DXRenderToSurface *iface,
127                                                          REFIID riid,
128                                                          void **out)
129 {
130     TRACE("iface %p, riid %s, out %p\n", iface, debugstr_guid(riid), out);
131 
132     if (IsEqualGUID(riid, &IID_ID3DXRenderToSurface)
133             || IsEqualGUID(riid, &IID_IUnknown))
134     {
135         IUnknown_AddRef(iface);
136         *out = iface;
137         return S_OK;
138     }
139 
140     WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid));
141 
142     *out = NULL;
143     return E_NOINTERFACE;
144 }
145 
146 static ULONG WINAPI D3DXRenderToSurface_AddRef(ID3DXRenderToSurface *iface)
147 {
148     struct render_to_surface *render = impl_from_ID3DXRenderToSurface(iface);
149     ULONG ref = InterlockedIncrement(&render->ref);
150 
151     TRACE("%p increasing refcount to %u\n", iface, ref);
152 
153     return ref;
154 }
155 
156 static ULONG WINAPI D3DXRenderToSurface_Release(ID3DXRenderToSurface *iface)
157 {
158     struct render_to_surface *render = impl_from_ID3DXRenderToSurface(iface);
159     ULONG ref = InterlockedDecrement(&render->ref);
160 
161     TRACE("%p decreasing refcount to %u\n", iface, ref);
162 
163     if (!ref)
164     {
165         if (render->dst_surface) IDirect3DSurface9_Release(render->dst_surface);
166 
167         if (render->render_target) IDirect3DSurface9_Release(render->render_target);
168         if (render->depth_stencil) IDirect3DSurface9_Release(render->depth_stencil);
169 
170         device_state_release(&render->previous_state);
171 
172         IDirect3DDevice9_Release(render->device);
173 
174         HeapFree(GetProcessHeap(), 0, render);
175     }
176 
177     return ref;
178 }
179 
180 static HRESULT WINAPI D3DXRenderToSurface_GetDevice(ID3DXRenderToSurface *iface,
181                                                     IDirect3DDevice9 **device)
182 {
183     struct render_to_surface *render = impl_from_ID3DXRenderToSurface(iface);
184 
185     TRACE("(%p)->(%p)\n", iface, device);
186 
187     if (!device) return D3DERR_INVALIDCALL;
188 
189     IDirect3DDevice9_AddRef(render->device);
190     *device = render->device;
191     return D3D_OK;
192 }
193 
194 static HRESULT WINAPI D3DXRenderToSurface_GetDesc(ID3DXRenderToSurface *iface,
195                                                   D3DXRTS_DESC *desc)
196 {
197     struct render_to_surface *render = impl_from_ID3DXRenderToSurface(iface);
198 
199     TRACE("(%p)->(%p)\n", iface, desc);
200 
201     if (!desc) return D3DERR_INVALIDCALL;
202 
203     *desc = render->desc;
204     return D3D_OK;
205 }
206 
207 static HRESULT WINAPI D3DXRenderToSurface_BeginScene(ID3DXRenderToSurface *iface,
208                                                      IDirect3DSurface9 *surface,
209                                                      const D3DVIEWPORT9 *viewport)
210 {
211     struct render_to_surface *render = impl_from_ID3DXRenderToSurface(iface);
212     unsigned int i;
213     IDirect3DDevice9 *device;
214     D3DSURFACE_DESC surface_desc;
215     HRESULT hr = D3DERR_INVALIDCALL;
216     D3DMULTISAMPLE_TYPE multi_sample_type = D3DMULTISAMPLE_NONE;
217     DWORD multi_sample_quality = 0;
218 
219     TRACE("(%p)->(%p, %p)\n", iface, surface, viewport);
220 
221     if (!surface || render->dst_surface) return D3DERR_INVALIDCALL;
222 
223     IDirect3DSurface9_GetDesc(surface, &surface_desc);
224     if (surface_desc.Format != render->desc.Format
225             || surface_desc.Width != render->desc.Width
226             || surface_desc.Height != render->desc.Height)
227         return D3DERR_INVALIDCALL;
228 
229     if (viewport)
230     {
231         if (viewport->X > render->desc.Width || viewport->Y > render->desc.Height
232                 || viewport->X + viewport->Width > render->desc.Width
233                 || viewport->Y + viewport->Height > render->desc.Height)
234             return D3DERR_INVALIDCALL;
235 
236         if (!(surface_desc.Usage & D3DUSAGE_RENDERTARGET)
237                 && (viewport->X != 0 || viewport->Y != 0
238                 || viewport->Width != render->desc.Width
239                 || viewport->Height != render->desc.Height))
240             return D3DERR_INVALIDCALL;
241     }
242 
243     device = render->device;
244 
245     device_state_capture(device, &render->previous_state);
246 
247     /* prepare for rendering to surface */
248     for (i = 1; i < render->previous_state.num_render_targets; i++)
249         IDirect3DDevice9_SetRenderTarget(device, i, NULL);
250 
251     if (surface_desc.Usage & D3DUSAGE_RENDERTARGET)
252     {
253         hr = IDirect3DDevice9_SetRenderTarget(device, 0, surface);
254         multi_sample_type = surface_desc.MultiSampleType;
255         multi_sample_quality = surface_desc.MultiSampleQuality;
256     }
257     else
258     {
259         hr = IDirect3DDevice9_CreateRenderTarget(device, render->desc.Width, render->desc.Height,
260                 render->desc.Format, multi_sample_type, multi_sample_quality, FALSE,
261                 &render->render_target, NULL);
262         if (FAILED(hr)) goto cleanup;
263         hr = IDirect3DDevice9_SetRenderTarget(device, 0, render->render_target);
264     }
265 
266     if (FAILED(hr)) goto cleanup;
267 
268     if (render->desc.DepthStencil)
269     {
270         hr = IDirect3DDevice9_CreateDepthStencilSurface(device, render->desc.Width, render->desc.Height,
271                 render->desc.DepthStencilFormat, multi_sample_type, multi_sample_quality, TRUE,
272                 &render->depth_stencil, NULL);
273     }
274     else render->depth_stencil = NULL;
275 
276     if (FAILED(hr)) goto cleanup;
277 
278     hr = IDirect3DDevice9_SetDepthStencilSurface(device, render->depth_stencil);
279     if (FAILED(hr)) goto cleanup;
280 
281     if (viewport) IDirect3DDevice9_SetViewport(device, viewport);
282 
283     IDirect3DSurface9_AddRef(surface);
284     render->dst_surface = surface;
285     return IDirect3DDevice9_BeginScene(device);
286 
287 cleanup:
288     device_state_restore(device, &render->previous_state);
289 
290     if (render->dst_surface) IDirect3DSurface9_Release(render->dst_surface);
291     render->dst_surface = NULL;
292 
293     if (render->render_target) IDirect3DSurface9_Release(render->render_target);
294     render->render_target = NULL;
295     if (render->depth_stencil) IDirect3DSurface9_Release(render->depth_stencil);
296     render->depth_stencil = NULL;
297 
298     return hr;
299 }
300 
301 static HRESULT WINAPI D3DXRenderToSurface_EndScene(ID3DXRenderToSurface *iface,
302                                                    DWORD filter)
303 {
304     struct render_to_surface *render = impl_from_ID3DXRenderToSurface(iface);
305     HRESULT hr;
306 
307     TRACE("(%p)->(%#x)\n", iface, filter);
308 
309     if (!render->dst_surface) return D3DERR_INVALIDCALL;
310 
311     hr = IDirect3DDevice9_EndScene(render->device);
312 
313     /* copy render target data to destination surface, if needed */
314     if (render->render_target)
315     {
316         hr = D3DXLoadSurfaceFromSurface(render->dst_surface, NULL, NULL,
317                 render->render_target, NULL, NULL, filter, 0);
318         if (FAILED(hr)) ERR("Copying render target data to surface failed %#x\n", hr);
319     }
320 
321     device_state_restore(render->device, &render->previous_state);
322 
323     /* release resources */
324     if (render->render_target)
325     {
326         IDirect3DSurface9_Release(render->render_target);
327         render->render_target = NULL;
328     }
329 
330     if (render->depth_stencil)
331     {
332         IDirect3DSurface9_Release(render->depth_stencil);
333         render->depth_stencil = NULL;
334     }
335 
336     IDirect3DSurface9_Release(render->dst_surface);
337     render->dst_surface = NULL;
338 
339     return hr;
340 }
341 
342 static HRESULT WINAPI D3DXRenderToSurface_OnLostDevice(ID3DXRenderToSurface *iface)
343 {
344     FIXME("(%p)->(): stub\n", iface);
345     return D3D_OK;
346 }
347 
348 static HRESULT WINAPI D3DXRenderToSurface_OnResetDevice(ID3DXRenderToSurface *iface)
349 {
350     FIXME("(%p)->(): stub\n", iface);
351     return D3D_OK;
352 }
353 
354 static const ID3DXRenderToSurfaceVtbl render_to_surface_vtbl =
355 {
356     /* IUnknown methods */
357     D3DXRenderToSurface_QueryInterface,
358     D3DXRenderToSurface_AddRef,
359     D3DXRenderToSurface_Release,
360     /* ID3DXRenderToSurface methods */
361     D3DXRenderToSurface_GetDevice,
362     D3DXRenderToSurface_GetDesc,
363     D3DXRenderToSurface_BeginScene,
364     D3DXRenderToSurface_EndScene,
365     D3DXRenderToSurface_OnLostDevice,
366     D3DXRenderToSurface_OnResetDevice
367 };
368 
369 HRESULT WINAPI D3DXCreateRenderToSurface(IDirect3DDevice9 *device,
370                                          UINT width,
371                                          UINT height,
372                                          D3DFORMAT format,
373                                          BOOL depth_stencil,
374                                          D3DFORMAT depth_stencil_format,
375                                          ID3DXRenderToSurface **out)
376 {
377     HRESULT hr;
378     struct render_to_surface *render;
379 
380     TRACE("(%p, %u, %u, %#x, %d, %#x, %p)\n", device, width, height, format,
381             depth_stencil, depth_stencil_format, out);
382 
383     if (!device || !out) return D3DERR_INVALIDCALL;
384 
385     render = HeapAlloc(GetProcessHeap(), 0, sizeof(struct render_to_surface));
386     if (!render) return E_OUTOFMEMORY;
387 
388     render->ID3DXRenderToSurface_iface.lpVtbl = &render_to_surface_vtbl;
389     render->ref = 1;
390 
391     render->desc.Width = width;
392     render->desc.Height = height;
393     render->desc.Format = format;
394     render->desc.DepthStencil = depth_stencil;
395     render->desc.DepthStencilFormat = depth_stencil_format;
396 
397     render->dst_surface = NULL;
398     render->render_target = NULL;
399     render->depth_stencil = NULL;
400 
401     hr = device_state_init(device, &render->previous_state);
402     if (FAILED(hr))
403     {
404         HeapFree(GetProcessHeap(), 0, render);
405         return hr;
406     }
407 
408     IDirect3DDevice9_AddRef(device);
409     render->device = device;
410 
411     *out = &render->ID3DXRenderToSurface_iface;
412     return D3D_OK;
413 }
414 
415 
416 enum render_state
417 {
418     INITIAL,
419 
420     CUBE_BEGIN,
421     CUBE_FACE
422 };
423 
424 struct render_to_envmap
425 {
426     ID3DXRenderToEnvMap ID3DXRenderToEnvMap_iface;
427     LONG ref;
428 
429     IDirect3DDevice9 *device;
430     D3DXRTE_DESC desc;
431 
432     enum render_state state;
433     struct device_state previous_device_state;
434 
435     D3DCUBEMAP_FACES face;
436     DWORD filter;
437 
438     IDirect3DSurface9 *render_target;
439     IDirect3DSurface9 *depth_stencil;
440 
441     IDirect3DCubeTexture9 *dst_cube_texture;
442 };
443 
444 static void copy_render_target_to_cube_texture_face(IDirect3DCubeTexture9 *cube_texture,
445         D3DCUBEMAP_FACES face, IDirect3DSurface9 *render_target, DWORD filter)
446 {
447     HRESULT hr;
448     IDirect3DSurface9 *cube_surface;
449 
450     IDirect3DCubeTexture9_GetCubeMapSurface(cube_texture, face, 0, &cube_surface);
451 
452     hr = D3DXLoadSurfaceFromSurface(cube_surface, NULL, NULL, render_target, NULL, NULL, filter, 0);
453     if (FAILED(hr)) ERR("Copying render target data to surface failed %#x\n", hr);
454 
455     IDirect3DSurface9_Release(cube_surface);
456 }
457 
458 static inline struct render_to_envmap *impl_from_ID3DXRenderToEnvMap(ID3DXRenderToEnvMap *iface)
459 {
460     return CONTAINING_RECORD(iface, struct render_to_envmap, ID3DXRenderToEnvMap_iface);
461 }
462 
463 static HRESULT WINAPI D3DXRenderToEnvMap_QueryInterface(ID3DXRenderToEnvMap *iface,
464                                                         REFIID riid,
465                                                         void **out)
466 {
467     TRACE("iface %p, riid %s, out %p\n", iface, debugstr_guid(riid), out);
468 
469     if (IsEqualGUID(riid, &IID_ID3DXRenderToEnvMap)
470             || IsEqualGUID(riid, &IID_IUnknown))
471     {
472         IUnknown_AddRef(iface);
473         *out = iface;
474         return S_OK;
475     }
476 
477     WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid));
478 
479     *out = NULL;
480     return E_NOINTERFACE;
481 }
482 
483 static ULONG WINAPI D3DXRenderToEnvMap_AddRef(ID3DXRenderToEnvMap *iface)
484 {
485     struct render_to_envmap *render = impl_from_ID3DXRenderToEnvMap(iface);
486     ULONG ref = InterlockedIncrement(&render->ref);
487 
488     TRACE("%p increasing refcount to %u\n", iface, ref);
489 
490     return ref;
491 }
492 
493 static ULONG WINAPI D3DXRenderToEnvMap_Release(ID3DXRenderToEnvMap *iface)
494 {
495     struct render_to_envmap *render = impl_from_ID3DXRenderToEnvMap(iface);
496     ULONG ref = InterlockedDecrement(&render->ref);
497 
498     TRACE("%p decreasing refcount to %u\n", iface, ref);
499 
500     if (!ref)
501     {
502         if (render->dst_cube_texture) IDirect3DSurface9_Release(render->dst_cube_texture);
503 
504         if (render->render_target) IDirect3DSurface9_Release(render->render_target);
505         if (render->depth_stencil) IDirect3DSurface9_Release(render->depth_stencil);
506 
507         device_state_release(&render->previous_device_state);
508 
509         IDirect3DDevice9_Release(render->device);
510 
511         HeapFree(GetProcessHeap(), 0, render);
512     }
513 
514     return ref;
515 }
516 
517 static HRESULT WINAPI D3DXRenderToEnvMap_GetDevice(ID3DXRenderToEnvMap *iface,
518                                                    IDirect3DDevice9 **device)
519 {
520     struct render_to_envmap *render = impl_from_ID3DXRenderToEnvMap(iface);
521 
522     TRACE("(%p)->(%p)\n", iface, device);
523 
524     if (!device) return D3DERR_INVALIDCALL;
525 
526     IDirect3DDevice9_AddRef(render->device);
527     *device = render->device;
528     return D3D_OK;
529 }
530 
531 static HRESULT WINAPI D3DXRenderToEnvMap_GetDesc(ID3DXRenderToEnvMap *iface,
532                                                  D3DXRTE_DESC *desc)
533 {
534     struct render_to_envmap *render = impl_from_ID3DXRenderToEnvMap(iface);
535 
536     TRACE("(%p)->(%p)\n", iface, desc);
537 
538     if (!desc) return D3DERR_INVALIDCALL;
539 
540     *desc = render->desc;
541     return D3D_OK;
542 }
543 
544 static HRESULT WINAPI D3DXRenderToEnvMap_BeginCube(ID3DXRenderToEnvMap *iface,
545                                                    IDirect3DCubeTexture9 *texture)
546 {
547     struct render_to_envmap *render = impl_from_ID3DXRenderToEnvMap(iface);
548     HRESULT hr;
549     D3DSURFACE_DESC level_desc;
550 
551     TRACE("(%p)->(%p)\n", iface, texture);
552 
553     if (!texture) return D3DERR_INVALIDCALL;
554 
555     if (render->state != INITIAL) return D3DERR_INVALIDCALL;
556 
557     IDirect3DCubeTexture9_GetLevelDesc(texture, 0, &level_desc);
558     if (level_desc.Format != render->desc.Format || level_desc.Width != render->desc.Size)
559         return D3DERR_INVALIDCALL;
560 
561     if (!(level_desc.Usage & D3DUSAGE_RENDERTARGET))
562     {
563         hr = IDirect3DDevice9_CreateRenderTarget(render->device, level_desc.Width, level_desc.Height,
564                 level_desc.Format, level_desc.MultiSampleType, level_desc.MultiSampleQuality,
565                 TRUE, &render->render_target, NULL);
566         if (FAILED(hr)) goto cleanup;
567         IDirect3DCubeTexture9_GetLevelDesc(texture, 0, &level_desc);
568     }
569 
570     if (render->desc.DepthStencil)
571     {
572         hr = IDirect3DDevice9_CreateDepthStencilSurface(render->device, level_desc.Width, level_desc.Height,
573                 render->desc.DepthStencilFormat, level_desc.MultiSampleType, level_desc.MultiSampleQuality,
574                 TRUE, &render->depth_stencil, NULL);
575         if (FAILED(hr)) goto cleanup;
576     }
577 
578     IDirect3DCubeTexture9_AddRef(texture);
579     render->dst_cube_texture = texture;
580     render->state = CUBE_BEGIN;
581     return D3D_OK;
582 
583 cleanup:
584     if (render->dst_cube_texture) IDirect3DSurface9_Release(render->dst_cube_texture);
585     render->dst_cube_texture = NULL;
586 
587     if (render->render_target) IDirect3DSurface9_Release(render->render_target);
588     render->render_target = NULL;
589     if (render->depth_stencil) IDirect3DSurface9_Release(render->depth_stencil);
590     render->depth_stencil = NULL;
591 
592     return hr;
593 }
594 
595 static HRESULT WINAPI D3DXRenderToEnvMap_BeginSphere(ID3DXRenderToEnvMap *iface,
596                                                      IDirect3DTexture9 *texture)
597 {
598     FIXME("(%p)->(%p): stub\n", iface, texture);
599     return E_NOTIMPL;
600 }
601 
602 static HRESULT WINAPI D3DXRenderToEnvMap_BeginHemisphere(ID3DXRenderToEnvMap *iface,
603                                                          IDirect3DTexture9 *pos_z_texture,
604                                                          IDirect3DTexture9 *neg_z_texture)
605 {
606     FIXME("(%p)->(%p, %p): stub\n", iface, pos_z_texture, neg_z_texture);
607     return E_NOTIMPL;
608 }
609 
610 static HRESULT WINAPI D3DXRenderToEnvMap_BeginParabolic(ID3DXRenderToEnvMap *iface,
611                                                         IDirect3DTexture9 *pos_z_texture,
612                                                         IDirect3DTexture9 *neg_z_texture)
613 {
614     FIXME("(%p)->(%p, %p): stub\n", iface, pos_z_texture, neg_z_texture);
615     return E_NOTIMPL;
616 }
617 
618 static HRESULT WINAPI D3DXRenderToEnvMap_Face(ID3DXRenderToEnvMap *iface,
619                                               D3DCUBEMAP_FACES face,
620                                               DWORD filter)
621 {
622     struct render_to_envmap *render = impl_from_ID3DXRenderToEnvMap(iface);
623     HRESULT hr;
624     unsigned int i;
625 
626     TRACE("(%p)->(%u, %#x)\n", iface, face, filter);
627 
628     if (render->state == CUBE_FACE)
629     {
630         IDirect3DDevice9_EndScene(render->device);
631         if (render->render_target)
632             copy_render_target_to_cube_texture_face(render->dst_cube_texture, render->face,
633                     render->render_target, render->filter);
634 
635         device_state_restore(render->device, &render->previous_device_state);
636 
637         render->state = CUBE_BEGIN;
638     }
639     else if (render->state != CUBE_BEGIN)
640         return D3DERR_INVALIDCALL;
641 
642     device_state_capture(render->device, &render->previous_device_state);
643 
644     for (i = 1; i < render->previous_device_state.num_render_targets; i++)
645         IDirect3DDevice9_SetRenderTarget(render->device, i, NULL);
646 
647     if (!render->render_target)
648     {
649         IDirect3DSurface9 *render_target;
650         IDirect3DCubeTexture9_GetCubeMapSurface(render->dst_cube_texture, face, 0, &render_target);
651         hr = IDirect3DDevice9_SetRenderTarget(render->device, 0, render_target);
652         IDirect3DSurface9_Release(render_target);
653     }
654     else hr = IDirect3DDevice9_SetRenderTarget(render->device, 0, render->render_target);
655 
656     if (FAILED(hr)) goto cleanup;
657 
658     hr = IDirect3DDevice9_SetDepthStencilSurface(render->device, render->depth_stencil);
659     if (FAILED(hr)) goto cleanup;
660 
661     render->state = CUBE_FACE;
662     render->face = face;
663     render->filter = filter;
664     return IDirect3DDevice9_BeginScene(render->device);
665 
666 cleanup:
667     device_state_restore(render->device, &render->previous_device_state);
668     return hr;
669 }
670 
671 static HRESULT WINAPI D3DXRenderToEnvMap_End(ID3DXRenderToEnvMap *iface,
672                                              DWORD filter)
673 {
674     struct render_to_envmap *render = impl_from_ID3DXRenderToEnvMap(iface);
675 
676     TRACE("(%p)->(%#x)\n", iface, filter);
677 
678     if (render->state == INITIAL) return D3DERR_INVALIDCALL;
679 
680     if (render->state == CUBE_FACE)
681     {
682         IDirect3DDevice9_EndScene(render->device);
683         if (render->render_target)
684             copy_render_target_to_cube_texture_face(render->dst_cube_texture, render->face,
685                     render->render_target, render->filter);
686 
687         device_state_restore(render->device, &render->previous_device_state);
688     }
689 
690     D3DXFilterTexture((IDirect3DBaseTexture9 *)render->dst_cube_texture, NULL, 0, filter);
691 
692     if (render->render_target)
693     {
694         IDirect3DSurface9_Release(render->render_target);
695         render->render_target = NULL;
696     }
697 
698     if (render->depth_stencil)
699     {
700         IDirect3DSurface9_Release(render->depth_stencil);
701         render->depth_stencil = NULL;
702     }
703 
704     IDirect3DSurface9_Release(render->dst_cube_texture);
705     render->dst_cube_texture = NULL;
706 
707     render->state = INITIAL;
708     return D3D_OK;
709 }
710 
711 static HRESULT WINAPI D3DXRenderToEnvMap_OnLostDevice(ID3DXRenderToEnvMap *iface)
712 {
713     FIXME("(%p)->(): stub\n", iface);
714     return D3D_OK;
715 }
716 
717 static HRESULT WINAPI D3DXRenderToEnvMap_OnResetDevice(ID3DXRenderToEnvMap *iface)
718 {
719     FIXME("(%p)->(): stub\n", iface);
720     return D3D_OK;
721 }
722 
723 static const ID3DXRenderToEnvMapVtbl render_to_envmap_vtbl =
724 {
725     /* IUnknown methods */
726     D3DXRenderToEnvMap_QueryInterface,
727     D3DXRenderToEnvMap_AddRef,
728     D3DXRenderToEnvMap_Release,
729     /* ID3DXRenderToEnvMap methods */
730     D3DXRenderToEnvMap_GetDevice,
731     D3DXRenderToEnvMap_GetDesc,
732     D3DXRenderToEnvMap_BeginCube,
733     D3DXRenderToEnvMap_BeginSphere,
734     D3DXRenderToEnvMap_BeginHemisphere,
735     D3DXRenderToEnvMap_BeginParabolic,
736     D3DXRenderToEnvMap_Face,
737     D3DXRenderToEnvMap_End,
738     D3DXRenderToEnvMap_OnLostDevice,
739     D3DXRenderToEnvMap_OnResetDevice
740 };
741 
742 HRESULT WINAPI D3DXCreateRenderToEnvMap(IDirect3DDevice9 *device,
743                                         UINT size,
744                                         UINT mip_levels,
745                                         D3DFORMAT format,
746                                         BOOL depth_stencil,
747                                         D3DFORMAT depth_stencil_format,
748                                         ID3DXRenderToEnvMap **out)
749 {
750     HRESULT hr;
751     struct render_to_envmap *render;
752 
753     TRACE("(%p, %u, %u, %#x, %d, %#x, %p)\n", device, size, mip_levels,
754             format, depth_stencil, depth_stencil_format, out);
755 
756     if (!device || !out) return D3DERR_INVALIDCALL;
757 
758     hr = D3DXCheckTextureRequirements(device, &size, &size, &mip_levels,
759             D3DUSAGE_RENDERTARGET, &format, D3DPOOL_DEFAULT);
760     if (FAILED(hr)) return hr;
761 
762     render = HeapAlloc(GetProcessHeap(), 0, sizeof(struct render_to_envmap));
763     if (!render) return E_OUTOFMEMORY;
764 
765     render->ID3DXRenderToEnvMap_iface.lpVtbl = &render_to_envmap_vtbl;
766     render->ref = 1;
767 
768     render->desc.Size = size;
769     render->desc.MipLevels = mip_levels;
770     render->desc.Format = format;
771     render->desc.DepthStencil = depth_stencil;
772     render->desc.DepthStencilFormat = depth_stencil_format;
773 
774     render->state = INITIAL;
775     render->render_target = NULL;
776     render->depth_stencil = NULL;
777     render->dst_cube_texture = NULL;
778 
779     hr = device_state_init(device, &render->previous_device_state);
780     if (FAILED(hr))
781     {
782         HeapFree(GetProcessHeap(), 0, render);
783         return hr;
784     }
785 
786     IDirect3DDevice9_AddRef(device);
787     render->device = device;
788 
789     *out = &render->ID3DXRenderToEnvMap_iface;
790     return D3D_OK;
791 }
792