render.c 24.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * Copyright (C) 2012 Józef Kucia
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 *
 */

20 21 22 23
#include "config.h"
#include "wine/port.h"

#include "d3dx9_private.h"
24 25 26

WINE_DEFAULT_DEBUG_CHANNEL(d3dx);

27
struct device_state
28
{
29 30 31 32 33
    DWORD num_render_targets;
    IDirect3DSurface9 **render_targets;
    IDirect3DSurface9 *depth_stencil;
    D3DVIEWPORT9 viewport;
};
34

35 36 37 38 39
static HRESULT device_state_init(IDirect3DDevice9 *device, struct device_state *state)
{
    HRESULT hr;
    D3DCAPS9 caps;
    unsigned int i;
40

41 42
    hr = IDirect3DDevice9_GetDeviceCaps(device, &caps);
    if (FAILED(hr)) return hr;
43

44 45 46 47 48
    state->num_render_targets = caps.NumSimultaneousRTs;
    state->render_targets = HeapAlloc(GetProcessHeap(), 0,
        state->num_render_targets * sizeof(IDirect3DSurface9 *));
    if (!state->render_targets)
        return E_OUTOFMEMORY;
49

50 51 52 53 54
    for (i = 0; i < state->num_render_targets; i++)
        state->render_targets[i] = NULL;
    state->depth_stencil = NULL;
    return D3D_OK;
}
55

56
static void device_state_capture(IDirect3DDevice9 *device, struct device_state *state)
57
{
58 59 60 61 62 63 64 65 66 67 68 69 70
    HRESULT hr;
    unsigned int i;

    IDirect3DDevice9_GetViewport(device, &state->viewport);

    for (i = 0; i < state->num_render_targets; i++)
    {
        hr = IDirect3DDevice9_GetRenderTarget(device, i, &state->render_targets[i]);
        if (FAILED(hr)) state->render_targets[i] = NULL;
    }

    hr = IDirect3DDevice9_GetDepthStencilSurface(device, &state->depth_stencil);
    if (FAILED(hr)) state->depth_stencil = NULL;
71 72
}

73
static void device_state_restore(IDirect3DDevice9 *device, struct device_state *state)
74 75 76
{
    unsigned int i;

77
    for (i = 0; i < state->num_render_targets; i++)
78
    {
79 80 81 82
        IDirect3DDevice9_SetRenderTarget(device, i, state->render_targets[i]);
        if (state->render_targets[i])
            IDirect3DSurface9_Release(state->render_targets[i]);
        state->render_targets[i] = NULL;
83 84
    }

85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
    IDirect3DDevice9_SetDepthStencilSurface(device, state->depth_stencil);
    if (state->depth_stencil)
    {
        IDirect3DSurface9_Release(state->depth_stencil);
        state->depth_stencil = NULL;
    }

    IDirect3DDevice9_SetViewport(device, &state->viewport);
}

static void device_state_release(struct device_state *state)
{
    unsigned int i;

    for (i = 0; i < state->num_render_targets; i++)
100
    {
101 102
        if (state->render_targets[i])
            IDirect3DSurface9_Release(state->render_targets[i]);
103 104
    }

105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
    HeapFree(GetProcessHeap(), 0, state->render_targets);

    if (state->depth_stencil) IDirect3DSurface9_Release(state->depth_stencil);
}

struct render_to_surface
{
    ID3DXRenderToSurface ID3DXRenderToSurface_iface;
    LONG ref;

    IDirect3DDevice9 *device;
    D3DXRTS_DESC desc;

    IDirect3DSurface9 *dst_surface;

    IDirect3DSurface9 *render_target;
    IDirect3DSurface9 *depth_stencil;

    struct device_state previous_state;
};

static inline struct render_to_surface *impl_from_ID3DXRenderToSurface(ID3DXRenderToSurface *iface)
{
    return CONTAINING_RECORD(iface, struct render_to_surface, ID3DXRenderToSurface_iface);
129 130
}

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
static HRESULT WINAPI D3DXRenderToSurface_QueryInterface(ID3DXRenderToSurface *iface,
                                                         REFIID riid,
                                                         void **out)
{
    TRACE("iface %p, riid %s, out %p\n", iface, debugstr_guid(riid), out);

    if (IsEqualGUID(riid, &IID_ID3DXRenderToSurface)
            || IsEqualGUID(riid, &IID_IUnknown))
    {
        IUnknown_AddRef(iface);
        *out = iface;
        return S_OK;
    }

    WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid));

    *out = NULL;
    return E_NOINTERFACE;
}

static ULONG WINAPI D3DXRenderToSurface_AddRef(ID3DXRenderToSurface *iface)
{
    struct render_to_surface *render = impl_from_ID3DXRenderToSurface(iface);
    ULONG ref = InterlockedIncrement(&render->ref);

    TRACE("%p increasing refcount to %u\n", iface, ref);

    return ref;
}

static ULONG WINAPI D3DXRenderToSurface_Release(ID3DXRenderToSurface *iface)
{
    struct render_to_surface *render = impl_from_ID3DXRenderToSurface(iface);
    ULONG ref = InterlockedDecrement(&render->ref);

    TRACE("%p decreasing refcount to %u\n", iface, ref);

    if (!ref)
    {
170 171 172 173 174
        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);

175
        device_state_release(&render->previous_state);
176

177
        IDirect3DDevice9_Release(render->device);
178

179 180 181 182 183 184 185 186 187
        HeapFree(GetProcessHeap(), 0, render);
    }

    return ref;
}

static HRESULT WINAPI D3DXRenderToSurface_GetDevice(ID3DXRenderToSurface *iface,
                                                    IDirect3DDevice9 **device)
{
188 189 190 191 192 193 194 195 196
    struct render_to_surface *render = impl_from_ID3DXRenderToSurface(iface);

    TRACE("(%p)->(%p)\n", iface, device);

    if (!device) return D3DERR_INVALIDCALL;

    IDirect3DDevice9_AddRef(render->device);
    *device = render->device;
    return D3D_OK;
197 198 199 200 201
}

static HRESULT WINAPI D3DXRenderToSurface_GetDesc(ID3DXRenderToSurface *iface,
                                                  D3DXRTS_DESC *desc)
{
202 203 204 205 206 207 208 209
    struct render_to_surface *render = impl_from_ID3DXRenderToSurface(iface);

    TRACE("(%p)->(%p)\n", iface, desc);

    if (!desc) return D3DERR_INVALIDCALL;

    *desc = render->desc;
    return D3D_OK;
210 211 212 213 214 215
}

static HRESULT WINAPI D3DXRenderToSurface_BeginScene(ID3DXRenderToSurface *iface,
                                                     IDirect3DSurface9 *surface,
                                                     const D3DVIEWPORT9 *viewport)
{
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
    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;

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 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
        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:
293
    device_state_restore(device, &render->previous_state);
294 295 296 297 298 299 300 301 302 303

    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;
304 305 306
}

static HRESULT WINAPI D3DXRenderToSurface_EndScene(ID3DXRenderToSurface *iface,
307
                                                   DWORD filter)
308
{
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
    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);
    }

326
    device_state_restore(render->device, &render->previous_state);
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344

    /* 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;
345 346 347 348 349 350 351 352 353 354 355 356 357 358
}

static HRESULT WINAPI D3DXRenderToSurface_OnLostDevice(ID3DXRenderToSurface *iface)
{
    FIXME("(%p)->(): stub\n", iface);
    return D3D_OK;
}

static HRESULT WINAPI D3DXRenderToSurface_OnResetDevice(ID3DXRenderToSurface *iface)
{
    FIXME("(%p)->(): stub\n", iface);
    return D3D_OK;
}

359
static const ID3DXRenderToSurfaceVtbl render_to_surface_vtbl =
360 361 362 363 364 365 366 367 368 369 370 371 372 373
{
    /* IUnknown methods */
    D3DXRenderToSurface_QueryInterface,
    D3DXRenderToSurface_AddRef,
    D3DXRenderToSurface_Release,
    /* ID3DXRenderToSurface methods */
    D3DXRenderToSurface_GetDevice,
    D3DXRenderToSurface_GetDesc,
    D3DXRenderToSurface_BeginScene,
    D3DXRenderToSurface_EndScene,
    D3DXRenderToSurface_OnLostDevice,
    D3DXRenderToSurface_OnResetDevice
};

374 375 376 377 378 379 380 381
HRESULT WINAPI D3DXCreateRenderToSurface(IDirect3DDevice9 *device,
                                         UINT width,
                                         UINT height,
                                         D3DFORMAT format,
                                         BOOL depth_stencil,
                                         D3DFORMAT depth_stencil_format,
                                         ID3DXRenderToSurface **out)
{
382
    HRESULT hr;
383 384
    struct render_to_surface *render;

385
    TRACE("(%p, %u, %u, %#x, %d, %#x, %p)\n", device, width, height, format,
386 387 388 389
            depth_stencil, depth_stencil_format, out);

    if (!device || !out) return D3DERR_INVALIDCALL;

390 391 392
    render = HeapAlloc(GetProcessHeap(), 0, sizeof(struct render_to_surface));
    if (!render) return E_OUTOFMEMORY;

393
    render->ID3DXRenderToSurface_iface.lpVtbl = &render_to_surface_vtbl;
394 395
    render->ref = 1;

396 397 398 399 400 401
    render->desc.Width = width;
    render->desc.Height = height;
    render->desc.Format = format;
    render->desc.DepthStencil = depth_stencil;
    render->desc.DepthStencilFormat = depth_stencil_format;

402 403 404 405
    render->dst_surface = NULL;
    render->render_target = NULL;
    render->depth_stencil = NULL;

406 407
    hr = device_state_init(device, &render->previous_state);
    if (FAILED(hr))
408 409
    {
        HeapFree(GetProcessHeap(), 0, render);
410
        return hr;
411 412 413 414 415
    }

    IDirect3DDevice9_AddRef(device);
    render->device = device;

416 417
    *out = &render->ID3DXRenderToSurface_iface;
    return D3D_OK;
418
}
419

420

421 422 423 424 425 426 427 428
enum render_state
{
    INITIAL,

    CUBE_BEGIN,
    CUBE_FACE
};

429 430 431 432 433 434 435
struct render_to_envmap
{
    ID3DXRenderToEnvMap ID3DXRenderToEnvMap_iface;
    LONG ref;

    IDirect3DDevice9 *device;
    D3DXRTE_DESC desc;
436 437 438 439 440 441 442 443 444 445 446

    enum render_state state;
    struct device_state previous_device_state;

    D3DCUBEMAP_FACES face;
    DWORD filter;

    IDirect3DSurface9 *render_target;
    IDirect3DSurface9 *depth_stencil;

    IDirect3DCubeTexture9 *dst_cube_texture;
447 448
};

449 450 451 452 453 454 455 456 457 458 459 460 461 462
static void copy_render_target_to_cube_texture_face(IDirect3DCubeTexture9 *cube_texture,
        D3DCUBEMAP_FACES face, IDirect3DSurface9 *render_target, DWORD filter)
{
    HRESULT hr;
    IDirect3DSurface9 *cube_surface;

    IDirect3DCubeTexture9_GetCubeMapSurface(cube_texture, face, 0, &cube_surface);

    hr = D3DXLoadSurfaceFromSurface(cube_surface, NULL, NULL, render_target, NULL, NULL, filter, 0);
    if (FAILED(hr)) ERR("Copying render target data to surface failed %#x\n", hr);

    IDirect3DSurface9_Release(cube_surface);
}

463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
static inline struct render_to_envmap *impl_from_ID3DXRenderToEnvMap(ID3DXRenderToEnvMap *iface)
{
    return CONTAINING_RECORD(iface, struct render_to_envmap, ID3DXRenderToEnvMap_iface);
}

static HRESULT WINAPI D3DXRenderToEnvMap_QueryInterface(ID3DXRenderToEnvMap *iface,
                                                        REFIID riid,
                                                        void **out)
{
    TRACE("iface %p, riid %s, out %p\n", iface, debugstr_guid(riid), out);

    if (IsEqualGUID(riid, &IID_ID3DXRenderToEnvMap)
            || IsEqualGUID(riid, &IID_IUnknown))
    {
        IUnknown_AddRef(iface);
        *out = iface;
        return S_OK;
    }

    WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid));

    *out = NULL;
    return E_NOINTERFACE;
}

static ULONG WINAPI D3DXRenderToEnvMap_AddRef(ID3DXRenderToEnvMap *iface)
{
    struct render_to_envmap *render = impl_from_ID3DXRenderToEnvMap(iface);
    ULONG ref = InterlockedIncrement(&render->ref);

    TRACE("%p increasing refcount to %u\n", iface, ref);

    return ref;
}

static ULONG WINAPI D3DXRenderToEnvMap_Release(ID3DXRenderToEnvMap *iface)
{
    struct render_to_envmap *render = impl_from_ID3DXRenderToEnvMap(iface);
    ULONG ref = InterlockedDecrement(&render->ref);

    TRACE("%p decreasing refcount to %u\n", iface, ref);

    if (!ref)
    {
507 508 509 510 511 512 513
        if (render->dst_cube_texture) IDirect3DSurface9_Release(render->dst_cube_texture);

        if (render->render_target) IDirect3DSurface9_Release(render->render_target);
        if (render->depth_stencil) IDirect3DSurface9_Release(render->depth_stencil);

        device_state_release(&render->previous_device_state);

514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
        IDirect3DDevice9_Release(render->device);

        HeapFree(GetProcessHeap(), 0, render);
    }

    return ref;
}

static HRESULT WINAPI D3DXRenderToEnvMap_GetDevice(ID3DXRenderToEnvMap *iface,
                                                   IDirect3DDevice9 **device)
{
    struct render_to_envmap *render = impl_from_ID3DXRenderToEnvMap(iface);

    TRACE("(%p)->(%p)\n", iface, device);

    if (!device) return D3DERR_INVALIDCALL;

    IDirect3DDevice9_AddRef(render->device);
    *device = render->device;
    return D3D_OK;
}

static HRESULT WINAPI D3DXRenderToEnvMap_GetDesc(ID3DXRenderToEnvMap *iface,
                                                 D3DXRTE_DESC *desc)
{
    struct render_to_envmap *render = impl_from_ID3DXRenderToEnvMap(iface);

    TRACE("(%p)->(%p)\n", iface, desc);

    if (!desc) return D3DERR_INVALIDCALL;

    *desc = render->desc;
    return D3D_OK;
}

static HRESULT WINAPI D3DXRenderToEnvMap_BeginCube(ID3DXRenderToEnvMap *iface,
                                                   IDirect3DCubeTexture9 *texture)
{
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
    struct render_to_envmap *render = impl_from_ID3DXRenderToEnvMap(iface);
    HRESULT hr;
    D3DSURFACE_DESC level_desc;

    TRACE("(%p)->(%p)\n", iface, texture);

    if (!texture) return D3DERR_INVALIDCALL;

    if (render->state != INITIAL) return D3DERR_INVALIDCALL;

    IDirect3DCubeTexture9_GetLevelDesc(texture, 0, &level_desc);
    if (level_desc.Format != render->desc.Format || level_desc.Width != render->desc.Size)
        return D3DERR_INVALIDCALL;

    if (!(level_desc.Usage & D3DUSAGE_RENDERTARGET))
    {
        hr = IDirect3DDevice9_CreateRenderTarget(render->device, level_desc.Width, level_desc.Height,
                level_desc.Format, level_desc.MultiSampleType, level_desc.MultiSampleQuality,
                TRUE, &render->render_target, NULL);
        if (FAILED(hr)) goto cleanup;
        IDirect3DCubeTexture9_GetLevelDesc(texture, 0, &level_desc);
    }

    if (render->desc.DepthStencil)
    {
        hr = IDirect3DDevice9_CreateDepthStencilSurface(render->device, level_desc.Width, level_desc.Height,
                render->desc.DepthStencilFormat, level_desc.MultiSampleType, level_desc.MultiSampleQuality,
                TRUE, &render->depth_stencil, NULL);
        if (FAILED(hr)) goto cleanup;
    }

    IDirect3DCubeTexture9_AddRef(texture);
    render->dst_cube_texture = texture;
    render->state = CUBE_BEGIN;
    return D3D_OK;

cleanup:
    if (render->dst_cube_texture) IDirect3DSurface9_Release(render->dst_cube_texture);
    render->dst_cube_texture = 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;
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
}

static HRESULT WINAPI D3DXRenderToEnvMap_BeginSphere(ID3DXRenderToEnvMap *iface,
                                                     IDirect3DTexture9 *texture)
{
    FIXME("(%p)->(%p): stub\n", iface, texture);
    return E_NOTIMPL;
}

static HRESULT WINAPI D3DXRenderToEnvMap_BeginHemisphere(ID3DXRenderToEnvMap *iface,
                                                         IDirect3DTexture9 *pos_z_texture,
                                                         IDirect3DTexture9 *neg_z_texture)
{
    FIXME("(%p)->(%p, %p): stub\n", iface, pos_z_texture, neg_z_texture);
    return E_NOTIMPL;
}

static HRESULT WINAPI D3DXRenderToEnvMap_BeginParabolic(ID3DXRenderToEnvMap *iface,
                                                        IDirect3DTexture9 *pos_z_texture,
                                                        IDirect3DTexture9 *neg_z_texture)
{
    FIXME("(%p)->(%p, %p): stub\n", iface, pos_z_texture, neg_z_texture);
    return E_NOTIMPL;
}

static HRESULT WINAPI D3DXRenderToEnvMap_Face(ID3DXRenderToEnvMap *iface,
                                              D3DCUBEMAP_FACES face,
                                              DWORD filter)
{
627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673
    struct render_to_envmap *render = impl_from_ID3DXRenderToEnvMap(iface);
    HRESULT hr;
    unsigned int i;

    TRACE("(%p)->(%u, %#x)\n", iface, face, filter);

    if (render->state == CUBE_FACE)
    {
        IDirect3DDevice9_EndScene(render->device);
        if (render->render_target)
            copy_render_target_to_cube_texture_face(render->dst_cube_texture, render->face,
                    render->render_target, render->filter);

        device_state_restore(render->device, &render->previous_device_state);

        render->state = CUBE_BEGIN;
    }
    else if (render->state != CUBE_BEGIN)
        return D3DERR_INVALIDCALL;

    device_state_capture(render->device, &render->previous_device_state);

    for (i = 1; i < render->previous_device_state.num_render_targets; i++)
        IDirect3DDevice9_SetRenderTarget(render->device, i, NULL);

    if (!render->render_target)
    {
        IDirect3DSurface9 *render_target;
        IDirect3DCubeTexture9_GetCubeMapSurface(render->dst_cube_texture, face, 0, &render_target);
        hr = IDirect3DDevice9_SetRenderTarget(render->device, 0, render_target);
        IDirect3DSurface9_Release(render_target);
    }
    else hr = IDirect3DDevice9_SetRenderTarget(render->device, 0, render->render_target);

    if (FAILED(hr)) goto cleanup;

    hr = IDirect3DDevice9_SetDepthStencilSurface(render->device, render->depth_stencil);
    if (FAILED(hr)) goto cleanup;

    render->state = CUBE_FACE;
    render->face = face;
    render->filter = filter;
    return IDirect3DDevice9_BeginScene(render->device);

cleanup:
    device_state_restore(render->device, &render->previous_device_state);
    return hr;
674 675 676 677 678
}

static HRESULT WINAPI D3DXRenderToEnvMap_End(ID3DXRenderToEnvMap *iface,
                                             DWORD filter)
{
679 680 681
    struct render_to_envmap *render = impl_from_ID3DXRenderToEnvMap(iface);

    TRACE("(%p)->(%#x)\n", iface, filter);
682

683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
    if (render->state == INITIAL) return D3DERR_INVALIDCALL;

    if (render->state == CUBE_FACE)
    {
        IDirect3DDevice9_EndScene(render->device);
        if (render->render_target)
            copy_render_target_to_cube_texture_face(render->dst_cube_texture, render->face,
                    render->render_target, render->filter);

        device_state_restore(render->device, &render->previous_device_state);
    }

    D3DXFilterTexture((IDirect3DBaseTexture9 *)render->dst_cube_texture, NULL, 0, filter);

    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_cube_texture);
    render->dst_cube_texture = NULL;

    render->state = INITIAL;
    return D3D_OK;
}
715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746

static HRESULT WINAPI D3DXRenderToEnvMap_OnLostDevice(ID3DXRenderToEnvMap *iface)
{
    FIXME("(%p)->(): stub\n", iface);
    return D3D_OK;
}

static HRESULT WINAPI D3DXRenderToEnvMap_OnResetDevice(ID3DXRenderToEnvMap *iface)
{
    FIXME("(%p)->(): stub\n", iface);
    return D3D_OK;
}

static const ID3DXRenderToEnvMapVtbl render_to_envmap_vtbl =
{
    /* IUnknown methods */
    D3DXRenderToEnvMap_QueryInterface,
    D3DXRenderToEnvMap_AddRef,
    D3DXRenderToEnvMap_Release,
    /* ID3DXRenderToEnvMap methods */
    D3DXRenderToEnvMap_GetDevice,
    D3DXRenderToEnvMap_GetDesc,
    D3DXRenderToEnvMap_BeginCube,
    D3DXRenderToEnvMap_BeginSphere,
    D3DXRenderToEnvMap_BeginHemisphere,
    D3DXRenderToEnvMap_BeginParabolic,
    D3DXRenderToEnvMap_Face,
    D3DXRenderToEnvMap_End,
    D3DXRenderToEnvMap_OnLostDevice,
    D3DXRenderToEnvMap_OnResetDevice
};

747 748 749 750 751 752 753 754
HRESULT WINAPI D3DXCreateRenderToEnvMap(IDirect3DDevice9 *device,
                                        UINT size,
                                        UINT mip_levels,
                                        D3DFORMAT format,
                                        BOOL depth_stencil,
                                        D3DFORMAT depth_stencil_format,
                                        ID3DXRenderToEnvMap **out)
{
755 756 757 758
    HRESULT hr;
    struct render_to_envmap *render;

    TRACE("(%p, %u, %u, %#x, %d, %#x, %p)\n", device, size, mip_levels,
759 760 761 762
            format, depth_stencil, depth_stencil_format, out);

    if (!device || !out) return D3DERR_INVALIDCALL;

763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
    hr = D3DXCheckTextureRequirements(device, &size, &size, &mip_levels,
            D3DUSAGE_RENDERTARGET, &format, D3DPOOL_DEFAULT);
    if (FAILED(hr)) return hr;

    render = HeapAlloc(GetProcessHeap(), 0, sizeof(struct render_to_envmap));
    if (!render) return E_OUTOFMEMORY;

    render->ID3DXRenderToEnvMap_iface.lpVtbl = &render_to_envmap_vtbl;
    render->ref = 1;

    render->desc.Size = size;
    render->desc.MipLevels = mip_levels;
    render->desc.Format = format;
    render->desc.DepthStencil = depth_stencil;
    render->desc.DepthStencilFormat = depth_stencil_format;

779 780 781 782 783 784 785 786 787 788 789 790
    render->state = INITIAL;
    render->render_target = NULL;
    render->depth_stencil = NULL;
    render->dst_cube_texture = NULL;

    hr = device_state_init(device, &render->previous_device_state);
    if (FAILED(hr))
    {
        HeapFree(GetProcessHeap(), 0, render);
        return hr;
    }

791 792 793 794 795
    IDirect3DDevice9_AddRef(device);
    render->device = device;

    *out = &render->ID3DXRenderToEnvMap_iface;
    return D3D_OK;
796
}