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