swapchain.c 89.4 KB
Newer Older
Oliver Stieber's avatar
Oliver Stieber committed
1
/*
2 3 4 5
 * Copyright 2002-2003 Jason Edmeades
 * Copyright 2002-2003 Raphael Junqueira
 * Copyright 2005 Oliver Stieber
 * Copyright 2007-2008 Stefan Dösinger for CodeWeavers
6
 * Copyright 2011 Henri Verbeet for CodeWeavers
Oliver Stieber's avatar
Oliver Stieber committed
7
 *
8 9 10 11
 * 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.
Oliver Stieber's avatar
Oliver Stieber committed
12
 *
13 14 15 16
 * 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.
Oliver Stieber's avatar
Oliver Stieber committed
17
 *
18 19 20
 * 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
Oliver Stieber's avatar
Oliver Stieber committed
21 22 23 24
 */

#include "wined3d_private.h"

25
WINE_DEFAULT_DEBUG_CHANNEL(d3d);
Oliver Stieber's avatar
Oliver Stieber committed
26

27
void wined3d_swapchain_cleanup(struct wined3d_swapchain *swapchain)
28
{
29
    HRESULT hr;
30 31
    UINT i;

32
    TRACE("Destroying swapchain %p.\n", swapchain);
33

34
    wined3d_swapchain_state_cleanup(&swapchain->state);
35
    wined3d_swapchain_set_gamma_ramp(swapchain, 0, &swapchain->orig_gamma);
36 37 38 39 40

    /* Release the swapchain's draw buffers. Make sure swapchain->back_buffers[0]
     * is the last buffer to be destroyed, FindContext() depends on that. */
    if (swapchain->front_buffer)
    {
41 42
        wined3d_texture_set_swapchain(swapchain->front_buffer, NULL);
        if (wined3d_texture_decref(swapchain->front_buffer))
43 44 45 46 47 48
            WARN("Something's still holding the front buffer (%p).\n", swapchain->front_buffer);
        swapchain->front_buffer = NULL;
    }

    if (swapchain->back_buffers)
    {
49
        i = swapchain->state.desc.backbuffer_count;
50 51 52

        while (i--)
        {
53 54
            wined3d_texture_set_swapchain(swapchain->back_buffers[i], NULL);
            if (wined3d_texture_decref(swapchain->back_buffers[i]))
55 56
                WARN("Something's still holding back buffer %u (%p).\n", i, swapchain->back_buffers[i]);
        }
57
        heap_free(swapchain->back_buffers);
58 59 60 61 62 63 64 65 66
        swapchain->back_buffers = NULL;
    }

    /* Restore the screen resolution if we rendered in fullscreen.
     * This will restore the screen resolution to what it was before creating
     * the swapchain. In case of d3d8 and d3d9 this will be the original
     * desktop resolution. In case of d3d7 this will be a NOP because ddraw
     * sets the resolution before starting up Direct3D, thus orig_width and
     * orig_height will be equal to the modes in the presentation params. */
67
    if (!swapchain->state.desc.windowed)
68
    {
69
        if (swapchain->state.desc.auto_restore_display_mode)
70
        {
71
            if (FAILED(hr = wined3d_restore_display_modes(swapchain->device->wined3d)))
72 73
                ERR("Failed to restore display mode, hr %#x.\n", hr);

74
            if (swapchain->state.desc.flags & WINED3D_SWAPCHAIN_RESTORE_WINDOW_RECT)
75
            {
76
                wined3d_swapchain_state_restore_from_fullscreen(&swapchain->state,
77
                        swapchain->state.device_window, &swapchain->state.original_window_rect);
78 79 80 81
                wined3d_device_release_focus_window(swapchain->device);
            }
        }
        else
82
        {
83
            wined3d_swapchain_state_restore_from_fullscreen(&swapchain->state, swapchain->state.device_window, NULL);
84
        }
85 86 87
    }
}

88 89 90 91 92
static void wined3d_swapchain_gl_destroy_object(void *object)
{
    wined3d_swapchain_gl_destroy_contexts(object);
}

93 94
void wined3d_swapchain_gl_cleanup(struct wined3d_swapchain_gl *swapchain_gl)
{
95 96
    struct wined3d_cs *cs = swapchain_gl->s.device->cs;

97
    wined3d_swapchain_cleanup(&swapchain_gl->s);
98

99 100 101
    wined3d_cs_destroy_object(cs, wined3d_swapchain_gl_destroy_object, swapchain_gl);
    wined3d_cs_finish(cs, WINED3D_CS_QUEUE_DEFAULT);

102 103 104 105 106 107 108
    if (swapchain_gl->backup_dc)
    {
        TRACE("Destroying backup wined3d window %p, dc %p.\n", swapchain_gl->backup_wnd, swapchain_gl->backup_dc);

        wined3d_release_dc(swapchain_gl->backup_wnd, swapchain_gl->backup_dc);
        DestroyWindow(swapchain_gl->backup_wnd);
    }
109 110
}

111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
static void wined3d_swapchain_vk_destroy_vulkan_swapchain(struct wined3d_swapchain_vk *swapchain_vk)
{
    struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device);
    const struct wined3d_vk_info *vk_info;
    unsigned int i;
    VkResult vr;

    TRACE("swapchain_vk %p.\n", swapchain_vk);

    vk_info = &wined3d_adapter_vk(device_vk->d.adapter)->vk_info;

    if ((vr = VK_CALL(vkQueueWaitIdle(device_vk->vk_queue))) < 0)
        ERR("Failed to wait on queue, vr %s.\n", wined3d_debug_vkresult(vr));
    heap_free(swapchain_vk->vk_images);
    for (i = 0; i < swapchain_vk->image_count; ++i)
    {
        VK_CALL(vkDestroySemaphore(device_vk->vk_device, swapchain_vk->vk_semaphores[i].available, NULL));
        VK_CALL(vkDestroySemaphore(device_vk->vk_device, swapchain_vk->vk_semaphores[i].presentable, NULL));
    }
    heap_free(swapchain_vk->vk_semaphores);
    VK_CALL(vkDestroySwapchainKHR(device_vk->vk_device, swapchain_vk->vk_swapchain, NULL));
    VK_CALL(vkDestroySurfaceKHR(vk_info->instance, swapchain_vk->vk_surface, NULL));
}

static void wined3d_swapchain_vk_destroy_object(void *object)
{
    wined3d_swapchain_vk_destroy_vulkan_swapchain(object);
}

void wined3d_swapchain_vk_cleanup(struct wined3d_swapchain_vk *swapchain_vk)
{
    struct wined3d_cs *cs = swapchain_vk->s.device->cs;

    wined3d_cs_destroy_object(cs, wined3d_swapchain_vk_destroy_object, swapchain_vk);
    wined3d_cs_finish(cs, WINED3D_CS_QUEUE_DEFAULT);

    wined3d_swapchain_cleanup(&swapchain_vk->s);
}

150
ULONG CDECL wined3d_swapchain_incref(struct wined3d_swapchain *swapchain)
151 152 153 154 155 156 157 158
{
    ULONG refcount = InterlockedIncrement(&swapchain->ref);

    TRACE("%p increasing refcount to %u.\n", swapchain, refcount);

    return refcount;
}

159
ULONG CDECL wined3d_swapchain_decref(struct wined3d_swapchain *swapchain)
160 161 162 163 164 165
{
    ULONG refcount = InterlockedDecrement(&swapchain->ref);

    TRACE("%p decreasing refcount to %u.\n", swapchain, refcount);

    if (!refcount)
166
    {
167 168
        struct wined3d_device *device;

169
        wined3d_mutex_lock();
170

171 172 173 174
        device = swapchain->device;
        if (device->swapchain_count && device->swapchains[0] == swapchain)
            wined3d_device_uninit_3d(device);
        wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT);
175

176
        swapchain->parent_ops->wined3d_object_destroyed(swapchain->parent);
177
        swapchain->device->adapter->adapter_ops->adapter_destroy_swapchain(swapchain);
178 179

        wined3d_mutex_unlock();
180
    }
181 182 183 184

    return refcount;
}

185
void * CDECL wined3d_swapchain_get_parent(const struct wined3d_swapchain *swapchain)
186
{
187
    TRACE("swapchain %p.\n", swapchain);
188

189
    return swapchain->parent;
190 191
}

192
void CDECL wined3d_swapchain_set_window(struct wined3d_swapchain *swapchain, HWND window)
193 194
{
    if (!window)
195
        window = swapchain->state.device_window;
196
    if (window == swapchain->win_handle)
197
        return;
198 199 200

    TRACE("Setting swapchain %p window from %p to %p.\n",
            swapchain, swapchain->win_handle, window);
201

202
    wined3d_cs_finish(swapchain->device->cs, WINED3D_CS_QUEUE_DEFAULT);
203

204 205 206
    swapchain->win_handle = window;
}

207
HRESULT CDECL wined3d_swapchain_present(struct wined3d_swapchain *swapchain,
208 209
        const RECT *src_rect, const RECT *dst_rect, HWND dst_window_override,
        unsigned int swap_interval, DWORD flags)
210
{
211 212
    RECT s, d;

213
    TRACE("swapchain %p, src_rect %s, dst_rect %s, dst_window_override %p, swap_interval %u, flags %#x.\n",
214
            swapchain, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect),
215
            dst_window_override, swap_interval, flags);
216

217 218 219
    if (flags)
        FIXME("Ignoring flags %#x.\n", flags);

220 221
    wined3d_mutex_lock();

222 223
    if (!swapchain->back_buffers)
    {
224
        WARN("Swapchain doesn't have a backbuffer, returning WINED3DERR_INVALIDCALL.\n");
225
        wined3d_mutex_unlock();
226 227 228
        return WINED3DERR_INVALIDCALL;
    }

229 230
    if (!src_rect)
    {
231 232
        SetRect(&s, 0, 0, swapchain->state.desc.backbuffer_width,
                swapchain->state.desc.backbuffer_height);
233 234 235 236 237 238 239 240 241
        src_rect = &s;
    }

    if (!dst_rect)
    {
        GetClientRect(swapchain->win_handle, &d);
        dst_rect = &d;
    }

242
    wined3d_cs_emit_present(swapchain->device->cs, swapchain, src_rect,
243
            dst_rect, dst_window_override, swap_interval, flags);
244

245 246
    wined3d_mutex_unlock();

247
    return WINED3D_OK;
248 249
}

250
HRESULT CDECL wined3d_swapchain_get_front_buffer_data(const struct wined3d_swapchain *swapchain,
251
        struct wined3d_texture *dst_texture, unsigned int sub_resource_idx)
252
{
253
    RECT src_rect, dst_rect;
254

255
    TRACE("swapchain %p, dst_texture %p, sub_resource_idx %u.\n", swapchain, dst_texture, sub_resource_idx);
256

257
    SetRect(&src_rect, 0, 0, swapchain->front_buffer->resource.width, swapchain->front_buffer->resource.height);
258
    dst_rect = src_rect;
259

260
    if (swapchain->state.desc.windowed)
261 262 263 264 265
    {
        MapWindowPoints(swapchain->win_handle, NULL, (POINT *)&dst_rect, 2);
        FIXME("Using destination rect %s in windowed mode, this is likely wrong.\n",
                wine_dbgstr_rect(&dst_rect));
    }
266

267
    return wined3d_device_context_blt(&swapchain->device->cs->c, dst_texture, sub_resource_idx, &dst_rect,
268
            swapchain->front_buffer, 0, &src_rect, 0, NULL, WINED3D_TEXF_POINT);
269 270
}

271
struct wined3d_texture * CDECL wined3d_swapchain_get_back_buffer(const struct wined3d_swapchain *swapchain,
272
        UINT back_buffer_idx)
273
{
274 275
    TRACE("swapchain %p, back_buffer_idx %u.\n",
            swapchain, back_buffer_idx);
276 277 278 279 280 281

    /* Return invalid if there is no backbuffer array, otherwise it will
     * crash when ddraw is used (there swapchain->back_buffers is always
     * NULL). We need this because this function is called from
     * stateblock_init_default_state() to get the default scissorrect
     * dimensions. */
282
    if (!swapchain->back_buffers || back_buffer_idx >= swapchain->state.desc.backbuffer_count)
283 284 285 286
    {
        WARN("Invalid back buffer index.\n");
        /* Native d3d9 doesn't set NULL here, just as wine's d3d9. But set it
         * here in wined3d to avoid problems in other libs. */
287
        return NULL;
288 289
    }

290
    TRACE("Returning back buffer %p.\n", swapchain->back_buffers[back_buffer_idx]);
291

292
    return swapchain->back_buffers[back_buffer_idx];
293 294
}

295 296 297 298
struct wined3d_output * wined3d_swapchain_get_output(const struct wined3d_swapchain *swapchain)
{
    TRACE("swapchain %p.\n", swapchain);

299
    return swapchain->state.desc.output;
300 301
}

302
HRESULT CDECL wined3d_swapchain_get_raster_status(const struct wined3d_swapchain *swapchain,
303
        struct wined3d_raster_status *raster_status)
304
{
305 306
    struct wined3d_output *output;

307
    TRACE("swapchain %p, raster_status %p.\n", swapchain, raster_status);
308

309 310 311 312 313 314 315 316
    output = wined3d_swapchain_get_output(swapchain);
    if (!output)
    {
        ERR("Failed to get output from swapchain %p.\n", swapchain);
        return E_FAIL;
    }

    return wined3d_output_get_raster_status(output, raster_status);
317 318
}

319 320 321 322 323
struct wined3d_swapchain_state * CDECL wined3d_swapchain_get_state(struct wined3d_swapchain *swapchain)
{
    return &swapchain->state;
}

324
HRESULT CDECL wined3d_swapchain_get_display_mode(const struct wined3d_swapchain *swapchain,
325
        struct wined3d_display_mode *mode, enum wined3d_display_rotation *rotation)
326
{
327
    struct wined3d_output *output;
328 329
    HRESULT hr;

330
    TRACE("swapchain %p, mode %p, rotation %p.\n", swapchain, mode, rotation);
331

332 333 334 335 336 337 338
    if (!(output = wined3d_swapchain_get_output(swapchain)))
    {
        ERR("Failed to get output from swapchain %p.\n", swapchain);
        return E_FAIL;
    }

    hr = wined3d_output_get_display_mode(output, mode, rotation);
339 340

    TRACE("Returning w %u, h %u, refresh rate %u, format %s.\n",
341
            mode->width, mode->height, mode->refresh_rate, debug_d3dformat(mode->format_id));
342 343 344 345

    return hr;
}

346
struct wined3d_device * CDECL wined3d_swapchain_get_device(const struct wined3d_swapchain *swapchain)
347
{
348
    TRACE("swapchain %p.\n", swapchain);
349

350
    return swapchain->device;
351 352
}

353
void CDECL wined3d_swapchain_get_desc(const struct wined3d_swapchain *swapchain,
354
        struct wined3d_swapchain_desc *desc)
355
{
356
    TRACE("swapchain %p, desc %p.\n", swapchain, desc);
357

358
    *desc = swapchain->state.desc;
359 360
}

361
HRESULT CDECL wined3d_swapchain_set_gamma_ramp(const struct wined3d_swapchain *swapchain,
362
        DWORD flags, const struct wined3d_gamma_ramp *ramp)
363
{
364
    struct wined3d_output *output;
365

366
    TRACE("swapchain %p, flags %#x, ramp %p.\n", swapchain, flags, ramp);
367 368 369 370

    if (flags)
        FIXME("Ignoring flags %#x.\n", flags);

371 372 373 374 375
    if (!(output = wined3d_swapchain_get_output(swapchain)))
    {
        ERR("Failed to get output from swapchain %p.\n", swapchain);
        return E_FAIL;
    }
376

377
    return wined3d_output_set_gamma_ramp(output, ramp);
378 379
}

380 381 382
void CDECL wined3d_swapchain_set_palette(struct wined3d_swapchain *swapchain, struct wined3d_palette *palette)
{
    TRACE("swapchain %p, palette %p.\n", swapchain, palette);
383 384 385

    wined3d_cs_finish(swapchain->device->cs, WINED3D_CS_QUEUE_DEFAULT);

386 387 388
    swapchain->palette = palette;
}

389
HRESULT CDECL wined3d_swapchain_get_gamma_ramp(const struct wined3d_swapchain *swapchain,
390
        struct wined3d_gamma_ramp *ramp)
391
{
392
    struct wined3d_output *output;
393

394
    TRACE("swapchain %p, ramp %p.\n", swapchain, ramp);
395

396 397 398 399 400
    if (!(output = wined3d_swapchain_get_output(swapchain)))
    {
        ERR("Failed to get output from swapchain %p.\n", swapchain);
        return E_FAIL;
    }
401

402
    return wined3d_output_get_gamma_ramp(output, ramp);
403
}
Oliver Stieber's avatar
Oliver Stieber committed
404

405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
/* The is a fallback for cases where we e.g. can't create a GL context or
 * Vulkan swapchain for the swapchain window. */
static void swapchain_blit_gdi(struct wined3d_swapchain *swapchain,
        struct wined3d_context *context, const RECT *src_rect, const RECT *dst_rect)
{
    struct wined3d_texture *back_buffer = swapchain->back_buffers[0];
    D3DKMT_DESTROYDCFROMMEMORY destroy_desc;
    D3DKMT_CREATEDCFROMMEMORY create_desc;
    const struct wined3d_format *format;
    unsigned int row_pitch, slice_pitch;
    HDC src_dc, dst_dc;
    NTSTATUS status;
    HBITMAP bitmap;

    static unsigned int once;

    TRACE("swapchain %p, context %p, src_rect %s, dst_rect %s.\n",
            swapchain, context, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect));

    if (!once++)
        FIXME("Using GDI present.\n");

    format = back_buffer->resource.format;
    if (!format->ddi_format)
    {
        WARN("Cannot create a DC for format %s.\n", debug_d3dformat(format->id));
        return;
    }

    wined3d_texture_load_location(back_buffer, 0, context, WINED3D_LOCATION_SYSMEM);
    wined3d_texture_get_pitch(back_buffer, 0, &row_pitch, &slice_pitch);

    create_desc.pMemory = back_buffer->resource.heap_memory;
    create_desc.Format = format->ddi_format;
    create_desc.Width = wined3d_texture_get_level_width(back_buffer, 0);
    create_desc.Height = wined3d_texture_get_level_height(back_buffer, 0);
    create_desc.Pitch = row_pitch;
    create_desc.hDeviceDc = CreateCompatibleDC(NULL);
    create_desc.pColorTable = NULL;

    status = D3DKMTCreateDCFromMemory(&create_desc);
    DeleteDC(create_desc.hDeviceDc);
    if (status)
    {
        WARN("Failed to create DC, status %#x.\n", status);
        return;
    }

    src_dc = create_desc.hDc;
    bitmap = create_desc.hBitmap;

    TRACE("Created source DC %p, bitmap %p for backbuffer %p.\n", src_dc, bitmap, back_buffer);

    if (!(dst_dc = GetDCEx(swapchain->win_handle, 0, DCX_USESTYLE | DCX_CACHE)))
        ERR("Failed to get destination DC.\n");

461 462 463
    if (!StretchBlt(dst_dc, dst_rect->left, dst_rect->top, dst_rect->right - dst_rect->left,
            dst_rect->bottom - dst_rect->top, src_dc, src_rect->left, src_rect->top,
            src_rect->right - src_rect->left, src_rect->bottom - src_rect->top, SRCCOPY))
464 465 466 467 468 469 470 471 472
        ERR("Failed to blit.\n");

    ReleaseDC(swapchain->win_handle, dst_dc);
    destroy_desc.hDc = src_dc;
    destroy_desc.hBitmap = bitmap;
    if ((status = D3DKMTDestroyDCFromMemory(&destroy_desc)))
        ERR("Failed to destroy src dc, status %#x.\n", status);
}

473
/* A GL context is provided by the caller */
474
static void swapchain_blit(const struct wined3d_swapchain *swapchain,
475
        struct wined3d_context *context, const RECT *src_rect, const RECT *dst_rect)
476
{
477
    struct wined3d_texture *texture = swapchain->back_buffers[0];
478
    struct wined3d_device *device = swapchain->device;
479
    enum wined3d_texture_filter_type filter;
480
    DWORD location;
481

482
    TRACE("swapchain %p, context %p, src_rect %s, dst_rect %s.\n",
483
            swapchain, context, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect));
484

485 486 487 488
    if ((src_rect->right - src_rect->left == dst_rect->right - dst_rect->left
            && src_rect->bottom - src_rect->top == dst_rect->bottom - dst_rect->top)
            || is_complex_fixup(texture->resource.format->color_fixup))
        filter = WINED3D_TEXF_NONE;
489
    else
490
        filter = WINED3D_TEXF_LINEAR;
491

492 493 494
    location = WINED3D_LOCATION_TEXTURE_RGB;
    if (texture->resource.multisample_type)
        location = WINED3D_LOCATION_RB_RESOLVED;
495

496
    wined3d_texture_validate_location(texture, 0, WINED3D_LOCATION_DRAWABLE);
497
    device->blitter->ops->blitter_blit(device->blitter, WINED3D_BLIT_OP_COLOR_BLIT, context, texture, 0,
498
            location, src_rect, texture, 0, WINED3D_LOCATION_DRAWABLE, dst_rect, NULL, filter, NULL);
499
    wined3d_texture_invalidate_location(texture, 0, WINED3D_LOCATION_DRAWABLE);
500 501
}

502
static void swapchain_gl_set_swap_interval(struct wined3d_swapchain *swapchain,
503
        struct wined3d_context_gl *context_gl, unsigned int swap_interval)
504
{
505
    const struct wined3d_gl_info *gl_info = context_gl->gl_info;
506 507 508 509 510 511 512 513 514 515 516 517 518

    swap_interval = swap_interval <= 4 ? swap_interval : 1;
    if (swapchain->swap_interval == swap_interval)
        return;

    swapchain->swap_interval = swap_interval;

    if (!gl_info->supported[WGL_EXT_SWAP_CONTROL])
        return;

    if (!GL_EXTCALL(wglSwapIntervalEXT(swap_interval)))
    {
        ERR("Failed to set swap interval %u for context %p, last error %#x.\n",
519
                swap_interval, context_gl, GetLastError());
520 521 522
    }
}

523
/* Context activation is done by the caller. */
524
static void wined3d_swapchain_gl_rotate(struct wined3d_swapchain *swapchain, struct wined3d_context *context)
525
{
526
    struct wined3d_texture_sub_resource *sub_resource;
527
    struct wined3d_texture_gl *texture, *texture_prev;
528 529 530 531 532 533
    struct gl_texture tex0;
    GLuint rb0;
    DWORD locations0;
    unsigned int i;
    static const DWORD supported_locations = WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_RB_MULTISAMPLE;

534
    if (swapchain->state.desc.backbuffer_count < 2 || wined3d_settings.offscreen_rendering_mode != ORM_FBO)
535 536
        return;

537
    texture_prev = wined3d_texture_gl(swapchain->back_buffers[0]);
538 539

    /* Back buffer 0 is already in the draw binding. */
540
    tex0 = texture_prev->texture_rgb;
541
    rb0 = texture_prev->rb_multisample;
542
    locations0 = texture_prev->t.sub_resources[0].locations;
543

544
    for (i = 1; i < swapchain->state.desc.backbuffer_count; ++i)
545
    {
546 547
        texture = wined3d_texture_gl(swapchain->back_buffers[i]);
        sub_resource = &texture->t.sub_resources[0];
548

549
        if (!(sub_resource->locations & supported_locations))
550
            wined3d_texture_load_location(&texture->t, 0, context, texture->t.resource.draw_binding);
551

552
        texture_prev->texture_rgb = texture->texture_rgb;
553
        texture_prev->rb_multisample = texture->rb_multisample;
554

555 556
        wined3d_texture_validate_location(&texture_prev->t, 0, sub_resource->locations & supported_locations);
        wined3d_texture_invalidate_location(&texture_prev->t, 0, ~(sub_resource->locations & supported_locations));
557

558
        texture_prev = texture;
559 560
    }

561
    texture_prev->texture_rgb = tex0;
562
    texture_prev->rb_multisample = rb0;
563

564 565
    wined3d_texture_validate_location(&texture_prev->t, 0, locations0 & supported_locations);
    wined3d_texture_invalidate_location(&texture_prev->t, 0, ~(locations0 & supported_locations));
566 567 568 569

    device_invalidate_state(swapchain->device, STATE_FRAMEBUFFER);
}

570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
static bool swapchain_present_is_partial_copy(struct wined3d_swapchain *swapchain, const RECT *dst_rect)
{
    enum wined3d_swap_effect swap_effect = swapchain->state.desc.swap_effect;
    RECT client_rect;
    unsigned int t;

    if (swap_effect != WINED3D_SWAP_EFFECT_COPY && swap_effect != WINED3D_SWAP_EFFECT_COPY_VSYNC)
        return false;

    GetClientRect(swapchain->win_handle, &client_rect);

    t = client_rect.right - client_rect.left;
    if ((dst_rect->left && dst_rect->right) || abs(dst_rect->right - dst_rect->left) != t)
        return true;
    t = client_rect.bottom - client_rect.top;
    if ((dst_rect->top && dst_rect->bottom) || abs(dst_rect->bottom - dst_rect->top) != t)
        return true;

    return false;
}

591
static void swapchain_gl_present(struct wined3d_swapchain *swapchain,
592
        const RECT *src_rect, const RECT *dst_rect, unsigned int swap_interval, DWORD flags)
593
{
594
    struct wined3d_swapchain_gl *swapchain_gl = wined3d_swapchain_gl(swapchain);
595
    struct wined3d_texture *back_buffer = swapchain->back_buffers[0];
596
    const struct wined3d_pixel_format *pixel_format;
597
    const struct wined3d_gl_info *gl_info;
598
    struct wined3d_context_gl *context_gl;
599
    struct wined3d_context *context;
600

601
    context = context_acquire(swapchain->device, swapchain->front_buffer, 0);
602 603
    context_gl = wined3d_context_gl(context);
    if (!context_gl->valid)
604 605 606
    {
        context_release(context);
        WARN("Invalid context, skipping present.\n");
607
        return;
608
    }
609

610
    TRACE("Presenting DC %p.\n", context_gl->dc);
611

612 613 614
    pixel_format = &wined3d_adapter_gl(swapchain->device->adapter)->pixel_formats[context_gl->pixel_format];
    if (context_gl->dc == swapchain_gl->backup_dc || (pixel_format->swap_method != WGL_SWAP_COPY_ARB
            && swapchain_present_is_partial_copy(swapchain, dst_rect)))
615
    {
616
        swapchain_blit_gdi(swapchain, context, src_rect, dst_rect);
617
    }
618 619
    else
    {
620 621 622 623
        gl_info = context_gl->gl_info;

        swapchain_gl_set_swap_interval(swapchain, context_gl, swap_interval);

624
        wined3d_texture_load_location(back_buffer, 0, context, back_buffer->resource.draw_binding);
625

626 627
        if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
            swapchain_blit(swapchain, context, src_rect, dst_rect);
628

629 630 631 632 633 634
        if (swapchain_gl->context_count > 1)
            gl_info->gl_ops.gl.p_glFinish();

        /* call wglSwapBuffers through the gl table to avoid confusing the Steam overlay */
        gl_info->gl_ops.wgl.p_wglSwapBuffers(context_gl->dc);
    }
635

636 637
    if (context->d3d_info->fences)
        wined3d_context_gl_submit_command_fence(context_gl);
638

639
    wined3d_swapchain_gl_rotate(swapchain, context);
640

641
    TRACE("SwapBuffers called, Starting new frame\n");
Oliver Stieber's avatar
Oliver Stieber committed
642

643
    wined3d_texture_validate_location(swapchain->front_buffer, 0, WINED3D_LOCATION_DRAWABLE);
644
    wined3d_texture_invalidate_location(swapchain->front_buffer, 0, ~WINED3D_LOCATION_DRAWABLE);
645

646
    context_release(context);
Oliver Stieber's avatar
Oliver Stieber committed
647 648
}

649
static void swapchain_frontbuffer_updated(struct wined3d_swapchain *swapchain)
650
{
651
    struct wined3d_texture *front_buffer = swapchain->front_buffer;
652 653
    struct wined3d_context *context;

654
    context = context_acquire(swapchain->device, front_buffer, 0);
655
    wined3d_texture_load_location(front_buffer, 0, context, front_buffer->resource.draw_binding);
656 657 658 659
    context_release(context);
    SetRectEmpty(&swapchain->front_buffer_update);
}

660
static const struct wined3d_swapchain_ops swapchain_gl_ops =
Oliver Stieber's avatar
Oliver Stieber committed
661
{
662
    swapchain_gl_present,
663 664 665
    swapchain_frontbuffer_updated,
};

666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 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
static bool wined3d_swapchain_vk_present_mode_supported(struct wined3d_swapchain_vk *swapchain_vk,
        VkPresentModeKHR vk_present_mode)
{
    struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device);
    const struct wined3d_vk_info *vk_info;
    struct wined3d_adapter_vk *adapter_vk;
    VkPhysicalDevice vk_physical_device;
    VkPresentModeKHR *vk_modes;
    bool supported = false;
    uint32_t count, i;
    VkResult vr;

    adapter_vk = wined3d_adapter_vk(device_vk->d.adapter);
    vk_physical_device = adapter_vk->physical_device;
    vk_info = &adapter_vk->vk_info;

    if ((vr = VK_CALL(vkGetPhysicalDeviceSurfacePresentModesKHR(vk_physical_device,
            swapchain_vk->vk_surface, &count, NULL))) < 0)
    {
        ERR("Failed to get supported present mode count, vr %s.\n", wined3d_debug_vkresult(vr));
        return false;
    }

    if (!(vk_modes = heap_calloc(count, sizeof(*vk_modes))))
        return false;

    if ((vr = VK_CALL(vkGetPhysicalDeviceSurfacePresentModesKHR(vk_physical_device,
            swapchain_vk->vk_surface, &count, vk_modes))) < 0)
    {
        ERR("Failed to get supported present modes, vr %s.\n", wined3d_debug_vkresult(vr));
        goto done;
    }

    for (i = 0; i < count; ++i)
    {
        if (vk_modes[i] == vk_present_mode)
        {
            supported = true;
            goto done;
        }
    }

done:
    heap_free(vk_modes);
    return supported;
}

713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728
static VkFormat get_swapchain_fallback_format(VkFormat vk_format)
{
    switch (vk_format)
    {
        case VK_FORMAT_R8G8B8A8_SRGB:
            return VK_FORMAT_B8G8R8A8_SRGB;
        case VK_FORMAT_R8G8B8A8_UNORM:
        case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
        case VK_FORMAT_R16G16B16A16_SFLOAT:
            return VK_FORMAT_B8G8R8A8_UNORM;
        default:
            WARN("Unhandled format %#x.\n", vk_format);
            return VK_FORMAT_UNDEFINED;
    }
}

729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777
static VkFormat wined3d_swapchain_vk_select_vk_format(struct wined3d_swapchain_vk *swapchain_vk,
        VkSurfaceKHR vk_surface)
{
    struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device);
    const struct wined3d_swapchain_desc *desc = &swapchain_vk->s.state.desc;
    const struct wined3d_vk_info *vk_info;
    struct wined3d_adapter_vk *adapter_vk;
    const struct wined3d_format *format;
    VkPhysicalDevice vk_physical_device;
    VkSurfaceFormatKHR *vk_formats;
    uint32_t format_count, i;
    VkFormat vk_format;
    VkResult vr;

    adapter_vk = wined3d_adapter_vk(device_vk->d.adapter);
    vk_physical_device = adapter_vk->physical_device;
    vk_info = &adapter_vk->vk_info;

    if ((format = wined3d_get_format(&adapter_vk->a, desc->backbuffer_format, WINED3D_BIND_RENDER_TARGET)))
        vk_format = wined3d_format_vk(format)->vk_format;
    else
        vk_format = VK_FORMAT_B8G8R8A8_UNORM;

    vr = VK_CALL(vkGetPhysicalDeviceSurfaceFormatsKHR(vk_physical_device, vk_surface, &format_count, NULL));
    if (vr < 0 || !format_count)
    {
        WARN("Failed to get supported surface format count, vr %s.\n", wined3d_debug_vkresult(vr));
        return VK_FORMAT_UNDEFINED;
    }

    if (!(vk_formats = heap_calloc(format_count, sizeof(*vk_formats))))
        return VK_FORMAT_UNDEFINED;

    if ((vr = VK_CALL(vkGetPhysicalDeviceSurfaceFormatsKHR(vk_physical_device,
            vk_surface, &format_count, vk_formats))) < 0)
    {
        WARN("Failed to get supported surface formats, vr %s.\n", wined3d_debug_vkresult(vr));
        heap_free(vk_formats);
        return VK_FORMAT_UNDEFINED;
    }

    for (i = 0; i < format_count; ++i)
    {
        if (vk_formats[i].format == vk_format && vk_formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
            break;
    }
    if (i == format_count)
    {
        /* Try to create a swapchain with format conversion. */
778
        vk_format = get_swapchain_fallback_format(vk_format);
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887
        WARN("Failed to find Vulkan swapchain format for %s.\n", debug_d3dformat(desc->backbuffer_format));
        for (i = 0; i < format_count; ++i)
        {
            if (vk_formats[i].format == vk_format && vk_formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
                break;
        }
    }
    heap_free(vk_formats);
    if (i == format_count)
    {
        FIXME("Failed to find Vulkan swapchain format for %s.\n", debug_d3dformat(desc->backbuffer_format));
        return VK_FORMAT_UNDEFINED;
    }

    TRACE("Using Vulkan swapchain format %#x.\n", vk_format);

    return vk_format;
}

static bool wined3d_swapchain_vk_create_vulkan_swapchain_images(struct wined3d_swapchain_vk *swapchain_vk,
        VkSwapchainKHR vk_swapchain)
{
    struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device);
    const struct wined3d_vk_info *vk_info;
    VkSemaphoreCreateInfo semaphore_info;
    uint32_t image_count, i;
    VkResult vr;

    vk_info = &wined3d_adapter_vk(device_vk->d.adapter)->vk_info;

    if ((vr = VK_CALL(vkGetSwapchainImagesKHR(device_vk->vk_device, vk_swapchain, &image_count, NULL))) < 0)
    {
        ERR("Failed to get image count, vr %s\n", wined3d_debug_vkresult(vr));
        return false;
    }

    if (!(swapchain_vk->vk_images = heap_calloc(image_count, sizeof(*swapchain_vk->vk_images))))
    {
        ERR("Failed to allocate images array.\n");
        return false;
    }

    if ((vr = VK_CALL(vkGetSwapchainImagesKHR(device_vk->vk_device,
            vk_swapchain, &image_count, swapchain_vk->vk_images))) < 0)
    {
        ERR("Failed to get swapchain images, vr %s.\n", wined3d_debug_vkresult(vr));
        heap_free(swapchain_vk->vk_images);
        return false;
    }

    if (!(swapchain_vk->vk_semaphores = heap_calloc(image_count, sizeof(*swapchain_vk->vk_semaphores))))
    {
        ERR("Failed to allocate semaphores array.\n");
        heap_free(swapchain_vk->vk_images);
        return false;
    }

    semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
    semaphore_info.pNext = NULL;
    semaphore_info.flags = 0;
    for (i = 0; i < image_count; ++i)
    {
        if ((vr = VK_CALL(vkCreateSemaphore(device_vk->vk_device,
                &semaphore_info, NULL, &swapchain_vk->vk_semaphores[i].available))) < 0)
        {
            ERR("Failed to create semaphore, vr %s.\n", wined3d_debug_vkresult(vr));
            goto fail;
        }

        if ((vr = VK_CALL(vkCreateSemaphore(device_vk->vk_device,
                &semaphore_info, NULL, &swapchain_vk->vk_semaphores[i].presentable))) < 0)
        {
            ERR("Failed to create semaphore, vr %s.\n", wined3d_debug_vkresult(vr));
            goto fail;
        }
    }
    swapchain_vk->image_count = image_count;

    return true;

fail:
    for (i = 0; i < image_count; ++i)
    {
        if (swapchain_vk->vk_semaphores[i].available)
            VK_CALL(vkDestroySemaphore(device_vk->vk_device, swapchain_vk->vk_semaphores[i].available, NULL));
        if (swapchain_vk->vk_semaphores[i].presentable)
            VK_CALL(vkDestroySemaphore(device_vk->vk_device, swapchain_vk->vk_semaphores[i].presentable, NULL));
    }
    heap_free(swapchain_vk->vk_semaphores);
    heap_free(swapchain_vk->vk_images);
    return false;
}

static HRESULT wined3d_swapchain_vk_create_vulkan_swapchain(struct wined3d_swapchain_vk *swapchain_vk)
{
    struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device);
    const struct wined3d_swapchain_desc *desc = &swapchain_vk->s.state.desc;
    VkSwapchainCreateInfoKHR vk_swapchain_desc;
    VkWin32SurfaceCreateInfoKHR surface_desc;
    unsigned int width, height, image_count;
    const struct wined3d_vk_info *vk_info;
    VkSurfaceCapabilitiesKHR surface_caps;
    struct wined3d_adapter_vk *adapter_vk;
    VkPresentModeKHR vk_present_mode;
    VkSwapchainKHR vk_swapchain;
    VkImageUsageFlags usage;
    VkSurfaceKHR vk_surface;
    VkBool32 supported;
    VkFormat vk_format;
888
    RECT client_rect;
889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935
    VkResult vr;

    adapter_vk = wined3d_adapter_vk(device_vk->d.adapter);
    vk_info = &adapter_vk->vk_info;

    surface_desc.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
    surface_desc.pNext = NULL;
    surface_desc.flags = 0;
    surface_desc.hinstance = (HINSTANCE)GetWindowLongPtrW(swapchain_vk->s.win_handle, GWLP_HINSTANCE);
    surface_desc.hwnd = swapchain_vk->s.win_handle;
    if ((vr = VK_CALL(vkCreateWin32SurfaceKHR(vk_info->instance, &surface_desc, NULL, &vk_surface))) < 0)
    {
        ERR("Failed to create Vulkan surface, vr %s.\n", wined3d_debug_vkresult(vr));
        return E_FAIL;
    }
    swapchain_vk->vk_surface = vk_surface;

    if ((vr = VK_CALL(vkGetPhysicalDeviceSurfaceSupportKHR(adapter_vk->physical_device,
            device_vk->vk_queue_family_index, vk_surface, &supported))) < 0 || !supported)
    {
        ERR("Queue family does not support presentation on this surface, vr %s.\n", wined3d_debug_vkresult(vr));
        goto fail;
    }

    if ((vk_format = wined3d_swapchain_vk_select_vk_format(swapchain_vk, vk_surface)) == VK_FORMAT_UNDEFINED)
    {
        ERR("Failed to select swapchain format.\n");
        goto fail;
    }

    if ((vr = VK_CALL(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(adapter_vk->physical_device,
            swapchain_vk->vk_surface, &surface_caps))) < 0)
    {
        ERR("Failed to get surface capabilities, vr %s.\n", wined3d_debug_vkresult(vr));
        goto fail;
    }

    image_count = desc->backbuffer_count;
    if (image_count < surface_caps.minImageCount)
        image_count = surface_caps.minImageCount;
    else if (surface_caps.maxImageCount && image_count > surface_caps.maxImageCount)
        image_count = surface_caps.maxImageCount;

    if (image_count != desc->backbuffer_count)
        WARN("Image count %u is not supported (%u-%u).\n", desc->backbuffer_count,
                surface_caps.minImageCount, surface_caps.maxImageCount);

936 937 938
    GetClientRect(swapchain_vk->s.win_handle, &client_rect);

    width = client_rect.right - client_rect.left;
939 940 941 942 943
    if (width < surface_caps.minImageExtent.width)
        width = surface_caps.minImageExtent.width;
    else if (width > surface_caps.maxImageExtent.width)
        width = surface_caps.maxImageExtent.width;

944
    height = client_rect.bottom - client_rect.top;
945 946 947 948 949
    if (height < surface_caps.minImageExtent.height)
        height = surface_caps.minImageExtent.height;
    else if (height > surface_caps.maxImageExtent.height)
        height = surface_caps.maxImageExtent.height;

950
    if (width != client_rect.right - client_rect.left || height != client_rect.bottom - client_rect.top)
951
        WARN("Swapchain dimensions %ux%u are not supported (%u-%u x %u-%u).\n",
952
                client_rect.right - client_rect.left, client_rect.bottom - client_rect.top,
953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008
                surface_caps.minImageExtent.width, surface_caps.maxImageExtent.width,
                surface_caps.minImageExtent.height, surface_caps.maxImageExtent.height);

    usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
    usage |= surface_caps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
    usage |= surface_caps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT;
    if (!(usage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) || !(usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT))
        WARN("Transfer not supported for swapchain images.\n");

    if (!(surface_caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR))
    {
        FIXME("Unsupported alpha mode, %#x.\n", surface_caps.supportedCompositeAlpha);
        goto fail;
    }

    vk_present_mode = VK_PRESENT_MODE_FIFO_KHR;
    if (!swapchain_vk->s.swap_interval)
    {
        if (wined3d_swapchain_vk_present_mode_supported(swapchain_vk, VK_PRESENT_MODE_IMMEDIATE_KHR))
            vk_present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
        else
            FIXME("Unsupported swap interval %u.\n", swapchain_vk->s.swap_interval);
    }

    vk_swapchain_desc.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
    vk_swapchain_desc.pNext = NULL;
    vk_swapchain_desc.flags = 0;
    vk_swapchain_desc.surface = vk_surface;
    vk_swapchain_desc.minImageCount = image_count;
    vk_swapchain_desc.imageFormat = vk_format;
    vk_swapchain_desc.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
    vk_swapchain_desc.imageExtent.width = width;
    vk_swapchain_desc.imageExtent.height = height;
    vk_swapchain_desc.imageArrayLayers = 1;
    vk_swapchain_desc.imageUsage = usage;
    vk_swapchain_desc.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
    vk_swapchain_desc.queueFamilyIndexCount = 0;
    vk_swapchain_desc.pQueueFamilyIndices = NULL;
    vk_swapchain_desc.preTransform = surface_caps.currentTransform;
    vk_swapchain_desc.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
    vk_swapchain_desc.presentMode = vk_present_mode;
    vk_swapchain_desc.clipped = VK_TRUE;
    vk_swapchain_desc.oldSwapchain = VK_NULL_HANDLE;
    if ((vr = VK_CALL(vkCreateSwapchainKHR(device_vk->vk_device, &vk_swapchain_desc, NULL, &vk_swapchain))) < 0)
    {
        ERR("Failed to create Vulkan swapchain, vr %s.\n", wined3d_debug_vkresult(vr));
        goto fail;
    }
    swapchain_vk->vk_swapchain = vk_swapchain;

    if (!wined3d_swapchain_vk_create_vulkan_swapchain_images(swapchain_vk, vk_swapchain))
    {
        VK_CALL(vkDestroySwapchainKHR(device_vk->vk_device, vk_swapchain, NULL));
        goto fail;
    }

1009 1010 1011
    swapchain_vk->width = width;
    swapchain_vk->height = height;

1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044
    return WINED3D_OK;

fail:
    VK_CALL(vkDestroySurfaceKHR(vk_info->instance, vk_surface, NULL));
    return E_FAIL;
}

static HRESULT wined3d_swapchain_vk_recreate(struct wined3d_swapchain_vk *swapchain_vk)
{
    TRACE("swapchain_vk %p.\n", swapchain_vk);

    wined3d_swapchain_vk_destroy_vulkan_swapchain(swapchain_vk);

    return wined3d_swapchain_vk_create_vulkan_swapchain(swapchain_vk);
}

static void wined3d_swapchain_vk_set_swap_interval(struct wined3d_swapchain_vk *swapchain_vk,
        unsigned int swap_interval)
{
    if (swap_interval > 1)
    {
        if (swap_interval <= 4)
            FIXME("Unsupported swap interval %u.\n", swap_interval);
        swap_interval = 1;
    }

    if (swapchain_vk->s.swap_interval == swap_interval)
        return;

    swapchain_vk->s.swap_interval = swap_interval;
    wined3d_swapchain_vk_recreate(swapchain_vk);
}

1045
static VkResult wined3d_swapchain_vk_blit(struct wined3d_swapchain_vk *swapchain_vk,
1046 1047 1048 1049 1050 1051 1052
        struct wined3d_context_vk *context_vk, const RECT *src_rect, const RECT *dst_rect, unsigned int swap_interval)
{
    struct wined3d_texture_vk *back_buffer_vk = wined3d_texture_vk(swapchain_vk->s.back_buffers[0]);
    struct wined3d_device_vk *device_vk = wined3d_device_vk(swapchain_vk->s.device);
    const struct wined3d_swapchain_desc *desc = &swapchain_vk->s.state.desc;
    const struct wined3d_vk_info *vk_info = context_vk->vk_info;
    VkCommandBuffer vk_command_buffer;
1053
    VkImageSubresourceRange vk_range;
1054 1055 1056 1057
    VkPresentInfoKHR present_desc;
    unsigned int present_idx;
    VkImageLayout vk_layout;
    uint32_t image_idx;
1058
    RECT dst_rect_tmp;
1059
    VkImageBlit blit;
1060
    VkFilter filter;
1061 1062 1063 1064
    VkResult vr;

    static const VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;

1065 1066 1067
    TRACE("swapchain_vk %p, context_vk %p, src_rect %s, dst_rect %s, swap_interval %u.\n",
            swapchain_vk, context_vk, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect), swap_interval);

1068 1069 1070 1071
    wined3d_swapchain_vk_set_swap_interval(swapchain_vk, swap_interval);

    present_idx = swapchain_vk->current++ % swapchain_vk->image_count;
    wined3d_context_vk_wait_command_buffer(context_vk, swapchain_vk->vk_semaphores[present_idx].command_buffer_id);
1072 1073
    if ((vr = VK_CALL(vkAcquireNextImageKHR(device_vk->vk_device, swapchain_vk->vk_swapchain, UINT64_MAX,
            swapchain_vk->vk_semaphores[present_idx].available, VK_NULL_HANDLE, &image_idx))) < 0)
1074 1075
    {
        WARN("Failed to acquire image, vr %s.\n", wined3d_debug_vkresult(vr));
1076
        return vr;
1077
    }
1078 1079

    if (dst_rect->right > swapchain_vk->width || dst_rect->bottom > swapchain_vk->height)
1080
    {
1081 1082 1083 1084 1085 1086
        dst_rect_tmp = *dst_rect;
        if (dst_rect->right > swapchain_vk->width)
            dst_rect_tmp.right = swapchain_vk->width;
        if (dst_rect->bottom > swapchain_vk->height)
            dst_rect_tmp.bottom = swapchain_vk->height;
        dst_rect = &dst_rect_tmp;
1087
    }
1088 1089 1090
    filter = src_rect->right - src_rect->left != dst_rect->right - dst_rect->left
            || src_rect->bottom - src_rect->top != dst_rect->bottom - dst_rect->top
            ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
1091 1092 1093 1094
    vk_command_buffer = wined3d_context_vk_get_command_buffer(context_vk);

    wined3d_context_vk_end_current_render_pass(context_vk);

1095 1096 1097 1098 1099 1100
    vk_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    vk_range.baseMipLevel = 0;
    vk_range.levelCount = 1;
    vk_range.baseArrayLayer = 0;
    vk_range.layerCount = 1;

1101 1102 1103 1104 1105
    wined3d_context_vk_image_barrier(context_vk, vk_command_buffer,
            VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
            vk_access_mask_from_bind_flags(back_buffer_vk->t.resource.bind_flags),
            VK_ACCESS_TRANSFER_READ_BIT,
            back_buffer_vk->layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
1106
            back_buffer_vk->image.vk_image, &vk_range);
1107 1108 1109 1110 1111

    wined3d_context_vk_image_barrier(context_vk, vk_command_buffer,
            VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
            0, VK_ACCESS_TRANSFER_WRITE_BIT,
            VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1112
            swapchain_vk->vk_images[image_idx], &vk_range);
1113

1114 1115 1116 1117
    blit.srcSubresource.aspectMask = vk_range.aspectMask;
    blit.srcSubresource.mipLevel = vk_range.baseMipLevel;
    blit.srcSubresource.baseArrayLayer = vk_range.baseArrayLayer;
    blit.srcSubresource.layerCount = vk_range.layerCount;
1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131
    blit.srcOffsets[0].x = src_rect->left;
    blit.srcOffsets[0].y = src_rect->top;
    blit.srcOffsets[0].z = 0;
    blit.srcOffsets[1].x = src_rect->right;
    blit.srcOffsets[1].y = src_rect->bottom;
    blit.srcOffsets[1].z = 1;
    blit.dstSubresource = blit.srcSubresource;
    blit.dstOffsets[0].x = dst_rect->left;
    blit.dstOffsets[0].y = dst_rect->top;
    blit.dstOffsets[0].z = 0;
    blit.dstOffsets[1].x = dst_rect->right;
    blit.dstOffsets[1].y = dst_rect->bottom;
    blit.dstOffsets[1].z = 1;
    VK_CALL(vkCmdBlitImage(vk_command_buffer,
1132
            back_buffer_vk->image.vk_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
1133
            swapchain_vk->vk_images[image_idx], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1134
            1, &blit, filter));
1135

1136
    wined3d_context_vk_reference_texture(context_vk, back_buffer_vk);
1137 1138 1139 1140
    wined3d_context_vk_image_barrier(context_vk, vk_command_buffer,
            VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
            VK_ACCESS_TRANSFER_WRITE_BIT, 0,
            VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
1141
            swapchain_vk->vk_images[image_idx], &vk_range);
1142 1143 1144 1145 1146 1147 1148 1149 1150 1151

    if (desc->swap_effect == WINED3D_SWAP_EFFECT_DISCARD || desc->swap_effect == WINED3D_SWAP_EFFECT_FLIP_DISCARD)
        vk_layout = VK_IMAGE_LAYOUT_UNDEFINED;
    else
        vk_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
    wined3d_context_vk_image_barrier(context_vk, vk_command_buffer,
            VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
            VK_ACCESS_TRANSFER_READ_BIT,
            vk_access_mask_from_bind_flags(back_buffer_vk->t.resource.bind_flags),
            vk_layout, back_buffer_vk->layout,
1152
            back_buffer_vk->image.vk_image, &vk_range);
1153
    back_buffer_vk->bind_mask = 0;
1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167

    swapchain_vk->vk_semaphores[present_idx].command_buffer_id = context_vk->current_command_buffer.id;
    wined3d_context_vk_submit_command_buffer(context_vk,
            1, &swapchain_vk->vk_semaphores[present_idx].available, &wait_stage,
            1, &swapchain_vk->vk_semaphores[present_idx].presentable);

    present_desc.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
    present_desc.pNext = NULL;
    present_desc.waitSemaphoreCount = 1;
    present_desc.pWaitSemaphores = &swapchain_vk->vk_semaphores[present_idx].presentable;
    present_desc.swapchainCount = 1;
    present_desc.pSwapchains = &swapchain_vk->vk_swapchain;
    present_desc.pImageIndices = &image_idx;
    present_desc.pResults = NULL;
1168 1169 1170
    if ((vr = VK_CALL(vkQueuePresentKHR(device_vk->vk_queue, &present_desc))))
        WARN("Present returned vr %s.\n", wined3d_debug_vkresult(vr));
    return vr;
1171 1172 1173 1174 1175 1176
}

static void wined3d_swapchain_vk_rotate(struct wined3d_swapchain *swapchain, struct wined3d_context_vk *context_vk)
{
    struct wined3d_texture_sub_resource *sub_resource;
    struct wined3d_texture_vk *texture, *texture_prev;
1177
    struct wined3d_image_vk image0;
1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190
    VkDescriptorImageInfo vk_info0;
    VkImageLayout vk_layout0;
    DWORD locations0;
    unsigned int i;

    static const DWORD supported_locations = WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_RB_MULTISAMPLE;

    if (swapchain->state.desc.backbuffer_count < 2)
        return;

    texture_prev = wined3d_texture_vk(swapchain->back_buffers[0]);

    /* Back buffer 0 is already in the draw binding. */
1191
    image0 = texture_prev->image;
1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203
    vk_layout0 = texture_prev->layout;
    vk_info0 = texture_prev->default_image_info;
    locations0 = texture_prev->t.sub_resources[0].locations;

    for (i = 1; i < swapchain->state.desc.backbuffer_count; ++i)
    {
        texture = wined3d_texture_vk(swapchain->back_buffers[i]);
        sub_resource = &texture->t.sub_resources[0];

        if (!(sub_resource->locations & supported_locations))
            wined3d_texture_load_location(&texture->t, 0, &context_vk->c, texture->t.resource.draw_binding);

1204
        texture_prev->image = texture->image;
1205 1206 1207 1208 1209 1210 1211 1212 1213
        texture_prev->layout = texture->layout;
        texture_prev->default_image_info = texture->default_image_info;

        wined3d_texture_validate_location(&texture_prev->t, 0, sub_resource->locations & supported_locations);
        wined3d_texture_invalidate_location(&texture_prev->t, 0, ~(sub_resource->locations & supported_locations));

        texture_prev = texture;
    }

1214
    texture_prev->image = image0;
1215 1216 1217 1218 1219 1220 1221 1222 1223
    texture_prev->layout = vk_layout0;
    texture_prev->default_image_info = vk_info0;

    wined3d_texture_validate_location(&texture_prev->t, 0, locations0 & supported_locations);
    wined3d_texture_invalidate_location(&texture_prev->t, 0, ~(locations0 & supported_locations));

    device_invalidate_state(swapchain->device, STATE_FRAMEBUFFER);
}

1224 1225 1226
static void swapchain_vk_present(struct wined3d_swapchain *swapchain, const RECT *src_rect,
        const RECT *dst_rect, unsigned int swap_interval, uint32_t flags)
{
1227 1228 1229
    struct wined3d_swapchain_vk *swapchain_vk = wined3d_swapchain_vk(swapchain);
    struct wined3d_texture *back_buffer = swapchain->back_buffers[0];
    struct wined3d_context_vk *context_vk;
1230 1231
    VkResult vr;
    HRESULT hr;
1232 1233 1234

    context_vk = wined3d_context_vk(context_acquire(swapchain->device, back_buffer, 0));

1235 1236 1237 1238 1239
    if (!swapchain_vk->vk_swapchain || swapchain_present_is_partial_copy(swapchain, dst_rect))
    {
        swapchain_blit_gdi(swapchain, &context_vk->c, src_rect, dst_rect);
    }
    else
1240
    {
1241 1242
        wined3d_texture_load_location(back_buffer, 0, &context_vk->c, back_buffer->resource.draw_binding);

1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258
        if ((vr = wined3d_swapchain_vk_blit(swapchain_vk, context_vk, src_rect, dst_rect, swap_interval)))
        {
            if (vr == VK_ERROR_OUT_OF_DATE_KHR || vr == VK_SUBOPTIMAL_KHR)
            {
                if (FAILED(hr = wined3d_swapchain_vk_recreate(swapchain_vk)))
                    ERR("Failed to recreate swapchain, hr %#x.\n", hr);
                else if (vr == VK_ERROR_OUT_OF_DATE_KHR && (vr = wined3d_swapchain_vk_blit(
                        swapchain_vk, context_vk, src_rect, dst_rect, swap_interval)))
                    ERR("Failed to blit image, vr %s.\n", wined3d_debug_vkresult(vr));
            }
            else
            {
                ERR("Failed to blit image, vr %s.\n", wined3d_debug_vkresult(vr));
            }
        }
    }
1259 1260 1261 1262 1263 1264 1265 1266 1267

    wined3d_swapchain_vk_rotate(swapchain, context_vk);

    wined3d_texture_validate_location(swapchain->front_buffer, 0, WINED3D_LOCATION_DRAWABLE);
    wined3d_texture_invalidate_location(swapchain->front_buffer, 0, ~WINED3D_LOCATION_DRAWABLE);

    TRACE("Starting new frame.\n");

    context_release(&context_vk->c);
1268 1269 1270 1271 1272 1273
}

static const struct wined3d_swapchain_ops swapchain_vk_ops =
{
    swapchain_vk_present,
    swapchain_frontbuffer_updated,
Oliver Stieber's avatar
Oliver Stieber committed
1274
};
1275

1276
static void swapchain_gdi_frontbuffer_updated(struct wined3d_swapchain *swapchain)
1277
{
1278
    struct wined3d_dc_info *front;
1279 1280 1281 1282 1283
    POINT offset = {0, 0};
    HDC src_dc, dst_dc;
    RECT draw_rect;
    HWND window;

1284
    TRACE("swapchain %p.\n", swapchain);
1285

1286
    front = &swapchain->front_buffer->dc_info[0];
1287
    if (swapchain->palette)
1288
        wined3d_palette_apply_to_dc(swapchain->palette, front->dc);
1289

1290
    if (swapchain->front_buffer->resource.map_count)
1291 1292
        ERR("Trying to blit a mapped surface.\n");

1293 1294
    TRACE("Copying surface %p to screen.\n", front);

1295
    src_dc = front->dc;
1296 1297 1298 1299 1300
    window = swapchain->win_handle;
    dst_dc = GetDCEx(window, 0, DCX_CLIPSIBLINGS | DCX_CACHE);

    /* Front buffer coordinates are screen coordinates. Map them to the
     * destination window if not fullscreened. */
1301
    if (swapchain->state.desc.windowed)
1302 1303 1304 1305
        ClientToScreen(window, &offset);

    TRACE("offset %s.\n", wine_dbgstr_point(&offset));

1306 1307
    SetRect(&draw_rect, 0, 0, swapchain->front_buffer->resource.width,
            swapchain->front_buffer->resource.height);
1308
    IntersectRect(&draw_rect, &draw_rect, &swapchain->front_buffer_update);
1309 1310 1311 1312 1313

    BitBlt(dst_dc, draw_rect.left - offset.x, draw_rect.top - offset.y,
            draw_rect.right - draw_rect.left, draw_rect.bottom - draw_rect.top,
            src_dc, draw_rect.left, draw_rect.top, SRCCOPY);
    ReleaseDC(window, dst_dc);
1314 1315

    SetRectEmpty(&swapchain->front_buffer_update);
1316 1317
}

1318
static void swapchain_gdi_present(struct wined3d_swapchain *swapchain,
1319
        const RECT *src_rect, const RECT *dst_rect, unsigned int swap_interval, DWORD flags)
1320
{
1321
    struct wined3d_dc_info *front, *back;
1322 1323 1324
    HBITMAP bitmap;
    void *data;
    HDC dc;
1325

1326 1327
    front = &swapchain->front_buffer->dc_info[0];
    back = &swapchain->back_buffers[0]->dc_info[0];
1328 1329

    /* Flip the surface data. */
1330 1331
    dc = front->dc;
    bitmap = front->bitmap;
1332
    data = swapchain->front_buffer->resource.heap_memory;
1333

1334 1335
    front->dc = back->dc;
    front->bitmap = back->bitmap;
1336
    swapchain->front_buffer->resource.heap_memory = swapchain->back_buffers[0]->resource.heap_memory;
1337

1338 1339
    back->dc = dc;
    back->bitmap = bitmap;
1340
    swapchain->back_buffers[0]->resource.heap_memory = data;
1341

1342 1343 1344 1345
    SetRect(&swapchain->front_buffer_update, 0, 0,
            swapchain->front_buffer->resource.width,
            swapchain->front_buffer->resource.height);
    swapchain_gdi_frontbuffer_updated(swapchain);
1346 1347
}

1348
static const struct wined3d_swapchain_ops swapchain_no3d_ops =
1349
{
1350
    swapchain_gdi_present,
1351
    swapchain_gdi_frontbuffer_updated,
1352 1353
};

1354 1355 1356
static void wined3d_swapchain_apply_sample_count_override(const struct wined3d_swapchain *swapchain,
        enum wined3d_format_id format_id, enum wined3d_multisample_type *type, DWORD *quality)
{
1357
    const struct wined3d_adapter *adapter;
1358 1359 1360 1361 1362 1363 1364
    const struct wined3d_gl_info *gl_info;
    const struct wined3d_format *format;
    enum wined3d_multisample_type t;

    if (wined3d_settings.sample_count == ~0u)
        return;

1365 1366
    adapter = swapchain->device->adapter;
    gl_info = &adapter->gl_info;
1367
    if (!(format = wined3d_get_format(adapter, format_id, WINED3D_BIND_RENDER_TARGET)))
1368 1369 1370 1371 1372 1373 1374 1375 1376 1377
        return;

    if ((t = min(wined3d_settings.sample_count, gl_info->limits.samples)))
        while (!(format->multisample_types & 1u << (t - 1)))
            ++t;
    TRACE("Using sample count %u.\n", t);
    *type = t;
    *quality = 0;
}

1378 1379 1380 1381 1382 1383
void swapchain_set_max_frame_latency(struct wined3d_swapchain *swapchain, const struct wined3d_device *device)
{
    /* Subtract 1 for the implicit OpenGL latency. */
    swapchain->max_frame_latency = device->max_frame_latency >= 2 ? device->max_frame_latency - 1 : 1;
}

1384
static enum wined3d_format_id adapter_format_from_backbuffer_format(const struct wined3d_adapter *adapter,
1385 1386 1387 1388 1389 1390 1391 1392
        enum wined3d_format_id format_id)
{
    const struct wined3d_format *backbuffer_format;

    backbuffer_format = wined3d_get_format(adapter, format_id, WINED3D_BIND_RENDER_TARGET);
    return pixelformat_for_depth(backbuffer_format->byte_count * CHAR_BIT);
}

1393
static HRESULT wined3d_swapchain_state_init(struct wined3d_swapchain_state *state,
1394 1395
        const struct wined3d_swapchain_desc *desc, HWND window, struct wined3d *wined3d,
        struct wined3d_swapchain_state_parent *parent)
1396 1397 1398 1399 1400
{
    HRESULT hr;

    state->desc = *desc;

1401
    if (FAILED(hr = wined3d_output_get_display_mode(desc->output, &state->original_mode, NULL)))
1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412
    {
        ERR("Failed to get current display mode, hr %#x.\n", hr);
        return hr;
    }

    if (!desc->windowed)
    {
        if (desc->flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH)
        {
            state->d3d_mode.width = desc->backbuffer_width;
            state->d3d_mode.height = desc->backbuffer_height;
1413
            state->d3d_mode.format_id = adapter_format_from_backbuffer_format(desc->output->adapter,
1414
                    desc->backbuffer_format);
1415 1416 1417 1418 1419 1420 1421 1422 1423 1424
            state->d3d_mode.refresh_rate = desc->refresh_rate;
            state->d3d_mode.scanline_ordering = WINED3D_SCANLINE_ORDERING_UNKNOWN;
        }
        else
        {
            state->d3d_mode = state->original_mode;
        }
    }

    GetWindowRect(window, &state->original_window_rect);
1425
    state->wined3d = wined3d;
1426
    state->device_window = window;
1427
    state->parent = parent;
1428

1429
    if (desc->flags & WINED3D_SWAPCHAIN_REGISTER_STATE)
1430
        wined3d_swapchain_state_register(state);
1431

1432 1433 1434
    return hr;
}

1435
static HRESULT wined3d_swapchain_init(struct wined3d_swapchain *swapchain, struct wined3d_device *device,
1436 1437
        struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent,
        void *parent, const struct wined3d_parent_ops *parent_ops,
1438
        const struct wined3d_swapchain_ops *swapchain_ops)
1439
{
1440
    struct wined3d_resource_desc texture_desc;
1441
    struct wined3d_output_desc output_desc;
1442
    BOOL displaymode_set = FALSE;
1443
    DWORD texture_flags = 0;
1444
    HRESULT hr = E_FAIL;
1445
    RECT client_rect;
1446
    unsigned int i;
1447 1448
    HWND window;

1449 1450
    wined3d_mutex_lock();

1451
    if (desc->backbuffer_count > 1)
1452 1453 1454 1455 1456
    {
        FIXME("The application requested more than one back buffer, this is not properly supported.\n"
                "Please configure the application to use double buffering (1 back buffer) if possible.\n");
    }

1457 1458 1459 1460 1461
    if (desc->swap_effect != WINED3D_SWAP_EFFECT_DISCARD
            && desc->swap_effect != WINED3D_SWAP_EFFECT_SEQUENTIAL
            && desc->swap_effect != WINED3D_SWAP_EFFECT_COPY)
        FIXME("Unimplemented swap effect %#x.\n", desc->swap_effect);

1462
    window = desc->device_window ? desc->device_window : device->create_parms.focus_window;
1463
    if (FAILED(hr = wined3d_swapchain_state_init(&swapchain->state, desc, window, device->wined3d, state_parent)))
1464 1465
    {
        ERR("Failed to initialise swapchain state, hr %#x.\n", hr);
1466 1467
        wined3d_mutex_unlock();
        return hr;
1468
    }
1469

1470
    swapchain->swapchain_ops = swapchain_ops;
1471 1472
    swapchain->device = device;
    swapchain->parent = parent;
1473
    swapchain->parent_ops = parent_ops;
1474 1475
    swapchain->ref = 1;
    swapchain->win_handle = window;
1476
    swapchain->swap_interval = WINED3D_SWAP_INTERVAL_DEFAULT;
1477
    swapchain_set_max_frame_latency(swapchain, device);
1478 1479

    GetClientRect(window, &client_rect);
1480
    if (desc->windowed)
1481
    {
1482 1483
        TRACE("Client rect %s.\n", wine_dbgstr_rect(&client_rect));

1484
        if (!desc->backbuffer_width)
1485
        {
1486
            desc->backbuffer_width = client_rect.right ? client_rect.right : 8;
1487
            TRACE("Updating width to %u.\n", desc->backbuffer_width);
1488
        }
1489
        if (!desc->backbuffer_height)
1490
        {
1491
            desc->backbuffer_height = client_rect.bottom ? client_rect.bottom : 8;
1492
            TRACE("Updating height to %u.\n", desc->backbuffer_height);
1493 1494
        }

1495
        if (desc->backbuffer_format == WINED3DFMT_UNKNOWN)
1496
        {
1497 1498
            desc->backbuffer_format = swapchain->state.original_mode.format_id;
            TRACE("Updating format to %s.\n", debug_d3dformat(swapchain->state.original_mode.format_id));
1499 1500
        }
    }
1501 1502
    else
    {
1503
        if (FAILED(hr = wined3d_output_get_desc(desc->output, &output_desc)))
1504 1505 1506 1507 1508 1509 1510 1511
        {
            ERR("Failed to get output description, hr %#x.\n", hr);
            goto err;
        }

        wined3d_swapchain_state_setup_fullscreen(&swapchain->state, window,
                output_desc.desktop_rect.left, output_desc.desktop_rect.top, desc->backbuffer_width,
                desc->backbuffer_height);
1512
    }
1513 1514 1515
    swapchain->state.desc = *desc;
    wined3d_swapchain_apply_sample_count_override(swapchain, swapchain->state.desc.backbuffer_format,
            &swapchain->state.desc.multisample_type, &swapchain->state.desc.multisample_quality);
1516 1517

    TRACE("Creating front buffer.\n");
1518

1519
    texture_desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
1520 1521 1522
    texture_desc.format = swapchain->state.desc.backbuffer_format;
    texture_desc.multisample_type = swapchain->state.desc.multisample_type;
    texture_desc.multisample_quality = swapchain->state.desc.multisample_quality;
1523
    texture_desc.usage = 0;
1524 1525
    if (device->wined3d->flags & WINED3D_NO3D)
        texture_desc.usage |= WINED3DUSAGE_OWNDC;
1526
    texture_desc.bind_flags = 0;
1527 1528 1529 1530
    if (device->wined3d->flags & WINED3D_NO3D)
        texture_desc.access = WINED3D_RESOURCE_ACCESS_CPU;
    else
        texture_desc.access = WINED3D_RESOURCE_ACCESS_GPU;
1531
    if (swapchain->state.desc.flags & WINED3D_SWAPCHAIN_LOCKABLE_BACKBUFFER)
1532
        texture_desc.access |= WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W;
1533 1534
    texture_desc.width = swapchain->state.desc.backbuffer_width;
    texture_desc.height = swapchain->state.desc.backbuffer_height;
1535 1536
    texture_desc.depth = 1;
    texture_desc.size = 0;
1537

1538
    if (swapchain->state.desc.flags & WINED3D_SWAPCHAIN_GDI_COMPATIBLE)
1539 1540
        texture_flags |= WINED3D_TEXTURE_CREATE_GET_DC;

1541
    if (FAILED(hr = device->device_parent->ops->create_swapchain_texture(device->device_parent,
1542
            parent, &texture_desc, texture_flags, &swapchain->front_buffer)))
1543 1544 1545 1546 1547
    {
        WARN("Failed to create front buffer, hr %#x.\n", hr);
        goto err;
    }

1548
    wined3d_texture_set_swapchain(swapchain->front_buffer, swapchain);
1549
    if (!(device->wined3d->flags & WINED3D_NO3D))
1550
    {
1551
        wined3d_texture_validate_location(swapchain->front_buffer, 0, WINED3D_LOCATION_DRAWABLE);
1552
        wined3d_texture_invalidate_location(swapchain->front_buffer, 0, ~WINED3D_LOCATION_DRAWABLE);
1553
    }
1554 1555 1556 1557

    /* MSDN says we're only allowed a single fullscreen swapchain per device,
     * so we should really check to see if there is a fullscreen swapchain
     * already. Does a single head count as full screen? */
1558
    if (!desc->windowed && desc->flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH)
1559
    {
1560
        /* Change the display settings */
1561
        if (FAILED(hr = wined3d_output_set_display_mode(desc->output,
1562
                &swapchain->state.d3d_mode)))
1563
        {
1564 1565
            WARN("Failed to set display mode, hr %#x.\n", hr);
            goto err;
1566
        }
1567
        displaymode_set = TRUE;
1568 1569
    }

1570
    if (swapchain->state.desc.backbuffer_count > 0)
1571
    {
1572
        if (!(swapchain->back_buffers = heap_calloc(swapchain->state.desc.backbuffer_count,
1573
                sizeof(*swapchain->back_buffers))))
1574 1575 1576 1577 1578 1579
        {
            ERR("Failed to allocate backbuffer array memory.\n");
            hr = E_OUTOFMEMORY;
            goto err;
        }

1580
        texture_desc.bind_flags = swapchain->state.desc.backbuffer_bind_flags;
1581
        texture_desc.usage = 0;
1582 1583
        if (device->wined3d->flags & WINED3D_NO3D)
            texture_desc.usage |= WINED3DUSAGE_OWNDC;
1584
        for (i = 0; i < swapchain->state.desc.backbuffer_count; ++i)
1585 1586
        {
            TRACE("Creating back buffer %u.\n", i);
1587
            if (FAILED(hr = device->device_parent->ops->create_swapchain_texture(device->device_parent,
1588
                    parent, &texture_desc, texture_flags, &swapchain->back_buffers[i])))
1589 1590
            {
                WARN("Failed to create back buffer %u, hr %#x.\n", i, hr);
1591
                swapchain->state.desc.backbuffer_count = i;
1592 1593
                goto err;
            }
1594
            wined3d_texture_set_swapchain(swapchain->back_buffers[i], swapchain);
1595 1596 1597 1598
        }
    }

    /* Swapchains share the depth/stencil buffer, so only create a single depthstencil surface. */
1599
    if (desc->enable_auto_depth_stencil)
1600 1601
    {
        TRACE("Creating depth/stencil buffer.\n");
1602
        if (!device->auto_depth_stencil_view)
1603
        {
1604
            struct wined3d_view_desc desc;
1605
            struct wined3d_texture *ds;
1606

1607
            texture_desc.format = swapchain->state.desc.auto_depth_stencil_format;
1608
            texture_desc.usage = 0;
1609
            texture_desc.bind_flags = WINED3D_BIND_DEPTH_STENCIL;
1610 1611 1612 1613
            if (device->wined3d->flags & WINED3D_NO3D)
                texture_desc.access = WINED3D_RESOURCE_ACCESS_CPU;
            else
                texture_desc.access = WINED3D_RESOURCE_ACCESS_GPU;
1614

1615
            if (FAILED(hr = device->device_parent->ops->create_swapchain_texture(device->device_parent,
1616
                    device->device_parent, &texture_desc, 0, &ds)))
1617 1618 1619 1620 1621
            {
                WARN("Failed to create the auto depth/stencil surface, hr %#x.\n", hr);
                goto err;
            }

1622
            desc.format_id = ds->resource.format->id;
1623
            desc.flags = 0;
1624
            desc.u.texture.level_idx = 0;
1625
            desc.u.texture.level_count = 1;
1626 1627 1628 1629 1630
            desc.u.texture.layer_idx = 0;
            desc.u.texture.layer_count = 1;
            hr = wined3d_rendertarget_view_create(&desc, &ds->resource, NULL, &wined3d_null_parent_ops,
                    &device->auto_depth_stencil_view);
            wined3d_texture_decref(ds);
1631
            if (FAILED(hr))
1632
            {
1633
                ERR("Failed to create rendertarget view, hr %#x.\n", hr);
1634 1635 1636 1637 1638
                goto err;
            }
        }
    }

1639
    wined3d_swapchain_get_gamma_ramp(swapchain, &swapchain->orig_gamma);
1640

1641 1642
    wined3d_mutex_unlock();

1643 1644 1645 1646 1647
    return WINED3D_OK;

err:
    if (displaymode_set)
    {
1648
        if (FAILED(wined3d_restore_display_modes(device->wined3d)))
1649
            ERR("Failed to restore display mode.\n");
1650 1651
    }

1652
    if (swapchain->back_buffers)
1653
    {
1654
        for (i = 0; i < swapchain->state.desc.backbuffer_count; ++i)
1655
        {
1656
            if (swapchain->back_buffers[i])
1657
            {
1658 1659
                wined3d_texture_set_swapchain(swapchain->back_buffers[i], NULL);
                wined3d_texture_decref(swapchain->back_buffers[i]);
1660
            }
1661
        }
1662
        heap_free(swapchain->back_buffers);
1663 1664
    }

1665
    if (swapchain->front_buffer)
1666
    {
1667 1668
        wined3d_texture_set_swapchain(swapchain->front_buffer, NULL);
        wined3d_texture_decref(swapchain->front_buffer);
1669
    }
1670

1671
    wined3d_swapchain_state_cleanup(&swapchain->state);
1672 1673
    wined3d_mutex_unlock();

1674 1675 1676
    return hr;
}

1677
HRESULT wined3d_swapchain_no3d_init(struct wined3d_swapchain *swapchain_no3d, struct wined3d_device *device,
1678 1679
        struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent,
        void *parent, const struct wined3d_parent_ops *parent_ops)
1680
{
1681 1682
    TRACE("swapchain_no3d %p, device %p, desc %p, state_parent %p, parent %p, parent_ops %p.\n",
            swapchain_no3d, device, desc, state_parent, parent, parent_ops);
1683

1684 1685
    return wined3d_swapchain_init(swapchain_no3d, device, desc, state_parent, parent, parent_ops,
            &swapchain_no3d_ops);
1686 1687
}

1688
HRESULT wined3d_swapchain_gl_init(struct wined3d_swapchain_gl *swapchain_gl, struct wined3d_device *device,
1689 1690
        struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent,
        void *parent, const struct wined3d_parent_ops *parent_ops)
1691
{
1692 1693
    HRESULT hr;

1694 1695
    TRACE("swapchain_gl %p, device %p, desc %p, state_parent %p, parent %p, parent_ops %p.\n",
            swapchain_gl, device, desc, state_parent, parent, parent_ops);
1696

1697 1698
    if (FAILED(hr = wined3d_swapchain_init(&swapchain_gl->s, device, desc, state_parent, parent,
            parent_ops, &swapchain_gl_ops)))
1699 1700 1701 1702 1703 1704 1705
    {
        /* Cleanup any context that may have been created for the swapchain. */
        wined3d_cs_destroy_object(device->cs, wined3d_swapchain_gl_destroy_object, swapchain_gl);
        wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT);
    }

    return hr;
1706 1707
}

1708
HRESULT wined3d_swapchain_vk_init(struct wined3d_swapchain_vk *swapchain_vk, struct wined3d_device *device,
1709 1710
        struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent,
        void *parent, const struct wined3d_parent_ops *parent_ops)
1711
{
1712 1713
    HRESULT hr;

1714 1715 1716
    TRACE("swapchain_vk %p, device %p, desc %p, parent %p, parent_ops %p.\n",
            swapchain_vk, device, desc, parent, parent_ops);

1717 1718
    if (FAILED(hr = wined3d_swapchain_init(&swapchain_vk->s, device, desc, state_parent, parent,
            parent_ops, &swapchain_vk_ops)))
1719 1720 1721 1722 1723 1724 1725 1726 1727
        return hr;

    if (swapchain_vk->s.win_handle == GetDesktopWindow())
    {
        WARN("Creating a desktop window swapchain.\n");
        return hr;
    }

    if (FAILED(hr = wined3d_swapchain_vk_create_vulkan_swapchain(swapchain_vk)))
1728
        WARN("Failed to create a Vulkan swapchain, hr %#x.\n", hr);
1729

1730
    return WINED3D_OK;
1731 1732
}

1733 1734 1735 1736
HRESULT CDECL wined3d_swapchain_create(struct wined3d_device *device,
        struct wined3d_swapchain_desc *desc, struct wined3d_swapchain_state_parent *state_parent,
        void *parent, const struct wined3d_parent_ops *parent_ops,
        struct wined3d_swapchain **swapchain)
1737 1738 1739 1740
{
    struct wined3d_swapchain *object;
    HRESULT hr;

1741
    if (FAILED(hr = device->adapter->adapter_ops->adapter_create_swapchain(device,
1742
            desc, state_parent, parent, parent_ops, &object)))
1743 1744
        return hr;

1745 1746 1747 1748 1749 1750
    if (desc->flags & WINED3D_SWAPCHAIN_IMPLICIT)
    {
        wined3d_mutex_lock();
        if (FAILED(hr = wined3d_device_set_implicit_swapchain(device, object)))
        {
            wined3d_cs_finish(device->cs, WINED3D_CS_QUEUE_DEFAULT);
1751
            device->adapter->adapter_ops->adapter_destroy_swapchain(object);
1752 1753 1754 1755 1756 1757
            wined3d_mutex_unlock();
            return hr;
        }
        wined3d_mutex_unlock();
    }

1758 1759
    *swapchain = object;

1760
    return hr;
1761 1762
}

1763
static struct wined3d_context_gl *wined3d_swapchain_gl_create_context(struct wined3d_swapchain_gl *swapchain_gl)
1764
{
1765
    struct wined3d_device *device = swapchain_gl->s.device;
1766
    struct wined3d_context_gl *context_gl;
1767

1768
    TRACE("Creating a new context for swapchain %p, thread %u.\n", swapchain_gl, GetCurrentThreadId());
1769

1770 1771 1772 1773 1774 1775 1776 1777
    wined3d_from_cs(device->cs);

    if (!(context_gl = heap_alloc_zero(sizeof(*context_gl))))
    {
        ERR("Failed to allocate context memory.\n");
        return NULL;
    }

1778
    if (FAILED(wined3d_context_gl_init(context_gl, swapchain_gl)))
1779
    {
1780 1781
        WARN("Failed to initialise context.\n");
        heap_free(context_gl);
1782 1783
        return NULL;
    }
1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794

    if (!device_context_add(device, &context_gl->c))
    {
        ERR("Failed to add the newly created context to the context list.\n");
        wined3d_context_gl_destroy(context_gl);
        return NULL;
    }

    TRACE("Created context %p.\n", context_gl);

    context_release(&context_gl->c);
1795

1796 1797
    if (!wined3d_array_reserve((void **)&swapchain_gl->contexts, &swapchain_gl->contexts_size,
            swapchain_gl->context_count + 1, sizeof(*swapchain_gl->contexts)))
1798
    {
1799 1800
        ERR("Failed to allocate new context array memory.\n");
        wined3d_context_gl_destroy(context_gl);
1801 1802
        return NULL;
    }
1803
    swapchain_gl->contexts[swapchain_gl->context_count++] = context_gl;
1804

1805
    return context_gl;
1806
}
1807

1808
void wined3d_swapchain_gl_destroy_contexts(struct wined3d_swapchain_gl *swapchain_gl)
1809 1810 1811
{
    unsigned int i;

1812 1813
    TRACE("swapchain_gl %p.\n", swapchain_gl);

1814
    for (i = 0; i < swapchain_gl->context_count; ++i)
1815
    {
1816
        wined3d_context_gl_destroy(swapchain_gl->contexts[i]);
1817
    }
1818 1819 1820 1821
    heap_free(swapchain_gl->contexts);
    swapchain_gl->contexts_size = 0;
    swapchain_gl->context_count = 0;
    swapchain_gl->contexts = NULL;
1822 1823
}

1824
struct wined3d_context_gl *wined3d_swapchain_gl_get_context(struct wined3d_swapchain_gl *swapchain_gl)
1825 1826 1827 1828
{
    DWORD tid = GetCurrentThreadId();
    unsigned int i;

1829
    for (i = 0; i < swapchain_gl->context_count; ++i)
1830
    {
1831 1832
        if (swapchain_gl->contexts[i]->tid == tid)
            return swapchain_gl->contexts[i];
1833 1834
    }

1835 1836
    /* Create a new context for the thread. */
    return wined3d_swapchain_gl_create_context(swapchain_gl);
1837 1838
}

1839
HDC wined3d_swapchain_gl_get_backup_dc(struct wined3d_swapchain_gl *swapchain_gl)
1840
{
1841
    if (!swapchain_gl->backup_dc)
1842
    {
1843
        TRACE("Creating the backup window for swapchain %p.\n", swapchain_gl);
1844

1845
        if (!(swapchain_gl->backup_wnd = CreateWindowA(WINED3D_OPENGL_WINDOW_CLASS_NAME, "WineD3D fake window",
1846 1847 1848 1849 1850 1851
                WS_OVERLAPPEDWINDOW, 10, 10, 10, 10, NULL, NULL, NULL, NULL)))
        {
            ERR("Failed to create a window.\n");
            return NULL;
        }

1852
        if (!(swapchain_gl->backup_dc = GetDC(swapchain_gl->backup_wnd)))
1853 1854
        {
            ERR("Failed to get a DC.\n");
1855 1856
            DestroyWindow(swapchain_gl->backup_wnd);
            swapchain_gl->backup_wnd = NULL;
1857 1858 1859 1860
            return NULL;
        }
    }

1861
    return swapchain_gl->backup_dc;
1862
}
1863 1864 1865 1866 1867

void swapchain_update_draw_bindings(struct wined3d_swapchain *swapchain)
{
    UINT i;

1868
    wined3d_resource_update_draw_binding(&swapchain->front_buffer->resource);
1869

1870
    for (i = 0; i < swapchain->state.desc.backbuffer_count; ++i)
1871
    {
1872
        wined3d_resource_update_draw_binding(&swapchain->back_buffers[i]->resource);
1873 1874
    }
}
1875 1876 1877

void wined3d_swapchain_activate(struct wined3d_swapchain *swapchain, BOOL activate)
{
1878
    struct wined3d_device *device = swapchain->device;
1879
    HWND window = swapchain->state.device_window;
1880
    struct wined3d_output_desc output_desc;
1881
    unsigned int screensaver_active;
1882
    struct wined3d_output *output;
1883
    BOOL focus_messages, filter;
1884
    HRESULT hr;
1885

1886 1887 1888 1889 1890
    /* This code is not protected by the wined3d mutex, so it may run while
     * wined3d_device_reset is active. Testing on Windows shows that changing
     * focus during resets and resetting during focus change events causes
     * the application to crash with an invalid memory access. */

1891 1892
    if (!(focus_messages = device->wined3d->flags & WINED3D_FOCUS_MESSAGES))
        filter = wined3d_filter_messages(window, TRUE);
1893

1894 1895
    if (activate)
    {
1896 1897 1898 1899
        SystemParametersInfoW(SPI_GETSCREENSAVEACTIVE, 0, &screensaver_active, 0);
        if ((device->restore_screensaver = !!screensaver_active))
            SystemParametersInfoW(SPI_SETSCREENSAVEACTIVE, FALSE, NULL, 0);

1900
        if (!(device->create_parms.flags & WINED3DCREATE_NOWINDOWCHANGES))
1901 1902 1903 1904 1905 1906 1907 1908
        {
            /* The d3d versions do not agree on the exact messages here. D3d8 restores
             * the window but leaves the size untouched, d3d9 sets the size on an
             * invisible window, generates messages but doesn't change the window
             * properties. The implementation follows d3d9.
             *
             * Guild Wars 1 wants a WINDOWPOSCHANGED message on the device window to
             * resume drawing after a focus loss. */
1909 1910 1911 1912 1913 1914 1915
            output = wined3d_swapchain_get_output(swapchain);
            if (!output)
            {
                ERR("Failed to get output from swapchain %p.\n", swapchain);
                return;
            }

1916 1917 1918 1919 1920 1921
            if (SUCCEEDED(hr = wined3d_output_get_desc(output, &output_desc)))
                SetWindowPos(window, NULL, output_desc.desktop_rect.left,
                        output_desc.desktop_rect.top, swapchain->state.desc.backbuffer_width,
                        swapchain->state.desc.backbuffer_height, SWP_NOACTIVATE | SWP_NOZORDER);
            else
                ERR("Failed to get output description, hr %#x.\n", hr);
1922 1923
        }

1924
        if (device->wined3d->flags & WINED3D_RESTORE_MODE_ON_ACTIVATE)
1925
        {
1926 1927 1928 1929 1930 1931 1932
            output = wined3d_swapchain_get_output(swapchain);
            if (!output)
            {
                ERR("Failed to get output from swapchain %p.\n", swapchain);
                return;
            }

1933
            if (FAILED(hr = wined3d_output_set_display_mode(output,
1934
                    &swapchain->state.d3d_mode)))
1935
                ERR("Failed to set display mode, hr %#x.\n", hr);
1936
        }
1937 1938 1939

        if (swapchain == device->swapchains[0])
            device->device_parent->ops->activate(device->device_parent, TRUE);
1940 1941 1942
    }
    else
    {
1943 1944 1945 1946 1947 1948
        if (device->restore_screensaver)
        {
            SystemParametersInfoW(SPI_SETSCREENSAVEACTIVE, TRUE, NULL, 0);
            device->restore_screensaver = FALSE;
        }

1949 1950
        if (FAILED(hr = wined3d_restore_display_modes(device->wined3d)))
            ERR("Failed to restore display modes, hr %#x.\n", hr);
1951 1952 1953

        swapchain->reapply_mode = TRUE;

1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964
        /* Some DDraw apps (Deus Ex: GOTY, and presumably all UT 1 based games) destroy the device
         * during window minimization. Do our housekeeping now, as the device may not exist after
         * the ShowWindow call.
         *
         * In d3d9, the device is marked lost after the window is minimized. If we find an app
         * that needs this behavior (e.g. because it calls TestCooperativeLevel in the window proc)
         * we'll have to control this via a create flag. Note that the device and swapchain are not
         * safe to access after the ShowWindow call. */
        if (swapchain == device->swapchains[0])
            device->device_parent->ops->activate(device->device_parent, FALSE);

1965 1966
        if (!(device->create_parms.flags & WINED3DCREATE_NOWINDOWCHANGES) && IsWindowVisible(window))
            ShowWindow(window, SW_MINIMIZE);
1967
    }
1968

1969
    if (!focus_messages)
1970
        wined3d_filter_messages(window, filter);
1971
}
1972

1973
HRESULT CDECL wined3d_swapchain_resize_buffers(struct wined3d_swapchain *swapchain, unsigned int buffer_count,
1974 1975 1976
        unsigned int width, unsigned int height, enum wined3d_format_id format_id,
        enum wined3d_multisample_type multisample_type, unsigned int multisample_quality)
{
1977
    struct wined3d_swapchain_desc *desc = &swapchain->state.desc;
1978 1979 1980 1981 1982 1983 1984
    BOOL update_desc = FALSE;

    TRACE("swapchain %p, buffer_count %u, width %u, height %u, format %s, "
            "multisample_type %#x, multisample_quality %#x.\n",
            swapchain, buffer_count, width, height, debug_d3dformat(format_id),
            multisample_type, multisample_quality);

1985 1986
    wined3d_swapchain_apply_sample_count_override(swapchain, format_id, &multisample_type, &multisample_quality);

1987
    if (buffer_count && buffer_count != desc->backbuffer_count)
1988 1989
        FIXME("Cannot change the back buffer count yet.\n");

1990
    wined3d_cs_finish(swapchain->device->cs, WINED3D_CS_QUEUE_DEFAULT);
1991

1992 1993
    if (!width || !height)
    {
1994 1995
        RECT client_rect;

1996 1997 1998 1999
        /* The application is requesting that either the swapchain width or
         * height be set to the corresponding dimension in the window's
         * client rect. */

2000
        if (!GetClientRect(swapchain->state.device_window, &client_rect))
2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012
        {
            ERR("Failed to get client rect, last error %#x.\n", GetLastError());
            return WINED3DERR_INVALIDCALL;
        }

        if (!width)
            width = client_rect.right;

        if (!height)
            height = client_rect.bottom;
    }

2013
    if (width != desc->backbuffer_width || height != desc->backbuffer_height)
2014
    {
2015 2016
        desc->backbuffer_width = width;
        desc->backbuffer_height = height;
2017 2018 2019 2020 2021
        update_desc = TRUE;
    }

    if (format_id == WINED3DFMT_UNKNOWN)
    {
2022
        if (!desc->windowed)
2023
            return WINED3DERR_INVALIDCALL;
2024
        format_id = swapchain->state.original_mode.format_id;
2025 2026
    }

2027
    if (format_id != desc->backbuffer_format)
2028
    {
2029
        desc->backbuffer_format = format_id;
2030 2031 2032
        update_desc = TRUE;
    }

2033 2034
    if (multisample_type != desc->multisample_type
            || multisample_quality != desc->multisample_quality)
2035
    {
2036 2037
        desc->multisample_type = multisample_type;
        desc->multisample_quality = multisample_quality;
2038 2039 2040 2041 2042 2043 2044 2045
        update_desc = TRUE;
    }

    if (update_desc)
    {
        HRESULT hr;
        UINT i;

2046
        if (FAILED(hr = wined3d_texture_update_desc(swapchain->front_buffer, 0, desc->backbuffer_width,
2047 2048
                desc->backbuffer_height, desc->backbuffer_format,
                desc->multisample_type, desc->multisample_quality, NULL, 0)))
2049 2050
            return hr;

2051
        for (i = 0; i < desc->backbuffer_count; ++i)
2052
        {
2053
            if (FAILED(hr = wined3d_texture_update_desc(swapchain->back_buffers[i], 0, desc->backbuffer_width,
2054 2055
                    desc->backbuffer_height, desc->backbuffer_format,
                    desc->multisample_type, desc->multisample_quality, NULL, 0)))
2056 2057 2058 2059 2060 2061 2062 2063
                return hr;
        }
    }

    swapchain_update_draw_bindings(swapchain);

    return WINED3D_OK;
}
2064

2065
static HRESULT wined3d_swapchain_state_set_display_mode(struct wined3d_swapchain_state *state,
2066
        struct wined3d_output *output, struct wined3d_display_mode *mode)
2067 2068 2069
{
    HRESULT hr;

2070
    if (state->desc.flags & WINED3D_SWAPCHAIN_USE_CLOSEST_MATCHING_MODE)
2071
    {
2072
        if (FAILED(hr = wined3d_output_find_closest_matching_mode(output, mode)))
2073 2074 2075 2076 2077
        {
            WARN("Failed to find closest matching mode, hr %#x.\n", hr);
        }
    }

2078 2079
    if (output != state->desc.output)
    {
2080
        if (FAILED(hr = wined3d_restore_display_modes(state->wined3d)))
2081
        {
2082
            WARN("Failed to restore display modes, hr %#x.\n", hr);
2083 2084 2085 2086 2087 2088 2089 2090 2091 2092
            return hr;
        }

        if (FAILED(hr = wined3d_output_get_display_mode(output, &state->original_mode, NULL)))
        {
            WARN("Failed to get current display mode, hr %#x.\n", hr);
            return hr;
        }
    }

2093
    if (FAILED(hr = wined3d_output_set_display_mode(output, mode)))
2094 2095 2096 2097 2098 2099 2100 2101
    {
        WARN("Failed to set display mode, hr %#x.\n", hr);
        return WINED3DERR_INVALIDCALL;
    }

    return WINED3D_OK;
}

2102
HRESULT CDECL wined3d_swapchain_state_resize_target(struct wined3d_swapchain_state *state,
2103
        const struct wined3d_display_mode *mode)
2104
{
2105
    struct wined3d_display_mode actual_mode;
2106
    struct wined3d_output_desc output_desc;
2107
    RECT original_window_rect, window_rect;
2108
    int x, y, width, height;
2109
    HWND window;
2110 2111
    HRESULT hr;

2112
    TRACE("state %p, mode %p.\n", state, mode);
2113

2114 2115
    wined3d_mutex_lock();

2116
    window = state->device_window;
2117

2118
    if (state->desc.windowed)
2119
    {
2120
        SetRect(&window_rect, 0, 0, mode->width, mode->height);
2121
        AdjustWindowRectEx(&window_rect,
2122 2123 2124
                GetWindowLongW(window, GWL_STYLE), FALSE,
                GetWindowLongW(window, GWL_EXSTYLE));
        GetWindowRect(window, &original_window_rect);
2125 2126 2127 2128 2129

        x = original_window_rect.left;
        y = original_window_rect.top;
        width = window_rect.right - window_rect.left;
        height = window_rect.bottom - window_rect.top;
2130
    }
2131
    else
2132
    {
2133
        if (state->desc.flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH)
2134
        {
2135
            actual_mode = *mode;
2136 2137
            if (FAILED(hr = wined3d_swapchain_state_set_display_mode(state, state->desc.output,
                    &actual_mode)))
2138 2139 2140 2141 2142
            {
                ERR("Failed to set display mode, hr %#x.\n", hr);
                wined3d_mutex_unlock();
                return hr;
            }
2143
        }
2144

2145
        if (FAILED(hr = wined3d_output_get_desc(state->desc.output, &output_desc)))
2146
        {
2147
            ERR("Failed to get output description, hr %#x.\n", hr);
2148
            wined3d_mutex_unlock();
2149
            return hr;
2150 2151
        }

2152 2153 2154 2155
        x = output_desc.desktop_rect.left;
        y = output_desc.desktop_rect.top;
        width = output_desc.desktop_rect.right - output_desc.desktop_rect.left;
        height = output_desc.desktop_rect.bottom - output_desc.desktop_rect.top;
2156 2157
    }

2158 2159
    wined3d_mutex_unlock();

2160
    MoveWindow(window, x, y, width, height, TRUE);
2161 2162 2163 2164

    return WINED3D_OK;
}

2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181
static LONG fullscreen_style(LONG style)
{
    /* Make sure the window is managed, otherwise we won't get keyboard input. */
    style |= WS_POPUP | WS_SYSMENU;
    style &= ~(WS_CAPTION | WS_THICKFRAME);

    return style;
}

static LONG fullscreen_exstyle(LONG exstyle)
{
    /* Filter out window decorations. */
    exstyle &= ~(WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE);

    return exstyle;
}

2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212
struct wined3d_window_state
{
    HWND window;
    HWND window_pos_after;
    LONG style, exstyle;
    int x, y, width, height;
    uint32_t flags;
    bool set_style;
};

static DWORD WINAPI wined3d_set_window_state(void *ctx)
{
    struct wined3d_window_state *s = ctx;
    bool filter;

    filter = wined3d_filter_messages(s->window, TRUE);

    if (s->set_style)
    {
        SetWindowLongW(s->window, GWL_STYLE, s->style);
        SetWindowLongW(s->window, GWL_EXSTYLE, s->exstyle);
    }
    SetWindowPos(s->window, s->window_pos_after, s->x, s->y, s->width, s->height, s->flags);

    wined3d_filter_messages(s->window, filter);

    heap_free(s);

    return 0;
}

2213
HRESULT wined3d_swapchain_state_setup_fullscreen(struct wined3d_swapchain_state *state,
2214
        HWND window, int x, int y, int width, int height)
2215
{
2216 2217 2218
    struct wined3d_window_state *s;
    DWORD window_tid, tid;
    HANDLE thread;
2219 2220 2221 2222 2223 2224 2225 2226 2227

    TRACE("Setting up window %p for fullscreen mode.\n", window);

    if (!IsWindow(window))
    {
        WARN("%p is not a valid window.\n", window);
        return WINED3DERR_NOTAVAILABLE;
    }

2228 2229 2230 2231 2232 2233 2234 2235 2236
    if (!(s = heap_alloc(sizeof(*s))))
        return E_OUTOFMEMORY;
    s->window = window;
    s->window_pos_after = HWND_TOPMOST;
    s->x = x;
    s->y = y;
    s->width = width;
    s->height = height;

2237 2238 2239 2240 2241 2242
    if (state->style || state->exstyle)
    {
        ERR("Changing the window style for window %p, but another style (%08x, %08x) is already stored.\n",
                window, state->style, state->exstyle);
    }

2243
    s->flags = SWP_FRAMECHANGED | SWP_NOACTIVATE;
2244
    if (state->desc.flags & WINED3D_SWAPCHAIN_NO_WINDOW_CHANGES)
2245
        s->flags |= SWP_NOZORDER;
2246
    else
2247
        s->flags |= SWP_SHOWWINDOW;
2248

2249 2250 2251
    state->style = GetWindowLongW(window, GWL_STYLE);
    state->exstyle = GetWindowLongW(window, GWL_EXSTYLE);

2252 2253 2254
    s->style = fullscreen_style(state->style);
    s->exstyle = fullscreen_exstyle(state->exstyle);
    s->set_style = true;
2255 2256

    TRACE("Old style was %08x, %08x, setting to %08x, %08x.\n",
2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271
            state->style, state->exstyle, s->style, s->exstyle);

    window_tid = GetWindowThreadProcessId(window, NULL);
    tid = GetCurrentThreadId();

    TRACE("Window %p belongs to thread %#x.\n", window, window_tid);
    /* If the window belongs to a different thread, modifying the style and/or
     * position can potentially deadlock if that thread isn't processing
     * messages. */
    if (window_tid == tid)
        wined3d_set_window_state(s);
    else if (!(thread = CreateThread(NULL, 0, wined3d_set_window_state, s, 0, NULL)))
        ERR("Failed to create thread.\n");
    else
        CloseHandle(thread);
2272 2273 2274 2275

    return WINED3D_OK;
}

2276
void wined3d_swapchain_state_restore_from_fullscreen(struct wined3d_swapchain_state *state,
2277 2278
        HWND window, const RECT *window_rect)
{
2279 2280
    struct wined3d_window_state *s;
    DWORD window_tid, tid;
2281
    LONG style, exstyle;
2282
    HANDLE thread;
2283 2284 2285 2286

    if (!state->style && !state->exstyle)
        return;

2287 2288 2289 2290 2291 2292 2293
    if (!(s = heap_alloc(sizeof(*s))))
        return;

    s->window = window;
    s->window_pos_after = NULL;
    s->flags = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE;

2294 2295 2296
    if ((state->desc.flags & WINED3D_SWAPCHAIN_RESTORE_WINDOW_STATE)
            && !(state->desc.flags & WINED3D_SWAPCHAIN_NO_WINDOW_CHANGES))
    {
2297 2298 2299
        s->window_pos_after = (state->exstyle & WS_EX_TOPMOST) ? HWND_TOPMOST : HWND_NOTOPMOST;
        s->flags |= (state->style & WS_VISIBLE) ? SWP_SHOWWINDOW : SWP_HIDEWINDOW;
        s->flags &= ~SWP_NOZORDER;
2300 2301
    }

2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316
    style = GetWindowLongW(window, GWL_STYLE);
    exstyle = GetWindowLongW(window, GWL_EXSTYLE);

    /* These flags are set by wined3d_device_setup_fullscreen_window, not the
     * application, and we want to ignore them in the test below, since it's
     * not the application's fault that they changed. Additionally, we want to
     * preserve the current status of these flags (i.e. don't restore them) to
     * more closely emulate the behavior of Direct3D, which leaves these flags
     * alone when returning to windowed mode. */
    state->style ^= (state->style ^ style) & WS_VISIBLE;
    state->exstyle ^= (state->exstyle ^ exstyle) & WS_EX_TOPMOST;

    TRACE("Restoring window style of window %p to %08x, %08x.\n",
            window, state->style, state->exstyle);

2317 2318
    s->style = state->style;
    s->exstyle = state->exstyle;
2319 2320 2321 2322
    /* Only restore the style if the application didn't modify it during the
     * fullscreen phase. Some applications change it before calling Reset()
     * when switching between windowed and fullscreen modes (HL2), some
     * depend on the original style (Eve Online). */
2323
    s->set_style = style == fullscreen_style(state->style) && exstyle == fullscreen_exstyle(state->exstyle);
2324 2325

    if (window_rect)
2326 2327 2328 2329 2330 2331
    {
        s->x = window_rect->left;
        s->y = window_rect->top;
        s->width = window_rect->right - window_rect->left;
        s->height = window_rect->bottom - window_rect->top;
    }
2332
    else
2333 2334 2335 2336
    {
        s->x = s->y = s->width = s->height = 0;
        s->flags |= (SWP_NOMOVE | SWP_NOSIZE);
    }
2337

2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350
    window_tid = GetWindowThreadProcessId(window, NULL);
    tid = GetCurrentThreadId();

    TRACE("Window %p belongs to thread %#x.\n", window, window_tid);
    /* If the window belongs to a different thread, modifying the style and/or
     * position can potentially deadlock if that thread isn't processing
     * messages. */
    if (window_tid == tid)
        wined3d_set_window_state(s);
    else if (!(thread = CreateThread(NULL, 0, wined3d_set_window_state, s, 0, NULL)))
        ERR("Failed to create thread.\n");
    else
        CloseHandle(thread);
2351 2352 2353 2354 2355 2356

    /* Delete the old values. */
    state->style = 0;
    state->exstyle = 0;
}

2357
HRESULT CDECL wined3d_swapchain_state_set_fullscreen(struct wined3d_swapchain_state *state,
2358
        const struct wined3d_swapchain_desc *swapchain_desc,
2359
        const struct wined3d_display_mode *mode)
2360
{
2361
    struct wined3d_display_mode actual_mode;
2362
    struct wined3d_output_desc output_desc;
2363
    BOOL windowed = state->desc.windowed;
2364 2365
    HRESULT hr;

2366
    TRACE("state %p, swapchain_desc %p, mode %p.\n", state, swapchain_desc, mode);
2367

2368
    if (state->desc.flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH)
2369
    {
2370 2371 2372
        if (mode)
        {
            actual_mode = *mode;
2373 2374 2375
            if (FAILED(hr = wined3d_swapchain_state_set_display_mode(state, swapchain_desc->output,
                    &actual_mode)))
                return hr;
2376 2377
        }
        else
2378 2379 2380
        {
            if (!swapchain_desc->windowed)
            {
2381 2382 2383
                actual_mode.width = swapchain_desc->backbuffer_width;
                actual_mode.height = swapchain_desc->backbuffer_height;
                actual_mode.refresh_rate = swapchain_desc->refresh_rate;
2384
                actual_mode.format_id = adapter_format_from_backbuffer_format(swapchain_desc->output->adapter,
2385
                        swapchain_desc->backbuffer_format);
2386
                actual_mode.scanline_ordering = WINED3D_SCANLINE_ORDERING_UNKNOWN;
2387 2388 2389
                if (FAILED(hr = wined3d_swapchain_state_set_display_mode(state, swapchain_desc->output,
                        &actual_mode)))
                    return hr;
2390 2391 2392
            }
            else
            {
2393 2394 2395 2396 2397
                if (FAILED(hr = wined3d_restore_display_modes(state->wined3d)))
                {
                    WARN("Failed to restore display modes for all outputs, hr %#x.\n", hr);
                    return hr;
                }
2398 2399 2400 2401 2402 2403 2404 2405
            }
        }
    }
    else
    {
        if (mode)
            WARN("WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH is not set, ignoring mode.\n");

2406 2407
        if (FAILED(hr = wined3d_output_get_display_mode(swapchain_desc->output, &actual_mode,
                NULL)))
2408
        {
2409 2410
            ERR("Failed to get display mode, hr %#x.\n", hr);
            return WINED3DERR_INVALIDCALL;
2411 2412 2413 2414 2415
        }
    }

    if (!swapchain_desc->windowed)
    {
2416 2417 2418
        unsigned int width = actual_mode.width;
        unsigned int height = actual_mode.height;

2419
        if (FAILED(hr = wined3d_output_get_desc(swapchain_desc->output, &output_desc)))
2420 2421 2422 2423 2424
        {
            ERR("Failed to get output description, hr %#x.\n", hr);
            return hr;
        }

2425
        if (state->desc.windowed)
2426
        {
2427
            /* Switch from windowed to fullscreen */
2428 2429
            if (FAILED(hr = wined3d_swapchain_state_setup_fullscreen(state, state->device_window,
                    output_desc.desktop_rect.left, output_desc.desktop_rect.top, width, height)))
2430
                return hr;
2431 2432 2433
        }
        else
        {
2434
            HWND window = state->device_window;
2435
            BOOL filter;
2436

2437 2438
            /* Fullscreen -> fullscreen mode change */
            filter = wined3d_filter_messages(window, TRUE);
2439 2440
            MoveWindow(window, output_desc.desktop_rect.left, output_desc.desktop_rect.top, width,
                    height, TRUE);
2441 2442
            ShowWindow(window, SW_SHOW);
            wined3d_filter_messages(window, filter);
2443
        }
2444
        state->d3d_mode = actual_mode;
2445
    }
2446
    else if (!state->desc.windowed)
2447 2448
    {
        /* Fullscreen -> windowed switch */
2449
        RECT *window_rect = NULL;
2450
        if (state->desc.flags & WINED3D_SWAPCHAIN_RESTORE_WINDOW_RECT)
2451
            window_rect = &state->original_window_rect;
2452
        wined3d_swapchain_state_restore_from_fullscreen(state, state->device_window, window_rect);
2453 2454
    }

2455
    state->desc.output = swapchain_desc->output;
2456
    state->desc.windowed = swapchain_desc->windowed;
2457

2458 2459 2460
    if (windowed != state->desc.windowed)
        state->parent->ops->windowed_state_changed(state->parent, state->desc.windowed);

2461 2462
    return WINED3D_OK;
}
2463

2464 2465 2466 2467 2468 2469 2470
BOOL CDECL wined3d_swapchain_state_is_windowed(const struct wined3d_swapchain_state *state)
{
    TRACE("state %p.\n", state);

    return state->desc.windowed;
}

2471 2472
void CDECL wined3d_swapchain_state_destroy(struct wined3d_swapchain_state *state)
{
2473
    wined3d_swapchain_state_cleanup(state);
2474 2475 2476 2477
    heap_free(state);
}

HRESULT CDECL wined3d_swapchain_state_create(const struct wined3d_swapchain_desc *desc,
2478 2479
        HWND window, struct wined3d *wined3d, struct wined3d_swapchain_state_parent *state_parent,
        struct wined3d_swapchain_state **state)
2480 2481
{
    struct wined3d_swapchain_state *s;
2482 2483
    HRESULT hr;

2484
    TRACE("desc %p, window %p, wined3d %p, state %p.\n", desc, window, wined3d, state);
2485 2486 2487 2488

    if (!(s = heap_alloc_zero(sizeof(*s))))
        return E_OUTOFMEMORY;

2489
    if (FAILED(hr = wined3d_swapchain_state_init(s, desc, window, wined3d, state_parent)))
2490 2491
    {
        heap_free(s);
2492
        return hr;
2493
    }
2494 2495 2496

    *state = s;

2497
    return hr;
2498
}