xref: /reactos/dll/directx/wine/wined3d/swapchain.c (revision b917d826)
1 /*
2  * Copyright 2002-2003 Jason Edmeades
3  * Copyright 2002-2003 Raphael Junqueira
4  * Copyright 2005 Oliver Stieber
5  * Copyright 2007-2008 Stefan Dösinger for CodeWeavers
6  * Copyright 2011 Henri Verbeet for CodeWeavers
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22 
23 #include "wined3d_private.h"
24 
25 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
26 WINE_DECLARE_DEBUG_CHANNEL(fps);
27 
28 static void wined3d_swapchain_destroy_object(void *object)
29 {
30     swapchain_destroy_contexts(object);
31 }
32 
33 static void swapchain_cleanup(struct wined3d_swapchain *swapchain)
34 {
35     HRESULT hr;
36     UINT i;
37 
38     TRACE("Destroying swapchain %p.\n", swapchain);
39 
40     wined3d_swapchain_set_gamma_ramp(swapchain, 0, &swapchain->orig_gamma);
41 
42     /* Release the swapchain's draw buffers. Make sure swapchain->back_buffers[0]
43      * is the last buffer to be destroyed, FindContext() depends on that. */
44     if (swapchain->front_buffer)
45     {
46         wined3d_texture_set_swapchain(swapchain->front_buffer, NULL);
47         if (wined3d_texture_decref(swapchain->front_buffer))
48             WARN("Something's still holding the front buffer (%p).\n", swapchain->front_buffer);
49         swapchain->front_buffer = NULL;
50     }
51 
52     if (swapchain->back_buffers)
53     {
54         i = swapchain->desc.backbuffer_count;
55 
56         while (i--)
57         {
58             wined3d_texture_set_swapchain(swapchain->back_buffers[i], NULL);
59             if (wined3d_texture_decref(swapchain->back_buffers[i]))
60                 WARN("Something's still holding back buffer %u (%p).\n", i, swapchain->back_buffers[i]);
61         }
62         HeapFree(GetProcessHeap(), 0, swapchain->back_buffers);
63         swapchain->back_buffers = NULL;
64     }
65 
66     wined3d_cs_destroy_object(swapchain->device->cs, wined3d_swapchain_destroy_object, swapchain);
67     swapchain->device->cs->ops->finish(swapchain->device->cs, WINED3D_CS_QUEUE_DEFAULT);
68 
69     /* Restore the screen resolution if we rendered in fullscreen.
70      * This will restore the screen resolution to what it was before creating
71      * the swapchain. In case of d3d8 and d3d9 this will be the original
72      * desktop resolution. In case of d3d7 this will be a NOP because ddraw
73      * sets the resolution before starting up Direct3D, thus orig_width and
74      * orig_height will be equal to the modes in the presentation params. */
75     if (!swapchain->desc.windowed && swapchain->desc.auto_restore_display_mode)
76     {
77         if (FAILED(hr = wined3d_set_adapter_display_mode(swapchain->device->wined3d,
78                 swapchain->device->adapter->ordinal, &swapchain->original_mode)))
79             ERR("Failed to restore display mode, hr %#x.\n", hr);
80 
81         if (swapchain->desc.flags & WINED3D_SWAPCHAIN_RESTORE_WINDOW_RECT)
82         {
83             wined3d_device_restore_fullscreen_window(swapchain->device, swapchain->device_window,
84                     &swapchain->original_window_rect);
85             wined3d_device_release_focus_window(swapchain->device);
86         }
87     }
88 
89     if (swapchain->backup_dc)
90     {
91         TRACE("Destroying backup wined3d window %p, dc %p.\n", swapchain->backup_wnd, swapchain->backup_dc);
92 
93         wined3d_release_dc(swapchain->backup_wnd, swapchain->backup_dc);
94         DestroyWindow(swapchain->backup_wnd);
95     }
96 }
97 
98 ULONG CDECL wined3d_swapchain_incref(struct wined3d_swapchain *swapchain)
99 {
100     ULONG refcount = InterlockedIncrement(&swapchain->ref);
101 
102     TRACE("%p increasing refcount to %u.\n", swapchain, refcount);
103 
104     return refcount;
105 }
106 
107 ULONG CDECL wined3d_swapchain_decref(struct wined3d_swapchain *swapchain)
108 {
109     ULONG refcount = InterlockedDecrement(&swapchain->ref);
110 
111     TRACE("%p decreasing refcount to %u.\n", swapchain, refcount);
112 
113     if (!refcount)
114     {
115         struct wined3d_device *device = swapchain->device;
116 
117         device->cs->ops->finish(device->cs, WINED3D_CS_QUEUE_DEFAULT);
118 
119         swapchain_cleanup(swapchain);
120         swapchain->parent_ops->wined3d_object_destroyed(swapchain->parent);
121         HeapFree(GetProcessHeap(), 0, swapchain);
122     }
123 
124     return refcount;
125 }
126 
127 void * CDECL wined3d_swapchain_get_parent(const struct wined3d_swapchain *swapchain)
128 {
129     TRACE("swapchain %p.\n", swapchain);
130 
131     return swapchain->parent;
132 }
133 
134 void CDECL wined3d_swapchain_set_window(struct wined3d_swapchain *swapchain, HWND window)
135 {
136     if (!window)
137         window = swapchain->device_window;
138     if (window == swapchain->win_handle)
139         return;
140 
141     TRACE("Setting swapchain %p window from %p to %p.\n",
142             swapchain, swapchain->win_handle, window);
143     swapchain->win_handle = window;
144 }
145 
146 HRESULT CDECL wined3d_swapchain_present(struct wined3d_swapchain *swapchain,
147         const RECT *src_rect, const RECT *dst_rect, HWND dst_window_override, DWORD flags)
148 {
149     RECT s, d;
150 
151     TRACE("swapchain %p, src_rect %s, dst_rect %s, dst_window_override %p, flags %#x.\n",
152             swapchain, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect),
153             dst_window_override, flags);
154 
155     if (flags)
156         FIXME("Ignoring flags %#x.\n", flags);
157 
158     if (!swapchain->back_buffers)
159     {
160         WARN("Swapchain doesn't have a backbuffer, returning WINED3DERR_INVALIDCALL\n");
161         return WINED3DERR_INVALIDCALL;
162     }
163 
164     if (!src_rect)
165     {
166         SetRect(&s, 0, 0, swapchain->desc.backbuffer_width,
167                 swapchain->desc.backbuffer_height);
168         src_rect = &s;
169     }
170 
171     if (!dst_rect)
172     {
173         GetClientRect(swapchain->win_handle, &d);
174         dst_rect = &d;
175     }
176 
177     wined3d_cs_emit_present(swapchain->device->cs, swapchain, src_rect,
178             dst_rect, dst_window_override, flags);
179 
180     return WINED3D_OK;
181 }
182 
183 HRESULT CDECL wined3d_swapchain_get_front_buffer_data(const struct wined3d_swapchain *swapchain,
184         struct wined3d_texture *dst_texture, unsigned int sub_resource_idx)
185 {
186     RECT src_rect, dst_rect;
187 
188     TRACE("swapchain %p, dst_texture %p, sub_resource_idx %u.\n", swapchain, dst_texture, sub_resource_idx);
189 
190     SetRect(&src_rect, 0, 0, swapchain->front_buffer->resource.width, swapchain->front_buffer->resource.height);
191     dst_rect = src_rect;
192 
193     if (swapchain->desc.windowed)
194     {
195         MapWindowPoints(swapchain->win_handle, NULL, (POINT *)&dst_rect, 2);
196         FIXME("Using destination rect %s in windowed mode, this is likely wrong.\n",
197                 wine_dbgstr_rect(&dst_rect));
198     }
199 
200     return wined3d_texture_blt(dst_texture, sub_resource_idx, &dst_rect,
201             swapchain->front_buffer, 0, &src_rect, 0, NULL, WINED3D_TEXF_POINT);
202 }
203 
204 struct wined3d_texture * CDECL wined3d_swapchain_get_back_buffer(const struct wined3d_swapchain *swapchain,
205         UINT back_buffer_idx)
206 {
207     TRACE("swapchain %p, back_buffer_idx %u.\n",
208             swapchain, back_buffer_idx);
209 
210     /* Return invalid if there is no backbuffer array, otherwise it will
211      * crash when ddraw is used (there swapchain->back_buffers is always
212      * NULL). We need this because this function is called from
213      * stateblock_init_default_state() to get the default scissorrect
214      * dimensions. */
215     if (!swapchain->back_buffers || back_buffer_idx >= swapchain->desc.backbuffer_count)
216     {
217         WARN("Invalid back buffer index.\n");
218         /* Native d3d9 doesn't set NULL here, just as wine's d3d9. But set it
219          * here in wined3d to avoid problems in other libs. */
220         return NULL;
221     }
222 
223     TRACE("Returning back buffer %p.\n", swapchain->back_buffers[back_buffer_idx]);
224 
225     return swapchain->back_buffers[back_buffer_idx];
226 }
227 
228 HRESULT CDECL wined3d_swapchain_get_raster_status(const struct wined3d_swapchain *swapchain,
229         struct wined3d_raster_status *raster_status)
230 {
231     TRACE("swapchain %p, raster_status %p.\n", swapchain, raster_status);
232 
233     return wined3d_get_adapter_raster_status(swapchain->device->wined3d,
234             swapchain->device->adapter->ordinal, raster_status);
235 }
236 
237 HRESULT CDECL wined3d_swapchain_get_display_mode(const struct wined3d_swapchain *swapchain,
238         struct wined3d_display_mode *mode, enum wined3d_display_rotation *rotation)
239 {
240     HRESULT hr;
241 
242     TRACE("swapchain %p, mode %p, rotation %p.\n", swapchain, mode, rotation);
243 
244     hr = wined3d_get_adapter_display_mode(swapchain->device->wined3d,
245             swapchain->device->adapter->ordinal, mode, rotation);
246 
247     TRACE("Returning w %u, h %u, refresh rate %u, format %s.\n",
248             mode->width, mode->height, mode->refresh_rate, debug_d3dformat(mode->format_id));
249 
250     return hr;
251 }
252 
253 struct wined3d_device * CDECL wined3d_swapchain_get_device(const struct wined3d_swapchain *swapchain)
254 {
255     TRACE("swapchain %p.\n", swapchain);
256 
257     return swapchain->device;
258 }
259 
260 void CDECL wined3d_swapchain_get_desc(const struct wined3d_swapchain *swapchain,
261         struct wined3d_swapchain_desc *desc)
262 {
263     TRACE("swapchain %p, desc %p.\n", swapchain, desc);
264 
265     *desc = swapchain->desc;
266 }
267 
268 HRESULT CDECL wined3d_swapchain_set_gamma_ramp(const struct wined3d_swapchain *swapchain,
269         DWORD flags, const struct wined3d_gamma_ramp *ramp)
270 {
271     HDC dc;
272 
273     TRACE("swapchain %p, flags %#x, ramp %p.\n", swapchain, flags, ramp);
274 
275     if (flags)
276         FIXME("Ignoring flags %#x.\n", flags);
277 
278     dc = GetDCEx(swapchain->device_window, 0, DCX_USESTYLE | DCX_CACHE);
279     SetDeviceGammaRamp(dc, (void *)ramp);
280     ReleaseDC(swapchain->device_window, dc);
281 
282     return WINED3D_OK;
283 }
284 
285 void CDECL wined3d_swapchain_set_palette(struct wined3d_swapchain *swapchain, struct wined3d_palette *palette)
286 {
287     TRACE("swapchain %p, palette %p.\n", swapchain, palette);
288     swapchain->palette = palette;
289 }
290 
291 HRESULT CDECL wined3d_swapchain_get_gamma_ramp(const struct wined3d_swapchain *swapchain,
292         struct wined3d_gamma_ramp *ramp)
293 {
294     HDC dc;
295 
296     TRACE("swapchain %p, ramp %p.\n", swapchain, ramp);
297 
298     dc = GetDCEx(swapchain->device_window, 0, DCX_USESTYLE | DCX_CACHE);
299     GetDeviceGammaRamp(dc, ramp);
300     ReleaseDC(swapchain->device_window, dc);
301 
302     return WINED3D_OK;
303 }
304 
305 /* A GL context is provided by the caller */
306 static void swapchain_blit(const struct wined3d_swapchain *swapchain,
307         struct wined3d_context *context, const RECT *src_rect, const RECT *dst_rect)
308 {
309     struct wined3d_texture *texture = swapchain->back_buffers[0];
310     struct wined3d_surface *back_buffer = texture->sub_resources[0].u.surface;
311     struct wined3d_device *device = swapchain->device;
312     enum wined3d_texture_filter_type filter;
313     DWORD location;
314 
315     TRACE("swapchain %p, context %p, src_rect %s, dst_rect %s.\n",
316             swapchain, context, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect));
317 
318     if ((src_rect->right - src_rect->left == dst_rect->right - dst_rect->left
319             && src_rect->bottom - src_rect->top == dst_rect->bottom - dst_rect->top)
320             || is_complex_fixup(texture->resource.format->color_fixup))
321         filter = WINED3D_TEXF_NONE;
322     else
323         filter = WINED3D_TEXF_LINEAR;
324 
325     location = WINED3D_LOCATION_TEXTURE_RGB;
326     if (texture->resource.multisample_type)
327         location = WINED3D_LOCATION_RB_RESOLVED;
328 
329     wined3d_texture_validate_location(texture, 0, WINED3D_LOCATION_DRAWABLE);
330     device->blitter->ops->blitter_blit(device->blitter, WINED3D_BLIT_OP_COLOR_BLIT, context, back_buffer,
331             location, src_rect, back_buffer, WINED3D_LOCATION_DRAWABLE, dst_rect, NULL, filter);
332     wined3d_texture_invalidate_location(texture, 0, WINED3D_LOCATION_DRAWABLE);
333 }
334 
335 /* Context activation is done by the caller. */
336 static void wined3d_swapchain_rotate(struct wined3d_swapchain *swapchain, struct wined3d_context *context)
337 {
338     struct wined3d_texture_sub_resource *sub_resource;
339     struct wined3d_texture *texture, *texture_prev;
340     struct gl_texture tex0;
341     GLuint rb0;
342     DWORD locations0;
343     unsigned int i;
344     static const DWORD supported_locations = WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_RB_MULTISAMPLE;
345 
346     if (swapchain->desc.backbuffer_count < 2 || !swapchain->render_to_fbo)
347         return;
348 
349     texture_prev = swapchain->back_buffers[0];
350 
351     /* Back buffer 0 is already in the draw binding. */
352     tex0 = texture_prev->texture_rgb;
353     rb0 = texture_prev->rb_multisample;
354     locations0 = texture_prev->sub_resources[0].locations;
355 
356     for (i = 1; i < swapchain->desc.backbuffer_count; ++i)
357     {
358         texture = swapchain->back_buffers[i];
359         sub_resource = &texture->sub_resources[0];
360 
361         if (!(sub_resource->locations & supported_locations))
362             wined3d_texture_load_location(texture, 0, context, texture->resource.draw_binding);
363 
364         texture_prev->texture_rgb = texture->texture_rgb;
365         texture_prev->rb_multisample = texture->rb_multisample;
366 
367         wined3d_texture_validate_location(texture_prev, 0, sub_resource->locations & supported_locations);
368         wined3d_texture_invalidate_location(texture_prev, 0, ~(sub_resource->locations & supported_locations));
369 
370         texture_prev = texture;
371     }
372 
373     texture_prev->texture_rgb = tex0;
374     texture_prev->rb_multisample = rb0;
375 
376     wined3d_texture_validate_location(texture_prev, 0, locations0 & supported_locations);
377     wined3d_texture_invalidate_location(texture_prev, 0, ~(locations0 & supported_locations));
378 
379     device_invalidate_state(swapchain->device, STATE_FRAMEBUFFER);
380 }
381 
382 static void swapchain_gl_present(struct wined3d_swapchain *swapchain,
383         const RECT *src_rect, const RECT *dst_rect, DWORD flags)
384 {
385     struct wined3d_texture *back_buffer = swapchain->back_buffers[0];
386     const struct wined3d_fb_state *fb = &swapchain->device->cs->fb;
387     const struct wined3d_gl_info *gl_info;
388     struct wined3d_texture *logo_texture;
389     struct wined3d_context *context;
390     BOOL render_to_fbo;
391 
392     context = context_acquire(swapchain->device, back_buffer, 0);
393     if (!context->valid)
394     {
395         context_release(context);
396         WARN("Invalid context, skipping present.\n");
397         return;
398     }
399 
400     gl_info = context->gl_info;
401 
402     if ((logo_texture = swapchain->device->logo_texture))
403     {
404         RECT rect = {0, 0, logo_texture->resource.width, logo_texture->resource.height};
405 
406         /* Blit the logo into the upper left corner of the drawable. */
407         wined3d_texture_blt(back_buffer, 0, &rect, logo_texture, 0, &rect,
408                 WINED3D_BLT_SRC_CKEY, NULL, WINED3D_TEXF_POINT);
409     }
410 
411     if (swapchain->device->bCursorVisible && swapchain->device->cursor_texture
412             && !swapchain->device->hardwareCursor)
413     {
414         RECT dst_rect =
415         {
416             swapchain->device->xScreenSpace - swapchain->device->xHotSpot,
417             swapchain->device->yScreenSpace - swapchain->device->yHotSpot,
418             swapchain->device->xScreenSpace + swapchain->device->cursorWidth - swapchain->device->xHotSpot,
419             swapchain->device->yScreenSpace + swapchain->device->cursorHeight - swapchain->device->yHotSpot,
420         };
421         RECT src_rect =
422         {
423             0, 0,
424             swapchain->device->cursor_texture->resource.width,
425             swapchain->device->cursor_texture->resource.height
426         };
427         const RECT clip_rect = {0, 0, back_buffer->resource.width, back_buffer->resource.height};
428 
429         TRACE("Rendering the software cursor.\n");
430 
431         if (swapchain->desc.windowed)
432             MapWindowPoints(NULL, swapchain->win_handle, (POINT *)&dst_rect, 2);
433         if (wined3d_clip_blit(&clip_rect, &dst_rect, &src_rect))
434             wined3d_texture_blt(back_buffer, 0, &dst_rect,
435                     swapchain->device->cursor_texture, 0, &src_rect,
436                     WINED3D_BLT_ALPHA_TEST, NULL, WINED3D_TEXF_POINT);
437     }
438 
439     TRACE("Presenting HDC %p.\n", context->hdc);
440 
441     if (!(render_to_fbo = swapchain->render_to_fbo)
442             && (src_rect->left || src_rect->top
443             || src_rect->right != swapchain->desc.backbuffer_width
444             || src_rect->bottom != swapchain->desc.backbuffer_height
445             || dst_rect->left || dst_rect->top
446             || dst_rect->right != swapchain->desc.backbuffer_width
447             || dst_rect->bottom != swapchain->desc.backbuffer_height))
448         render_to_fbo = TRUE;
449 
450     /* Rendering to a window of different size, presenting partial rectangles,
451      * or rendering to a different window needs help from FBO_blit or a textured
452      * draw. Render the swapchain to a FBO in the future.
453      *
454      * Note that FBO_blit from the backbuffer to the frontbuffer cannot solve
455      * all these issues - this fails if the window is smaller than the backbuffer.
456      */
457     if (!swapchain->render_to_fbo && render_to_fbo && wined3d_settings.offscreen_rendering_mode == ORM_FBO)
458     {
459         wined3d_texture_load_location(back_buffer, 0, context, WINED3D_LOCATION_TEXTURE_RGB);
460         wined3d_texture_invalidate_location(back_buffer, 0, WINED3D_LOCATION_DRAWABLE);
461         swapchain->render_to_fbo = TRUE;
462         swapchain_update_draw_bindings(swapchain);
463     }
464     else
465     {
466         wined3d_texture_load_location(back_buffer, 0, context, back_buffer->resource.draw_binding);
467     }
468 
469     if (swapchain->render_to_fbo)
470     {
471         static unsigned int once;
472 
473         if (swapchain->desc.swap_effect == WINED3D_SWAP_EFFECT_FLIP && !once++)
474             FIXME("WINED3D_SWAP_EFFECT_FLIP not implemented.\n");
475 
476         swapchain_blit(swapchain, context, src_rect, dst_rect);
477     }
478 
479     if (swapchain->num_contexts > 1)
480         gl_info->gl_ops.gl.p_glFinish();
481 
482     /* call wglSwapBuffers through the gl table to avoid confusing the Steam overlay */
483     gl_info->gl_ops.wgl.p_wglSwapBuffers(context->hdc);
484 
485     wined3d_swapchain_rotate(swapchain, context);
486 
487     TRACE("SwapBuffers called, Starting new frame\n");
488     /* FPS support */
489     if (TRACE_ON(fps))
490     {
491         DWORD time = GetTickCount();
492         ++swapchain->frames;
493 
494         /* every 1.5 seconds */
495         if (time - swapchain->prev_time > 1500)
496         {
497             TRACE_(fps)("%p @ approx %.2ffps\n",
498                     swapchain, 1000.0 * swapchain->frames / (time - swapchain->prev_time));
499             swapchain->prev_time = time;
500             swapchain->frames = 0;
501         }
502     }
503 
504     wined3d_texture_validate_location(swapchain->front_buffer, 0, WINED3D_LOCATION_DRAWABLE);
505     wined3d_texture_invalidate_location(swapchain->front_buffer, 0, ~WINED3D_LOCATION_DRAWABLE);
506     /* If the swapeffect is DISCARD, the back buffer is undefined. That means the SYSMEM
507      * and INTEXTURE copies can keep their old content if they have any defined content.
508      * If the swapeffect is COPY, the content remains the same.
509      *
510      * The FLIP swap effect is not implemented yet. We could mark WINED3D_LOCATION_DRAWABLE
511      * up to date and hope WGL flipped front and back buffers and read this data into
512      * the FBO. Don't bother about this for now. */
513     if (swapchain->desc.swap_effect == WINED3D_SWAP_EFFECT_DISCARD)
514         wined3d_texture_validate_location(swapchain->back_buffers[swapchain->desc.backbuffer_count - 1],
515                 0, WINED3D_LOCATION_DISCARDED);
516 
517     if (fb->depth_stencil)
518     {
519         struct wined3d_surface *ds = wined3d_rendertarget_view_get_surface(fb->depth_stencil);
520 
521         if (ds && (swapchain->desc.flags & WINED3D_SWAPCHAIN_DISCARD_DEPTHSTENCIL
522                 || ds->container->flags & WINED3D_TEXTURE_DISCARD))
523             wined3d_texture_validate_location(ds->container,
524                     fb->depth_stencil->sub_resource_idx, WINED3D_LOCATION_DISCARDED);
525     }
526 
527     context_release(context);
528 }
529 
530 static void swapchain_gl_frontbuffer_updated(struct wined3d_swapchain *swapchain)
531 {
532     struct wined3d_texture *front_buffer = swapchain->front_buffer;
533     struct wined3d_context *context;
534 
535     context = context_acquire(swapchain->device, front_buffer, 0);
536     wined3d_texture_load_location(front_buffer, 0, context, front_buffer->resource.draw_binding);
537     context_release(context);
538     SetRectEmpty(&swapchain->front_buffer_update);
539 }
540 
541 static const struct wined3d_swapchain_ops swapchain_gl_ops =
542 {
543     swapchain_gl_present,
544     swapchain_gl_frontbuffer_updated,
545 };
546 
547 static void swapchain_gdi_frontbuffer_updated(struct wined3d_swapchain *swapchain)
548 {
549     struct wined3d_surface *front;
550     POINT offset = {0, 0};
551     HDC src_dc, dst_dc;
552     RECT draw_rect;
553     HWND window;
554 
555     TRACE("swapchain %p.\n", swapchain);
556 
557     front = swapchain->front_buffer->sub_resources[0].u.surface;
558     if (swapchain->palette)
559         wined3d_palette_apply_to_dc(swapchain->palette, front->dc);
560 
561     if (front->container->resource.map_count)
562         ERR("Trying to blit a mapped surface.\n");
563 
564     TRACE("Copying surface %p to screen.\n", front);
565 
566     src_dc = front->dc;
567     window = swapchain->win_handle;
568     dst_dc = GetDCEx(window, 0, DCX_CLIPSIBLINGS | DCX_CACHE);
569 
570     /* Front buffer coordinates are screen coordinates. Map them to the
571      * destination window if not fullscreened. */
572     if (swapchain->desc.windowed)
573         ClientToScreen(window, &offset);
574 
575     TRACE("offset %s.\n", wine_dbgstr_point(&offset));
576 
577     SetRect(&draw_rect, 0, 0, swapchain->front_buffer->resource.width,
578             swapchain->front_buffer->resource.height);
579     IntersectRect(&draw_rect, &draw_rect, &swapchain->front_buffer_update);
580 
581     BitBlt(dst_dc, draw_rect.left - offset.x, draw_rect.top - offset.y,
582             draw_rect.right - draw_rect.left, draw_rect.bottom - draw_rect.top,
583             src_dc, draw_rect.left, draw_rect.top, SRCCOPY);
584     ReleaseDC(window, dst_dc);
585 
586     SetRectEmpty(&swapchain->front_buffer_update);
587 }
588 
589 static void swapchain_gdi_present(struct wined3d_swapchain *swapchain,
590         const RECT *src_rect, const RECT *dst_rect, DWORD flags)
591 {
592     struct wined3d_surface *front, *back;
593     HBITMAP bitmap;
594     void *data;
595     HDC dc;
596 
597     front = swapchain->front_buffer->sub_resources[0].u.surface;
598     back = swapchain->back_buffers[0]->sub_resources[0].u.surface;
599 
600     /* Flip the surface data. */
601     dc = front->dc;
602     bitmap = front->bitmap;
603     data = front->container->resource.heap_memory;
604 
605     front->dc = back->dc;
606     front->bitmap = back->bitmap;
607     front->container->resource.heap_memory = back->container->resource.heap_memory;
608 
609     back->dc = dc;
610     back->bitmap = bitmap;
611     back->container->resource.heap_memory = data;
612 
613     /* FPS support */
614     if (TRACE_ON(fps))
615     {
616         static LONG prev_time, frames;
617         DWORD time = GetTickCount();
618 
619         ++frames;
620 
621         /* every 1.5 seconds */
622         if (time - prev_time > 1500)
623         {
624             TRACE_(fps)("@ approx %.2ffps\n", 1000.0 * frames / (time - prev_time));
625             prev_time = time;
626             frames = 0;
627         }
628     }
629 
630     SetRect(&swapchain->front_buffer_update, 0, 0,
631             swapchain->front_buffer->resource.width,
632             swapchain->front_buffer->resource.height);
633     swapchain_gdi_frontbuffer_updated(swapchain);
634 }
635 
636 static const struct wined3d_swapchain_ops swapchain_gdi_ops =
637 {
638     swapchain_gdi_present,
639     swapchain_gdi_frontbuffer_updated,
640 };
641 
642 static void swapchain_update_render_to_fbo(struct wined3d_swapchain *swapchain)
643 {
644     if (wined3d_settings.offscreen_rendering_mode != ORM_FBO)
645         return;
646 
647     if (!swapchain->desc.backbuffer_count)
648     {
649         TRACE("Single buffered rendering.\n");
650         swapchain->render_to_fbo = FALSE;
651         return;
652     }
653 
654     TRACE("Rendering to FBO.\n");
655     swapchain->render_to_fbo = TRUE;
656 }
657 
658 static void wined3d_swapchain_apply_sample_count_override(const struct wined3d_swapchain *swapchain,
659         enum wined3d_format_id format_id, enum wined3d_multisample_type *type, DWORD *quality)
660 {
661     const struct wined3d_gl_info *gl_info;
662     const struct wined3d_format *format;
663     enum wined3d_multisample_type t;
664 
665     if (wined3d_settings.sample_count == ~0u)
666         return;
667 
668     gl_info = &swapchain->device->adapter->gl_info;
669     if (!(format = wined3d_get_format(gl_info, format_id, WINED3DUSAGE_RENDERTARGET)))
670         return;
671 
672     if ((t = min(wined3d_settings.sample_count, gl_info->limits.samples)))
673         while (!(format->multisample_types & 1u << (t - 1)))
674             ++t;
675     TRACE("Using sample count %u.\n", t);
676     *type = t;
677     *quality = 0;
678 }
679 
680 static void wined3d_swapchain_update_swap_interval_cs(void *object)
681 {
682     struct wined3d_swapchain *swapchain = object;
683     const struct wined3d_gl_info *gl_info;
684     struct wined3d_context *context;
685     int swap_interval;
686 
687     context = context_acquire(swapchain->device, swapchain->front_buffer, 0);
688     gl_info = context->gl_info;
689 
690     switch (swapchain->desc.swap_interval)
691     {
692         case WINED3DPRESENT_INTERVAL_IMMEDIATE:
693             swap_interval = 0;
694             break;
695         case WINED3DPRESENT_INTERVAL_DEFAULT:
696         case WINED3DPRESENT_INTERVAL_ONE:
697             swap_interval = 1;
698             break;
699         case WINED3DPRESENT_INTERVAL_TWO:
700             swap_interval = 2;
701             break;
702         case WINED3DPRESENT_INTERVAL_THREE:
703             swap_interval = 3;
704             break;
705         case WINED3DPRESENT_INTERVAL_FOUR:
706             swap_interval = 4;
707             break;
708         default:
709             FIXME("Unhandled present interval %#x.\n", swapchain->desc.swap_interval);
710             swap_interval = 1;
711     }
712 
713     if (gl_info->supported[WGL_EXT_SWAP_CONTROL])
714     {
715         if (!GL_EXTCALL(wglSwapIntervalEXT(swap_interval)))
716             ERR("wglSwapIntervalEXT failed to set swap interval %d for context %p, last error %#x\n",
717                 swap_interval, context, GetLastError());
718     }
719 
720     context_release(context);
721 }
722 
723 static void wined3d_swapchain_cs_init(void *object)
724 {
725     struct wined3d_swapchain *swapchain = object;
726     const struct wined3d_gl_info *gl_info;
727     unsigned int i;
728 
729     static const enum wined3d_format_id formats[] =
730     {
731         WINED3DFMT_D24_UNORM_S8_UINT,
732         WINED3DFMT_D32_UNORM,
733         WINED3DFMT_R24_UNORM_X8_TYPELESS,
734         WINED3DFMT_D16_UNORM,
735         WINED3DFMT_S1_UINT_D15_UNORM,
736     };
737 
738     gl_info = &swapchain->device->adapter->gl_info;
739 
740     /* Without ORM_FBO, switching the depth/stencil format is hard. Always
741      * request a depth/stencil buffer in the likely case it's needed later. */
742     for (i = 0; i < ARRAY_SIZE(formats); ++i)
743     {
744         swapchain->ds_format = wined3d_get_format(gl_info, formats[i], WINED3DUSAGE_DEPTHSTENCIL);
745         if ((swapchain->context[0] = context_create(swapchain, swapchain->front_buffer, swapchain->ds_format)))
746             break;
747         TRACE("Depth stencil format %s is not supported, trying next format.\n", debug_d3dformat(formats[i]));
748     }
749 
750     if (!swapchain->context[0])
751     {
752         WARN("Failed to create context.\n");
753         return;
754     }
755     swapchain->num_contexts = 1;
756 
757     if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
758             && (!swapchain->desc.enable_auto_depth_stencil
759             || swapchain->desc.auto_depth_stencil_format != swapchain->ds_format->id))
760         FIXME("Add OpenGL context recreation support.\n");
761 
762     context_release(swapchain->context[0]);
763 
764     wined3d_swapchain_update_swap_interval_cs(swapchain);
765 }
766 
767 static HRESULT swapchain_init(struct wined3d_swapchain *swapchain, struct wined3d_device *device,
768         struct wined3d_swapchain_desc *desc, void *parent, const struct wined3d_parent_ops *parent_ops)
769 {
770     const struct wined3d_adapter *adapter = device->adapter;
771     struct wined3d_resource_desc texture_desc;
772     BOOL displaymode_set = FALSE;
773     DWORD texture_flags = 0;
774     RECT client_rect;
775     HWND window;
776     HRESULT hr;
777     UINT i;
778 
779     if (desc->backbuffer_count > 1)
780     {
781         FIXME("The application requested more than one back buffer, this is not properly supported.\n"
782                 "Please configure the application to use double buffering (1 back buffer) if possible.\n");
783     }
784 
785     if (device->wined3d->flags & WINED3D_NO3D)
786         swapchain->swapchain_ops = &swapchain_gdi_ops;
787     else
788         swapchain->swapchain_ops = &swapchain_gl_ops;
789 
790     window = desc->device_window ? desc->device_window : device->create_parms.focus_window;
791 
792     swapchain->device = device;
793     swapchain->parent = parent;
794     swapchain->parent_ops = parent_ops;
795     swapchain->ref = 1;
796     swapchain->win_handle = window;
797     swapchain->device_window = window;
798 
799     if (FAILED(hr = wined3d_get_adapter_display_mode(device->wined3d,
800             adapter->ordinal, &swapchain->original_mode, NULL)))
801     {
802         ERR("Failed to get current display mode, hr %#x.\n", hr);
803         goto err;
804     }
805     GetWindowRect(window, &swapchain->original_window_rect);
806 
807     GetClientRect(window, &client_rect);
808     if (desc->windowed)
809     {
810         if (!desc->backbuffer_width)
811         {
812             desc->backbuffer_width = client_rect.right;
813             TRACE("Updating width to %u.\n", desc->backbuffer_width);
814         }
815 
816         if (!desc->backbuffer_height)
817         {
818             desc->backbuffer_height = client_rect.bottom;
819             TRACE("Updating height to %u.\n", desc->backbuffer_height);
820         }
821 
822         if (desc->backbuffer_format == WINED3DFMT_UNKNOWN)
823         {
824             desc->backbuffer_format = swapchain->original_mode.format_id;
825             TRACE("Updating format to %s.\n", debug_d3dformat(swapchain->original_mode.format_id));
826         }
827     }
828     swapchain->desc = *desc;
829     wined3d_swapchain_apply_sample_count_override(swapchain, swapchain->desc.backbuffer_format,
830             &swapchain->desc.multisample_type, &swapchain->desc.multisample_quality);
831     swapchain_update_render_to_fbo(swapchain);
832 
833     TRACE("Creating front buffer.\n");
834 
835     texture_desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
836     texture_desc.format = swapchain->desc.backbuffer_format;
837     texture_desc.multisample_type = swapchain->desc.multisample_type;
838     texture_desc.multisample_quality = swapchain->desc.multisample_quality;
839     texture_desc.usage = 0;
840     texture_desc.pool = WINED3D_POOL_DEFAULT;
841     texture_desc.width = swapchain->desc.backbuffer_width;
842     texture_desc.height = swapchain->desc.backbuffer_height;
843     texture_desc.depth = 1;
844     texture_desc.size = 0;
845 
846     if (swapchain->desc.flags & WINED3D_SWAPCHAIN_GDI_COMPATIBLE)
847         texture_flags |= WINED3D_TEXTURE_CREATE_GET_DC;
848 
849     if (FAILED(hr = device->device_parent->ops->create_swapchain_texture(device->device_parent,
850             parent, &texture_desc, texture_flags, &swapchain->front_buffer)))
851     {
852         WARN("Failed to create front buffer, hr %#x.\n", hr);
853         goto err;
854     }
855 
856     wined3d_texture_set_swapchain(swapchain->front_buffer, swapchain);
857     if (!(device->wined3d->flags & WINED3D_NO3D))
858     {
859         wined3d_texture_validate_location(swapchain->front_buffer, 0, WINED3D_LOCATION_DRAWABLE);
860         wined3d_texture_invalidate_location(swapchain->front_buffer, 0, ~WINED3D_LOCATION_DRAWABLE);
861     }
862 
863     /* MSDN says we're only allowed a single fullscreen swapchain per device,
864      * so we should really check to see if there is a fullscreen swapchain
865      * already. Does a single head count as full screen? */
866     if (!desc->windowed)
867     {
868         if (desc->flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH)
869         {
870             /* Change the display settings */
871             swapchain->d3d_mode.width = desc->backbuffer_width;
872             swapchain->d3d_mode.height = desc->backbuffer_height;
873             swapchain->d3d_mode.format_id = desc->backbuffer_format;
874             swapchain->d3d_mode.refresh_rate = desc->refresh_rate;
875             swapchain->d3d_mode.scanline_ordering = WINED3D_SCANLINE_ORDERING_UNKNOWN;
876 
877             if (FAILED(hr = wined3d_set_adapter_display_mode(device->wined3d,
878                     adapter->ordinal, &swapchain->d3d_mode)))
879             {
880                 WARN("Failed to set display mode, hr %#x.\n", hr);
881                 goto err;
882             }
883             displaymode_set = TRUE;
884         }
885         else
886         {
887             swapchain->d3d_mode = swapchain->original_mode;
888         }
889     }
890 
891     if (!(device->wined3d->flags & WINED3D_NO3D))
892     {
893         swapchain->context = HeapAlloc(GetProcessHeap(), 0, sizeof(*swapchain->context));
894         if (!swapchain->context)
895         {
896             ERR("Failed to create the context array.\n");
897             hr = E_OUTOFMEMORY;
898             goto err;
899         }
900 
901         wined3d_cs_init_object(device->cs, wined3d_swapchain_cs_init, swapchain);
902         device->cs->ops->finish(device->cs, WINED3D_CS_QUEUE_DEFAULT);
903 
904         if (!swapchain->context[0])
905         {
906             hr = WINED3DERR_NOTAVAILABLE;
907             goto err;
908         }
909     }
910 
911     if (swapchain->desc.backbuffer_count > 0)
912     {
913         if (!(swapchain->back_buffers = wined3d_calloc(swapchain->desc.backbuffer_count,
914                 sizeof(*swapchain->back_buffers))))
915         {
916             ERR("Failed to allocate backbuffer array memory.\n");
917             hr = E_OUTOFMEMORY;
918             goto err;
919         }
920 
921         texture_desc.usage |= WINED3DUSAGE_RENDERTARGET;
922         for (i = 0; i < swapchain->desc.backbuffer_count; ++i)
923         {
924             TRACE("Creating back buffer %u.\n", i);
925             if (FAILED(hr = device->device_parent->ops->create_swapchain_texture(device->device_parent,
926                     parent, &texture_desc, texture_flags, &swapchain->back_buffers[i])))
927             {
928                 WARN("Failed to create back buffer %u, hr %#x.\n", i, hr);
929                 swapchain->desc.backbuffer_count = i;
930                 goto err;
931             }
932             wined3d_texture_set_swapchain(swapchain->back_buffers[i], swapchain);
933         }
934     }
935 
936     /* Swapchains share the depth/stencil buffer, so only create a single depthstencil surface. */
937     if (desc->enable_auto_depth_stencil && !(device->wined3d->flags & WINED3D_NO3D))
938     {
939         TRACE("Creating depth/stencil buffer.\n");
940         if (!device->auto_depth_stencil_view)
941         {
942             struct wined3d_view_desc desc;
943             struct wined3d_texture *ds;
944 
945             texture_desc.format = swapchain->desc.auto_depth_stencil_format;
946             texture_desc.usage = WINED3DUSAGE_DEPTHSTENCIL;
947 
948             if (FAILED(hr = device->device_parent->ops->create_swapchain_texture(device->device_parent,
949                     device->device_parent, &texture_desc, texture_flags, &ds)))
950             {
951                 WARN("Failed to create the auto depth/stencil surface, hr %#x.\n", hr);
952                 goto err;
953             }
954 
955             desc.format_id = ds->resource.format->id;
956             desc.flags = 0;
957             desc.u.texture.level_idx = 0;
958             desc.u.texture.level_count = 1;
959             desc.u.texture.layer_idx = 0;
960             desc.u.texture.layer_count = 1;
961             hr = wined3d_rendertarget_view_create(&desc, &ds->resource, NULL, &wined3d_null_parent_ops,
962                     &device->auto_depth_stencil_view);
963             wined3d_texture_decref(ds);
964             if (FAILED(hr))
965             {
966                 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
967                 goto err;
968             }
969         }
970     }
971 
972     wined3d_swapchain_get_gamma_ramp(swapchain, &swapchain->orig_gamma);
973 
974     return WINED3D_OK;
975 
976 err:
977     if (displaymode_set)
978     {
979         if (FAILED(wined3d_set_adapter_display_mode(device->wined3d,
980                 adapter->ordinal, &swapchain->original_mode)))
981             ERR("Failed to restore display mode.\n");
982         ClipCursor(NULL);
983     }
984 
985     if (swapchain->back_buffers)
986     {
987         for (i = 0; i < swapchain->desc.backbuffer_count; ++i)
988         {
989             if (swapchain->back_buffers[i])
990             {
991                 wined3d_texture_set_swapchain(swapchain->back_buffers[i], NULL);
992                 wined3d_texture_decref(swapchain->back_buffers[i]);
993             }
994         }
995         HeapFree(GetProcessHeap(), 0, swapchain->back_buffers);
996     }
997 
998     wined3d_cs_destroy_object(swapchain->device->cs, wined3d_swapchain_destroy_object, swapchain);
999     swapchain->device->cs->ops->finish(device->cs, WINED3D_CS_QUEUE_DEFAULT);
1000 
1001     if (swapchain->front_buffer)
1002     {
1003         wined3d_texture_set_swapchain(swapchain->front_buffer, NULL);
1004         wined3d_texture_decref(swapchain->front_buffer);
1005     }
1006 
1007     return hr;
1008 }
1009 
1010 HRESULT CDECL wined3d_swapchain_create(struct wined3d_device *device, struct wined3d_swapchain_desc *desc,
1011         void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_swapchain **swapchain)
1012 {
1013     struct wined3d_swapchain *object;
1014     HRESULT hr;
1015 
1016     TRACE("device %p, desc %p, parent %p, parent_ops %p, swapchain %p.\n",
1017             device, desc, parent, parent_ops, swapchain);
1018 
1019     object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
1020     if (!object)
1021         return E_OUTOFMEMORY;
1022 
1023     hr = swapchain_init(object, device, desc, parent, parent_ops);
1024     if (FAILED(hr))
1025     {
1026         WARN("Failed to initialize swapchain, hr %#x.\n", hr);
1027         HeapFree(GetProcessHeap(), 0, object);
1028         return hr;
1029     }
1030 
1031     TRACE("Created swapchain %p.\n", object);
1032     *swapchain = object;
1033 
1034     return WINED3D_OK;
1035 }
1036 
1037 static struct wined3d_context *swapchain_create_context(struct wined3d_swapchain *swapchain)
1038 {
1039     struct wined3d_context **ctx_array;
1040     struct wined3d_context *ctx;
1041 
1042     TRACE("Creating a new context for swapchain %p, thread %u.\n", swapchain, GetCurrentThreadId());
1043 
1044     if (!(ctx = context_create(swapchain, swapchain->front_buffer, swapchain->ds_format)))
1045     {
1046         ERR("Failed to create a new context for the swapchain\n");
1047         return NULL;
1048     }
1049     context_release(ctx);
1050 
1051     if (!(ctx_array = wined3d_calloc(swapchain->num_contexts + 1, sizeof(*ctx_array))))
1052     {
1053         ERR("Out of memory when trying to allocate a new context array\n");
1054         context_destroy(swapchain->device, ctx);
1055         return NULL;
1056     }
1057     memcpy(ctx_array, swapchain->context, sizeof(*ctx_array) * swapchain->num_contexts);
1058     HeapFree(GetProcessHeap(), 0, swapchain->context);
1059     ctx_array[swapchain->num_contexts] = ctx;
1060     swapchain->context = ctx_array;
1061     swapchain->num_contexts++;
1062 
1063     TRACE("Returning context %p\n", ctx);
1064     return ctx;
1065 }
1066 
1067 void swapchain_destroy_contexts(struct wined3d_swapchain *swapchain)
1068 {
1069     unsigned int i;
1070 
1071     for (i = 0; i < swapchain->num_contexts; ++i)
1072     {
1073         context_destroy(swapchain->device, swapchain->context[i]);
1074     }
1075     HeapFree(GetProcessHeap(), 0, swapchain->context);
1076     swapchain->num_contexts = 0;
1077     swapchain->context = NULL;
1078 }
1079 
1080 struct wined3d_context *swapchain_get_context(struct wined3d_swapchain *swapchain)
1081 {
1082     DWORD tid = GetCurrentThreadId();
1083     unsigned int i;
1084 
1085     for (i = 0; i < swapchain->num_contexts; ++i)
1086     {
1087         if (swapchain->context[i]->tid == tid)
1088             return swapchain->context[i];
1089     }
1090 
1091     /* Create a new context for the thread */
1092     return swapchain_create_context(swapchain);
1093 }
1094 
1095 HDC swapchain_get_backup_dc(struct wined3d_swapchain *swapchain)
1096 {
1097     if (!swapchain->backup_dc)
1098     {
1099         TRACE("Creating the backup window for swapchain %p.\n", swapchain);
1100 
1101         if (!(swapchain->backup_wnd = CreateWindowA(WINED3D_OPENGL_WINDOW_CLASS_NAME, "WineD3D fake window",
1102                 WS_OVERLAPPEDWINDOW, 10, 10, 10, 10, NULL, NULL, NULL, NULL)))
1103         {
1104             ERR("Failed to create a window.\n");
1105             return NULL;
1106         }
1107 
1108         if (!(swapchain->backup_dc = GetDC(swapchain->backup_wnd)))
1109         {
1110             ERR("Failed to get a DC.\n");
1111             DestroyWindow(swapchain->backup_wnd);
1112             swapchain->backup_wnd = NULL;
1113             return NULL;
1114         }
1115     }
1116 
1117     return swapchain->backup_dc;
1118 }
1119 
1120 void swapchain_update_draw_bindings(struct wined3d_swapchain *swapchain)
1121 {
1122     UINT i;
1123 
1124     wined3d_resource_update_draw_binding(&swapchain->front_buffer->resource);
1125 
1126     for (i = 0; i < swapchain->desc.backbuffer_count; ++i)
1127     {
1128         wined3d_resource_update_draw_binding(&swapchain->back_buffers[i]->resource);
1129     }
1130 }
1131 
1132 void swapchain_update_swap_interval(struct wined3d_swapchain *swapchain)
1133 {
1134     wined3d_cs_init_object(swapchain->device->cs, wined3d_swapchain_update_swap_interval_cs, swapchain);
1135 }
1136 
1137 void wined3d_swapchain_activate(struct wined3d_swapchain *swapchain, BOOL activate)
1138 {
1139     struct wined3d_device *device = swapchain->device;
1140     BOOL filter_messages = device->filter_messages;
1141 
1142     /* This code is not protected by the wined3d mutex, so it may run while
1143      * wined3d_device_reset is active. Testing on Windows shows that changing
1144      * focus during resets and resetting during focus change events causes
1145      * the application to crash with an invalid memory access. */
1146 
1147     device->filter_messages = !(device->wined3d->flags & WINED3D_FOCUS_MESSAGES);
1148 
1149     if (activate)
1150     {
1151         if (!(device->create_parms.flags & WINED3DCREATE_NOWINDOWCHANGES))
1152         {
1153             /* The d3d versions do not agree on the exact messages here. D3d8 restores
1154              * the window but leaves the size untouched, d3d9 sets the size on an
1155              * invisible window, generates messages but doesn't change the window
1156              * properties. The implementation follows d3d9.
1157              *
1158              * Guild Wars 1 wants a WINDOWPOSCHANGED message on the device window to
1159              * resume drawing after a focus loss. */
1160             SetWindowPos(swapchain->device_window, NULL, 0, 0,
1161                     swapchain->desc.backbuffer_width, swapchain->desc.backbuffer_height,
1162                     SWP_NOACTIVATE | SWP_NOZORDER);
1163         }
1164 
1165         if (device->wined3d->flags & WINED3D_RESTORE_MODE_ON_ACTIVATE)
1166         {
1167             if (FAILED(wined3d_set_adapter_display_mode(device->wined3d,
1168                     device->adapter->ordinal, &swapchain->d3d_mode)))
1169                 ERR("Failed to set display mode.\n");
1170         }
1171     }
1172     else
1173     {
1174         if (FAILED(wined3d_set_adapter_display_mode(device->wined3d,
1175                 device->adapter->ordinal, NULL)))
1176             ERR("Failed to set display mode.\n");
1177 
1178         swapchain->reapply_mode = TRUE;
1179 
1180         if (!(device->create_parms.flags & WINED3DCREATE_NOWINDOWCHANGES)
1181                 && IsWindowVisible(swapchain->device_window))
1182             ShowWindow(swapchain->device_window, SW_MINIMIZE);
1183     }
1184 
1185     device->filter_messages = filter_messages;
1186 }
1187 
1188 HRESULT CDECL wined3d_swapchain_resize_buffers(struct wined3d_swapchain *swapchain, unsigned int buffer_count,
1189         unsigned int width, unsigned int height, enum wined3d_format_id format_id,
1190         enum wined3d_multisample_type multisample_type, unsigned int multisample_quality)
1191 {
1192     struct wined3d_device *device = swapchain->device;
1193     BOOL update_desc = FALSE;
1194 
1195     TRACE("swapchain %p, buffer_count %u, width %u, height %u, format %s, "
1196             "multisample_type %#x, multisample_quality %#x.\n",
1197             swapchain, buffer_count, width, height, debug_d3dformat(format_id),
1198             multisample_type, multisample_quality);
1199 
1200     wined3d_swapchain_apply_sample_count_override(swapchain, format_id, &multisample_type, &multisample_quality);
1201 
1202     if (buffer_count && buffer_count != swapchain->desc.backbuffer_count)
1203         FIXME("Cannot change the back buffer count yet.\n");
1204 
1205     device->cs->ops->finish(device->cs, WINED3D_CS_QUEUE_DEFAULT);
1206 
1207     if (!width || !height)
1208     {
1209         /* The application is requesting that either the swapchain width or
1210          * height be set to the corresponding dimension in the window's
1211          * client rect. */
1212 
1213         RECT client_rect;
1214 
1215         if (!swapchain->desc.windowed)
1216             return WINED3DERR_INVALIDCALL;
1217 
1218         if (!GetClientRect(swapchain->device_window, &client_rect))
1219         {
1220             ERR("Failed to get client rect, last error %#x.\n", GetLastError());
1221             return WINED3DERR_INVALIDCALL;
1222         }
1223 
1224         if (!width)
1225             width = client_rect.right;
1226 
1227         if (!height)
1228             height = client_rect.bottom;
1229     }
1230 
1231     if (width != swapchain->desc.backbuffer_width
1232             || height != swapchain->desc.backbuffer_height)
1233     {
1234         swapchain->desc.backbuffer_width = width;
1235         swapchain->desc.backbuffer_height = height;
1236         update_desc = TRUE;
1237     }
1238 
1239     if (format_id == WINED3DFMT_UNKNOWN)
1240     {
1241         if (!swapchain->desc.windowed)
1242             return WINED3DERR_INVALIDCALL;
1243         format_id = swapchain->original_mode.format_id;
1244     }
1245 
1246     if (format_id != swapchain->desc.backbuffer_format)
1247     {
1248         swapchain->desc.backbuffer_format = format_id;
1249         update_desc = TRUE;
1250     }
1251 
1252     if (multisample_type != swapchain->desc.multisample_type
1253             || multisample_quality != swapchain->desc.multisample_quality)
1254     {
1255         swapchain->desc.multisample_type = multisample_type;
1256         swapchain->desc.multisample_quality = multisample_quality;
1257         update_desc = TRUE;
1258     }
1259 
1260     if (update_desc)
1261     {
1262         HRESULT hr;
1263         UINT i;
1264 
1265         if (FAILED(hr = wined3d_texture_update_desc(swapchain->front_buffer, swapchain->desc.backbuffer_width,
1266                 swapchain->desc.backbuffer_height, swapchain->desc.backbuffer_format,
1267                 swapchain->desc.multisample_type, swapchain->desc.multisample_quality, NULL, 0)))
1268             return hr;
1269 
1270         for (i = 0; i < swapchain->desc.backbuffer_count; ++i)
1271         {
1272             if (FAILED(hr = wined3d_texture_update_desc(swapchain->back_buffers[i], swapchain->desc.backbuffer_width,
1273                     swapchain->desc.backbuffer_height, swapchain->desc.backbuffer_format,
1274                     swapchain->desc.multisample_type, swapchain->desc.multisample_quality, NULL, 0)))
1275                 return hr;
1276         }
1277     }
1278 
1279     swapchain_update_render_to_fbo(swapchain);
1280     swapchain_update_draw_bindings(swapchain);
1281 
1282     return WINED3D_OK;
1283 }
1284 
1285 static HRESULT wined3d_swapchain_set_display_mode(struct wined3d_swapchain *swapchain,
1286         struct wined3d_display_mode *mode)
1287 {
1288     struct wined3d_device *device = swapchain->device;
1289     HRESULT hr;
1290 
1291     if (swapchain->desc.flags & WINED3D_SWAPCHAIN_USE_CLOSEST_MATCHING_MODE)
1292     {
1293         if (FAILED(hr = wined3d_find_closest_matching_adapter_mode(device->wined3d,
1294                 device->adapter->ordinal, mode)))
1295         {
1296             WARN("Failed to find closest matching mode, hr %#x.\n", hr);
1297         }
1298     }
1299 
1300     if (FAILED(hr = wined3d_set_adapter_display_mode(device->wined3d,
1301             device->adapter->ordinal, mode)))
1302     {
1303         WARN("Failed to set display mode, hr %#x.\n", hr);
1304         return WINED3DERR_INVALIDCALL;
1305     }
1306 
1307     return WINED3D_OK;
1308 }
1309 
1310 HRESULT CDECL wined3d_swapchain_resize_target(struct wined3d_swapchain *swapchain,
1311         const struct wined3d_display_mode *mode)
1312 {
1313     struct wined3d_device *device = swapchain->device;
1314     struct wined3d_display_mode actual_mode;
1315     RECT original_window_rect, window_rect;
1316     HRESULT hr;
1317 
1318     TRACE("swapchain %p, mode %p.\n", swapchain, mode);
1319 
1320     if (swapchain->desc.windowed)
1321     {
1322         SetRect(&window_rect, 0, 0, mode->width, mode->height);
1323         AdjustWindowRectEx(&window_rect,
1324                 GetWindowLongW(swapchain->device_window, GWL_STYLE), FALSE,
1325                 GetWindowLongW(swapchain->device_window, GWL_EXSTYLE));
1326         SetRect(&window_rect, 0, 0,
1327                 window_rect.right - window_rect.left, window_rect.bottom - window_rect.top);
1328         GetWindowRect(swapchain->device_window, &original_window_rect);
1329         OffsetRect(&window_rect, original_window_rect.left, original_window_rect.top);
1330     }
1331     else if (swapchain->desc.flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH)
1332     {
1333         actual_mode = *mode;
1334         if (FAILED(hr = wined3d_swapchain_set_display_mode(swapchain, &actual_mode)))
1335             return hr;
1336         SetRect(&window_rect, 0, 0, actual_mode.width, actual_mode.height);
1337     }
1338     else
1339     {
1340         if (FAILED(hr = wined3d_get_adapter_display_mode(device->wined3d, device->adapter->ordinal,
1341                 &actual_mode, NULL)))
1342         {
1343             ERR("Failed to get display mode, hr %#x.\n", hr);
1344             return WINED3DERR_INVALIDCALL;
1345         }
1346 
1347         SetRect(&window_rect, 0, 0, actual_mode.width, actual_mode.height);
1348     }
1349 
1350     MoveWindow(swapchain->device_window, window_rect.left, window_rect.top,
1351             window_rect.right - window_rect.left,
1352             window_rect.bottom - window_rect.top, TRUE);
1353 
1354     return WINED3D_OK;
1355 }
1356 
1357 HRESULT CDECL wined3d_swapchain_set_fullscreen(struct wined3d_swapchain *swapchain,
1358         const struct wined3d_swapchain_desc *swapchain_desc, const struct wined3d_display_mode *mode)
1359 {
1360     struct wined3d_device *device = swapchain->device;
1361     struct wined3d_display_mode actual_mode;
1362     HRESULT hr;
1363 
1364     TRACE("swapchain %p, desc %p, mode %p.\n", swapchain, swapchain_desc, mode);
1365 
1366     if (swapchain->desc.flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH)
1367     {
1368         if (mode)
1369         {
1370             actual_mode = *mode;
1371         }
1372         else
1373         {
1374             if (!swapchain_desc->windowed)
1375             {
1376                 actual_mode.width = swapchain_desc->backbuffer_width;
1377                 actual_mode.height = swapchain_desc->backbuffer_height;
1378                 actual_mode.refresh_rate = swapchain_desc->refresh_rate;
1379                 actual_mode.format_id = swapchain_desc->backbuffer_format;
1380                 actual_mode.scanline_ordering = WINED3D_SCANLINE_ORDERING_UNKNOWN;
1381             }
1382             else
1383             {
1384                 actual_mode = swapchain->original_mode;
1385             }
1386         }
1387 
1388         if (FAILED(hr = wined3d_swapchain_set_display_mode(swapchain, &actual_mode)))
1389             return hr;
1390     }
1391     else
1392     {
1393         if (mode)
1394             WARN("WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH is not set, ignoring mode.\n");
1395 
1396         if (FAILED(hr = wined3d_get_adapter_display_mode(device->wined3d, device->adapter->ordinal,
1397                 &actual_mode, NULL)))
1398         {
1399             ERR("Failed to get display mode, hr %#x.\n", hr);
1400             return WINED3DERR_INVALIDCALL;
1401         }
1402     }
1403 
1404     if (!swapchain_desc->windowed)
1405     {
1406         unsigned int width = actual_mode.width;
1407         unsigned int height = actual_mode.height;
1408 
1409         if (swapchain->desc.windowed)
1410         {
1411             /* Switch from windowed to fullscreen */
1412             HWND focus_window = device->create_parms.focus_window;
1413             if (!focus_window)
1414                 focus_window = swapchain->device_window;
1415             if (FAILED(hr = wined3d_device_acquire_focus_window(device, focus_window)))
1416             {
1417                 ERR("Failed to acquire focus window, hr %#x.\n", hr);
1418                 return hr;
1419             }
1420 
1421             wined3d_device_setup_fullscreen_window(device, swapchain->device_window, width, height);
1422         }
1423         else
1424         {
1425             /* Fullscreen -> fullscreen mode change */
1426             BOOL filter_messages = device->filter_messages;
1427             device->filter_messages = TRUE;
1428 
1429             MoveWindow(swapchain->device_window, 0, 0, width, height, TRUE);
1430             ShowWindow(swapchain->device_window, SW_SHOW);
1431 
1432             device->filter_messages = filter_messages;
1433         }
1434         swapchain->d3d_mode = actual_mode;
1435     }
1436     else if (!swapchain->desc.windowed)
1437     {
1438         /* Fullscreen -> windowed switch */
1439         RECT *window_rect = NULL;
1440         if (swapchain->desc.flags & WINED3D_SWAPCHAIN_RESTORE_WINDOW_RECT)
1441             window_rect = &swapchain->original_window_rect;
1442         wined3d_device_restore_fullscreen_window(device, swapchain->device_window, window_rect);
1443         wined3d_device_release_focus_window(device);
1444     }
1445 
1446     swapchain->desc.windowed = swapchain_desc->windowed;
1447 
1448     return WINED3D_OK;
1449 }
1450