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