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