xref: /reactos/dll/directx/wine/wined3d/swapchain.c (revision 07608028)
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         unsigned int swap_interval, DWORD flags)
155 {
156     RECT s, d;
157 
158     TRACE("swapchain %p, src_rect %s, dst_rect %s, dst_window_override %p, flags %#x.\n",
159             swapchain, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect),
160             dst_window_override, flags);
161 
162     if (flags)
163         FIXME("Ignoring flags %#x.\n", flags);
164 
165     if (!swapchain->back_buffers)
166     {
167         WARN("Swapchain doesn't have a backbuffer, returning WINED3DERR_INVALIDCALL.\n");
168         return WINED3DERR_INVALIDCALL;
169     }
170 
171     if (!src_rect)
172     {
173         SetRect(&s, 0, 0, swapchain->desc.backbuffer_width,
174                 swapchain->desc.backbuffer_height);
175         src_rect = &s;
176     }
177 
178     if (!dst_rect)
179     {
180         GetClientRect(swapchain->win_handle, &d);
181         dst_rect = &d;
182     }
183 
184     wined3d_cs_emit_present(swapchain->device->cs, swapchain, src_rect,
185             dst_rect, dst_window_override, swap_interval, flags);
186 
187     return WINED3D_OK;
188 }
189 
190 HRESULT CDECL wined3d_swapchain_get_front_buffer_data(const struct wined3d_swapchain *swapchain,
191         struct wined3d_texture *dst_texture, unsigned int sub_resource_idx)
192 {
193     RECT src_rect, dst_rect;
194 
195     TRACE("swapchain %p, dst_texture %p, sub_resource_idx %u.\n", swapchain, dst_texture, sub_resource_idx);
196 
197     SetRect(&src_rect, 0, 0, swapchain->front_buffer->resource.width, swapchain->front_buffer->resource.height);
198     dst_rect = src_rect;
199 
200     if (swapchain->desc.windowed)
201     {
202         MapWindowPoints(swapchain->win_handle, NULL, (POINT *)&dst_rect, 2);
203         FIXME("Using destination rect %s in windowed mode, this is likely wrong.\n",
204                 wine_dbgstr_rect(&dst_rect));
205     }
206 
207     return wined3d_texture_blt(dst_texture, sub_resource_idx, &dst_rect,
208             swapchain->front_buffer, 0, &src_rect, 0, NULL, WINED3D_TEXF_POINT);
209 }
210 
211 struct wined3d_texture * CDECL wined3d_swapchain_get_back_buffer(const struct wined3d_swapchain *swapchain,
212         UINT back_buffer_idx)
213 {
214     TRACE("swapchain %p, back_buffer_idx %u.\n",
215             swapchain, back_buffer_idx);
216 
217     /* Return invalid if there is no backbuffer array, otherwise it will
218      * crash when ddraw is used (there swapchain->back_buffers is always
219      * NULL). We need this because this function is called from
220      * stateblock_init_default_state() to get the default scissorrect
221      * dimensions. */
222     if (!swapchain->back_buffers || back_buffer_idx >= swapchain->desc.backbuffer_count)
223     {
224         WARN("Invalid back buffer index.\n");
225         /* Native d3d9 doesn't set NULL here, just as wine's d3d9. But set it
226          * here in wined3d to avoid problems in other libs. */
227         return NULL;
228     }
229 
230     TRACE("Returning back buffer %p.\n", swapchain->back_buffers[back_buffer_idx]);
231 
232     return swapchain->back_buffers[back_buffer_idx];
233 }
234 
235 HRESULT CDECL wined3d_swapchain_get_raster_status(const struct wined3d_swapchain *swapchain,
236         struct wined3d_raster_status *raster_status)
237 {
238     TRACE("swapchain %p, raster_status %p.\n", swapchain, raster_status);
239 
240     return wined3d_get_adapter_raster_status(swapchain->device->wined3d,
241             swapchain->device->adapter->ordinal, raster_status);
242 }
243 
244 HRESULT CDECL wined3d_swapchain_get_display_mode(const struct wined3d_swapchain *swapchain,
245         struct wined3d_display_mode *mode, enum wined3d_display_rotation *rotation)
246 {
247     HRESULT hr;
248 
249     TRACE("swapchain %p, mode %p, rotation %p.\n", swapchain, mode, rotation);
250 
251     hr = wined3d_get_adapter_display_mode(swapchain->device->wined3d,
252             swapchain->device->adapter->ordinal, mode, rotation);
253 
254     TRACE("Returning w %u, h %u, refresh rate %u, format %s.\n",
255             mode->width, mode->height, mode->refresh_rate, debug_d3dformat(mode->format_id));
256 
257     return hr;
258 }
259 
260 struct wined3d_device * CDECL wined3d_swapchain_get_device(const struct wined3d_swapchain *swapchain)
261 {
262     TRACE("swapchain %p.\n", swapchain);
263 
264     return swapchain->device;
265 }
266 
267 void CDECL wined3d_swapchain_get_desc(const struct wined3d_swapchain *swapchain,
268         struct wined3d_swapchain_desc *desc)
269 {
270     TRACE("swapchain %p, desc %p.\n", swapchain, desc);
271 
272     *desc = swapchain->desc;
273 }
274 
275 HRESULT CDECL wined3d_swapchain_set_gamma_ramp(const struct wined3d_swapchain *swapchain,
276         DWORD flags, const struct wined3d_gamma_ramp *ramp)
277 {
278     HDC dc;
279 
280     TRACE("swapchain %p, flags %#x, ramp %p.\n", swapchain, flags, ramp);
281 
282     if (flags)
283         FIXME("Ignoring flags %#x.\n", flags);
284 
285     dc = GetDCEx(swapchain->device_window, 0, DCX_USESTYLE | DCX_CACHE);
286     SetDeviceGammaRamp(dc, (void *)ramp);
287     ReleaseDC(swapchain->device_window, dc);
288 
289     return WINED3D_OK;
290 }
291 
292 void CDECL wined3d_swapchain_set_palette(struct wined3d_swapchain *swapchain, struct wined3d_palette *palette)
293 {
294     TRACE("swapchain %p, palette %p.\n", swapchain, palette);
295     swapchain->palette = palette;
296 }
297 
298 HRESULT CDECL wined3d_swapchain_get_gamma_ramp(const struct wined3d_swapchain *swapchain,
299         struct wined3d_gamma_ramp *ramp)
300 {
301     HDC dc;
302 
303     TRACE("swapchain %p, ramp %p.\n", swapchain, ramp);
304 
305     dc = GetDCEx(swapchain->device_window, 0, DCX_USESTYLE | DCX_CACHE);
306     GetDeviceGammaRamp(dc, ramp);
307     ReleaseDC(swapchain->device_window, dc);
308 
309     return WINED3D_OK;
310 }
311 
312 /* A GL context is provided by the caller */
313 static void swapchain_blit(const struct wined3d_swapchain *swapchain,
314         struct wined3d_context *context, const RECT *src_rect, const RECT *dst_rect)
315 {
316     struct wined3d_texture *texture = swapchain->back_buffers[0];
317     struct wined3d_device *device = swapchain->device;
318     enum wined3d_texture_filter_type filter;
319     DWORD location;
320 
321     TRACE("swapchain %p, context %p, src_rect %s, dst_rect %s.\n",
322             swapchain, context, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect));
323 
324     if ((src_rect->right - src_rect->left == dst_rect->right - dst_rect->left
325             && src_rect->bottom - src_rect->top == dst_rect->bottom - dst_rect->top)
326             || is_complex_fixup(texture->resource.format->color_fixup))
327         filter = WINED3D_TEXF_NONE;
328     else
329         filter = WINED3D_TEXF_LINEAR;
330 
331     location = WINED3D_LOCATION_TEXTURE_RGB;
332     if (texture->resource.multisample_type)
333         location = WINED3D_LOCATION_RB_RESOLVED;
334 
335     wined3d_texture_validate_location(texture, 0, WINED3D_LOCATION_DRAWABLE);
336     device->blitter->ops->blitter_blit(device->blitter, WINED3D_BLIT_OP_COLOR_BLIT, context, texture, 0,
337             location, src_rect, texture, 0, WINED3D_LOCATION_DRAWABLE, dst_rect, NULL, filter);
338     wined3d_texture_invalidate_location(texture, 0, WINED3D_LOCATION_DRAWABLE);
339 }
340 
341 /* Context activation is done by the caller. */
342 static void wined3d_swapchain_rotate(struct wined3d_swapchain *swapchain, struct wined3d_context *context)
343 {
344     struct wined3d_texture_sub_resource *sub_resource;
345     struct wined3d_texture *texture, *texture_prev;
346     struct gl_texture tex0;
347     GLuint rb0;
348     DWORD locations0;
349     unsigned int i;
350     static const DWORD supported_locations = WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_RB_MULTISAMPLE;
351 
352     if (swapchain->desc.backbuffer_count < 2 || !swapchain->render_to_fbo)
353         return;
354 
355     texture_prev = swapchain->back_buffers[0];
356 
357     /* Back buffer 0 is already in the draw binding. */
358     tex0 = texture_prev->texture_rgb;
359     rb0 = texture_prev->rb_multisample;
360     locations0 = texture_prev->sub_resources[0].locations;
361 
362     for (i = 1; i < swapchain->desc.backbuffer_count; ++i)
363     {
364         texture = swapchain->back_buffers[i];
365         sub_resource = &texture->sub_resources[0];
366 
367         if (!(sub_resource->locations & supported_locations))
368             wined3d_texture_load_location(texture, 0, context, texture->resource.draw_binding);
369 
370         texture_prev->texture_rgb = texture->texture_rgb;
371         texture_prev->rb_multisample = texture->rb_multisample;
372 
373         wined3d_texture_validate_location(texture_prev, 0, sub_resource->locations & supported_locations);
374         wined3d_texture_invalidate_location(texture_prev, 0, ~(sub_resource->locations & supported_locations));
375 
376         texture_prev = texture;
377     }
378 
379     texture_prev->texture_rgb = tex0;
380     texture_prev->rb_multisample = rb0;
381 
382     wined3d_texture_validate_location(texture_prev, 0, locations0 & supported_locations);
383     wined3d_texture_invalidate_location(texture_prev, 0, ~(locations0 & supported_locations));
384 
385     device_invalidate_state(swapchain->device, STATE_FRAMEBUFFER);
386 }
387 
388 static void swapchain_gl_present(struct wined3d_swapchain *swapchain,
389         const RECT *src_rect, const RECT *dst_rect, DWORD flags)
390 {
391     struct wined3d_texture *back_buffer = swapchain->back_buffers[0];
392     const struct wined3d_fb_state *fb = &swapchain->device->cs->fb;
393     struct wined3d_rendertarget_view *dsv = fb->depth_stencil;
394     const struct wined3d_gl_info *gl_info;
395     struct wined3d_texture *logo_texture;
396     struct wined3d_context *context;
397     BOOL render_to_fbo;
398 
399     context = context_acquire(swapchain->device, back_buffer, 0);
400     if (!context->valid)
401     {
402         context_release(context);
403         WARN("Invalid context, skipping present.\n");
404         return;
405     }
406 
407     gl_info = context->gl_info;
408 
409     if ((logo_texture = swapchain->device->logo_texture))
410     {
411         RECT rect = {0, 0, logo_texture->resource.width, logo_texture->resource.height};
412 
413         /* Blit the logo into the upper left corner of the drawable. */
414         wined3d_texture_blt(back_buffer, 0, &rect, logo_texture, 0, &rect,
415                 WINED3D_BLT_SRC_CKEY, NULL, WINED3D_TEXF_POINT);
416     }
417 
418     if (swapchain->device->bCursorVisible && swapchain->device->cursor_texture
419             && !swapchain->device->hardwareCursor)
420     {
421         RECT dst_rect =
422         {
423             swapchain->device->xScreenSpace - swapchain->device->xHotSpot,
424             swapchain->device->yScreenSpace - swapchain->device->yHotSpot,
425             swapchain->device->xScreenSpace + swapchain->device->cursorWidth - swapchain->device->xHotSpot,
426             swapchain->device->yScreenSpace + swapchain->device->cursorHeight - swapchain->device->yHotSpot,
427         };
428         RECT src_rect =
429         {
430             0, 0,
431             swapchain->device->cursor_texture->resource.width,
432             swapchain->device->cursor_texture->resource.height
433         };
434         const RECT clip_rect = {0, 0, back_buffer->resource.width, back_buffer->resource.height};
435 
436         TRACE("Rendering the software cursor.\n");
437 
438         if (swapchain->desc.windowed)
439             MapWindowPoints(NULL, swapchain->win_handle, (POINT *)&dst_rect, 2);
440         if (wined3d_clip_blit(&clip_rect, &dst_rect, &src_rect))
441             wined3d_texture_blt(back_buffer, 0, &dst_rect,
442                     swapchain->device->cursor_texture, 0, &src_rect,
443                     WINED3D_BLT_ALPHA_TEST, NULL, WINED3D_TEXF_POINT);
444     }
445 
446     TRACE("Presenting HDC %p.\n", context->hdc);
447 
448     if (!(render_to_fbo = swapchain->render_to_fbo)
449             && (src_rect->left || src_rect->top
450             || src_rect->right != swapchain->desc.backbuffer_width
451             || src_rect->bottom != swapchain->desc.backbuffer_height
452             || dst_rect->left || dst_rect->top
453             || dst_rect->right != swapchain->desc.backbuffer_width
454             || dst_rect->bottom != swapchain->desc.backbuffer_height))
455         render_to_fbo = TRUE;
456 
457     /* Rendering to a window of different size, presenting partial rectangles,
458      * or rendering to a different window needs help from FBO_blit or a textured
459      * draw. Render the swapchain to a FBO in the future.
460      *
461      * Note that FBO_blit from the backbuffer to the frontbuffer cannot solve
462      * all these issues - this fails if the window is smaller than the backbuffer.
463      */
464     if (!swapchain->render_to_fbo && render_to_fbo && wined3d_settings.offscreen_rendering_mode == ORM_FBO)
465     {
466         wined3d_texture_load_location(back_buffer, 0, context, WINED3D_LOCATION_TEXTURE_RGB);
467         wined3d_texture_invalidate_location(back_buffer, 0, WINED3D_LOCATION_DRAWABLE);
468         swapchain->render_to_fbo = TRUE;
469         swapchain_update_draw_bindings(swapchain);
470     }
471     else
472     {
473         wined3d_texture_load_location(back_buffer, 0, context, back_buffer->resource.draw_binding);
474     }
475 
476     if (swapchain->render_to_fbo)
477         swapchain_blit(swapchain, context, src_rect, dst_rect);
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             || swapchain->desc.swap_effect == WINED3D_SWAP_EFFECT_FLIP_DISCARD)
515         wined3d_texture_validate_location(swapchain->back_buffers[swapchain->desc.backbuffer_count - 1],
516                 0, WINED3D_LOCATION_DISCARDED);
517 
518     if (dsv && dsv->resource->type != WINED3D_RTYPE_BUFFER)
519     {
520         struct wined3d_texture *ds = texture_from_resource(dsv->resource);
521 
522         if ((swapchain->desc.flags & WINED3D_SWAPCHAIN_DISCARD_DEPTHSTENCIL
523                 || ds->flags & WINED3D_TEXTURE_DISCARD))
524             wined3d_texture_validate_location(ds, dsv->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_dc_info *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->dc_info[0];
558     if (swapchain->palette)
559         wined3d_palette_apply_to_dc(swapchain->palette, front->dc);
560 
561     if (swapchain->front_buffer->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_dc_info *front, *back;
593     HBITMAP bitmap;
594     void *data;
595     HDC dc;
596 
597     front = &swapchain->front_buffer->dc_info[0];
598     back = &swapchain->back_buffers[0]->dc_info[0];
599 
600     /* Flip the surface data. */
601     dc = front->dc;
602     bitmap = front->bitmap;
603     data = swapchain->front_buffer->resource.heap_memory;
604 
605     front->dc = back->dc;
606     front->bitmap = back->bitmap;
607     swapchain->front_buffer->resource.heap_memory = swapchain->back_buffers[0]->resource.heap_memory;
608 
609     back->dc = dc;
610     back->bitmap = bitmap;
611     swapchain->back_buffers[0]->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 void wined3d_swapchain_set_swap_interval(struct wined3d_swapchain *swapchain,
681         unsigned int swap_interval)
682 {
683     const struct wined3d_gl_info *gl_info;
684     struct wined3d_context *context;
685 
686     swap_interval = swap_interval <= 4 ? swap_interval : 1;
687     if (swapchain->swap_interval == swap_interval)
688         return;
689 
690     swapchain->swap_interval = swap_interval;
691 
692     context = context_acquire(swapchain->device, swapchain->front_buffer, 0);
693     gl_info = context->gl_info;
694 
695     if (gl_info->supported[WGL_EXT_SWAP_CONTROL])
696     {
697         if (!GL_EXTCALL(wglSwapIntervalEXT(swap_interval)))
698             ERR("wglSwapIntervalEXT failed to set swap interval %d for context %p, last error %#x.\n",
699                     swap_interval, context, GetLastError());
700     }
701 
702     context_release(context);
703 }
704 
705 static void wined3d_swapchain_cs_init(void *object)
706 {
707     struct wined3d_swapchain *swapchain = object;
708     const struct wined3d_gl_info *gl_info;
709     unsigned int i;
710 
711     static const enum wined3d_format_id formats[] =
712     {
713         WINED3DFMT_D24_UNORM_S8_UINT,
714         WINED3DFMT_D32_UNORM,
715         WINED3DFMT_R24_UNORM_X8_TYPELESS,
716         WINED3DFMT_D16_UNORM,
717         WINED3DFMT_S1_UINT_D15_UNORM,
718     };
719 
720     gl_info = &swapchain->device->adapter->gl_info;
721 
722     /* Without ORM_FBO, switching the depth/stencil format is hard. Always
723      * request a depth/stencil buffer in the likely case it's needed later. */
724     for (i = 0; i < ARRAY_SIZE(formats); ++i)
725     {
726         swapchain->ds_format = wined3d_get_format(gl_info, formats[i], WINED3DUSAGE_DEPTHSTENCIL);
727         if ((swapchain->context[0] = context_create(swapchain, swapchain->front_buffer, swapchain->ds_format)))
728             break;
729         TRACE("Depth stencil format %s is not supported, trying next format.\n", debug_d3dformat(formats[i]));
730     }
731 
732     if (!swapchain->context[0])
733     {
734         WARN("Failed to create context.\n");
735         return;
736     }
737     swapchain->num_contexts = 1;
738 
739     if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
740             && (!swapchain->desc.enable_auto_depth_stencil
741             || swapchain->desc.auto_depth_stencil_format != swapchain->ds_format->id))
742         FIXME("Add OpenGL context recreation support.\n");
743 
744     context_release(swapchain->context[0]);
745 }
746 
747 void swapchain_set_max_frame_latency(struct wined3d_swapchain *swapchain, const struct wined3d_device *device)
748 {
749     /* Subtract 1 for the implicit OpenGL latency. */
750     swapchain->max_frame_latency = device->max_frame_latency >= 2 ? device->max_frame_latency - 1 : 1;
751 }
752 
753 static HRESULT swapchain_init(struct wined3d_swapchain *swapchain, struct wined3d_device *device,
754         struct wined3d_swapchain_desc *desc, void *parent, const struct wined3d_parent_ops *parent_ops)
755 {
756     const struct wined3d_adapter *adapter = device->adapter;
757     struct wined3d_resource_desc texture_desc;
758     BOOL displaymode_set = FALSE;
759     DWORD texture_flags = 0;
760     RECT client_rect;
761     unsigned int i;
762     HWND window;
763     HRESULT hr;
764 
765     if (desc->backbuffer_count > 1)
766     {
767         FIXME("The application requested more than one back buffer, this is not properly supported.\n"
768                 "Please configure the application to use double buffering (1 back buffer) if possible.\n");
769     }
770 
771     if (desc->swap_effect != WINED3D_SWAP_EFFECT_DISCARD
772             && desc->swap_effect != WINED3D_SWAP_EFFECT_SEQUENTIAL
773             && desc->swap_effect != WINED3D_SWAP_EFFECT_COPY)
774         FIXME("Unimplemented swap effect %#x.\n", desc->swap_effect);
775 
776     if (device->wined3d->flags & WINED3D_NO3D)
777         swapchain->swapchain_ops = &swapchain_gdi_ops;
778     else
779         swapchain->swapchain_ops = &swapchain_gl_ops;
780 
781     window = desc->device_window ? desc->device_window : device->create_parms.focus_window;
782 
783     swapchain->device = device;
784     swapchain->parent = parent;
785     swapchain->parent_ops = parent_ops;
786     swapchain->ref = 1;
787     swapchain->win_handle = window;
788     swapchain->device_window = window;
789     swapchain->swap_interval = WINED3D_SWAP_INTERVAL_DEFAULT;
790     swapchain_set_max_frame_latency(swapchain, device);
791 
792     if (FAILED(hr = wined3d_get_adapter_display_mode(device->wined3d,
793             adapter->ordinal, &swapchain->original_mode, NULL)))
794     {
795         ERR("Failed to get current display mode, hr %#x.\n", hr);
796         goto err;
797     }
798     GetWindowRect(window, &swapchain->original_window_rect);
799 
800     GetClientRect(window, &client_rect);
801     if (desc->windowed)
802     {
803         TRACE("Client rect %s.\n", wine_dbgstr_rect(&client_rect));
804 
805         if (!desc->backbuffer_width)
806         {
807             desc->backbuffer_width = client_rect.right ? client_rect.right : 8;
808             TRACE("Updating width to %u.\n", desc->backbuffer_width);
809         }
810         if (!desc->backbuffer_height)
811         {
812             desc->backbuffer_height = client_rect.bottom ? client_rect.bottom : 8;
813             TRACE("Updating height to %u.\n", desc->backbuffer_height);
814         }
815 
816         if (desc->backbuffer_format == WINED3DFMT_UNKNOWN)
817         {
818             desc->backbuffer_format = swapchain->original_mode.format_id;
819             TRACE("Updating format to %s.\n", debug_d3dformat(swapchain->original_mode.format_id));
820         }
821     }
822     swapchain->desc = *desc;
823     wined3d_swapchain_apply_sample_count_override(swapchain, swapchain->desc.backbuffer_format,
824             &swapchain->desc.multisample_type, &swapchain->desc.multisample_quality);
825     swapchain_update_render_to_fbo(swapchain);
826 
827     TRACE("Creating front buffer.\n");
828 
829     texture_desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
830     texture_desc.format = swapchain->desc.backbuffer_format;
831     texture_desc.multisample_type = swapchain->desc.multisample_type;
832     texture_desc.multisample_quality = swapchain->desc.multisample_quality;
833     texture_desc.usage = 0;
834     texture_desc.access = WINED3D_RESOURCE_ACCESS_GPU;
835     texture_desc.width = swapchain->desc.backbuffer_width;
836     texture_desc.height = swapchain->desc.backbuffer_height;
837     texture_desc.depth = 1;
838     texture_desc.size = 0;
839 
840     if (swapchain->desc.flags & WINED3D_SWAPCHAIN_GDI_COMPATIBLE)
841         texture_flags |= WINED3D_TEXTURE_CREATE_GET_DC;
842 
843     if (FAILED(hr = device->device_parent->ops->create_swapchain_texture(device->device_parent,
844             parent, &texture_desc, texture_flags, &swapchain->front_buffer)))
845     {
846         WARN("Failed to create front buffer, hr %#x.\n", hr);
847         goto err;
848     }
849 
850     wined3d_texture_set_swapchain(swapchain->front_buffer, swapchain);
851     if (!(device->wined3d->flags & WINED3D_NO3D))
852     {
853         wined3d_texture_validate_location(swapchain->front_buffer, 0, WINED3D_LOCATION_DRAWABLE);
854         wined3d_texture_invalidate_location(swapchain->front_buffer, 0, ~WINED3D_LOCATION_DRAWABLE);
855     }
856 
857     /* MSDN says we're only allowed a single fullscreen swapchain per device,
858      * so we should really check to see if there is a fullscreen swapchain
859      * already. Does a single head count as full screen? */
860     if (!desc->windowed)
861     {
862         if (desc->flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH)
863         {
864             /* Change the display settings */
865             swapchain->d3d_mode.width = desc->backbuffer_width;
866             swapchain->d3d_mode.height = desc->backbuffer_height;
867             swapchain->d3d_mode.format_id = desc->backbuffer_format;
868             swapchain->d3d_mode.refresh_rate = desc->refresh_rate;
869             swapchain->d3d_mode.scanline_ordering = WINED3D_SCANLINE_ORDERING_UNKNOWN;
870 
871             if (FAILED(hr = wined3d_set_adapter_display_mode(device->wined3d,
872                     adapter->ordinal, &swapchain->d3d_mode)))
873             {
874                 WARN("Failed to set display mode, hr %#x.\n", hr);
875                 goto err;
876             }
877             displaymode_set = TRUE;
878         }
879         else
880         {
881             swapchain->d3d_mode = swapchain->original_mode;
882         }
883     }
884 
885     if (!(device->wined3d->flags & WINED3D_NO3D))
886     {
887         if (!(swapchain->context = heap_alloc(sizeof(*swapchain->context))))
888         {
889             ERR("Failed to create the context array.\n");
890             hr = E_OUTOFMEMORY;
891             goto err;
892         }
893 
894         wined3d_cs_init_object(device->cs, wined3d_swapchain_cs_init, swapchain);
895         device->cs->ops->finish(device->cs, WINED3D_CS_QUEUE_DEFAULT);
896 
897         if (!swapchain->context[0])
898         {
899             hr = WINED3DERR_NOTAVAILABLE;
900             goto err;
901         }
902     }
903 
904     if (swapchain->desc.backbuffer_count > 0)
905     {
906         if (!(swapchain->back_buffers = heap_calloc(swapchain->desc.backbuffer_count,
907                 sizeof(*swapchain->back_buffers))))
908         {
909             ERR("Failed to allocate backbuffer array memory.\n");
910             hr = E_OUTOFMEMORY;
911             goto err;
912         }
913 
914         texture_desc.usage = swapchain->desc.backbuffer_usage;
915         for (i = 0; i < swapchain->desc.backbuffer_count; ++i)
916         {
917             TRACE("Creating back buffer %u.\n", i);
918             if (FAILED(hr = device->device_parent->ops->create_swapchain_texture(device->device_parent,
919                     parent, &texture_desc, texture_flags, &swapchain->back_buffers[i])))
920             {
921                 WARN("Failed to create back buffer %u, hr %#x.\n", i, hr);
922                 swapchain->desc.backbuffer_count = i;
923                 goto err;
924             }
925             wined3d_texture_set_swapchain(swapchain->back_buffers[i], swapchain);
926         }
927     }
928 
929     /* Swapchains share the depth/stencil buffer, so only create a single depthstencil surface. */
930     if (desc->enable_auto_depth_stencil && !(device->wined3d->flags & WINED3D_NO3D))
931     {
932         TRACE("Creating depth/stencil buffer.\n");
933         if (!device->auto_depth_stencil_view)
934         {
935             struct wined3d_view_desc desc;
936             struct wined3d_texture *ds;
937 
938             texture_desc.format = swapchain->desc.auto_depth_stencil_format;
939             texture_desc.usage = WINED3DUSAGE_DEPTHSTENCIL;
940 
941             if (FAILED(hr = device->device_parent->ops->create_swapchain_texture(device->device_parent,
942                     device->device_parent, &texture_desc, texture_flags, &ds)))
943             {
944                 WARN("Failed to create the auto depth/stencil surface, hr %#x.\n", hr);
945                 goto err;
946             }
947 
948             desc.format_id = ds->resource.format->id;
949             desc.flags = 0;
950             desc.u.texture.level_idx = 0;
951             desc.u.texture.level_count = 1;
952             desc.u.texture.layer_idx = 0;
953             desc.u.texture.layer_count = 1;
954             hr = wined3d_rendertarget_view_create(&desc, &ds->resource, NULL, &wined3d_null_parent_ops,
955                     &device->auto_depth_stencil_view);
956             wined3d_texture_decref(ds);
957             if (FAILED(hr))
958             {
959                 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
960                 goto err;
961             }
962         }
963     }
964 
965     wined3d_swapchain_get_gamma_ramp(swapchain, &swapchain->orig_gamma);
966 
967     return WINED3D_OK;
968 
969 err:
970     if (displaymode_set)
971     {
972         if (FAILED(wined3d_set_adapter_display_mode(device->wined3d,
973                 adapter->ordinal, &swapchain->original_mode)))
974             ERR("Failed to restore display mode.\n");
975         ClipCursor(NULL);
976     }
977 
978     if (swapchain->back_buffers)
979     {
980         for (i = 0; i < swapchain->desc.backbuffer_count; ++i)
981         {
982             if (swapchain->back_buffers[i])
983             {
984                 wined3d_texture_set_swapchain(swapchain->back_buffers[i], NULL);
985                 wined3d_texture_decref(swapchain->back_buffers[i]);
986             }
987         }
988         heap_free(swapchain->back_buffers);
989     }
990 
991     wined3d_cs_destroy_object(swapchain->device->cs, wined3d_swapchain_destroy_object, swapchain);
992     swapchain->device->cs->ops->finish(device->cs, WINED3D_CS_QUEUE_DEFAULT);
993 
994     if (swapchain->front_buffer)
995     {
996         wined3d_texture_set_swapchain(swapchain->front_buffer, NULL);
997         wined3d_texture_decref(swapchain->front_buffer);
998     }
999 
1000     return hr;
1001 }
1002 
1003 HRESULT CDECL wined3d_swapchain_create(struct wined3d_device *device, struct wined3d_swapchain_desc *desc,
1004         void *parent, const struct wined3d_parent_ops *parent_ops, struct wined3d_swapchain **swapchain)
1005 {
1006     struct wined3d_swapchain *object;
1007     HRESULT hr;
1008 
1009     TRACE("device %p, desc %p, parent %p, parent_ops %p, swapchain %p.\n",
1010             device, desc, parent, parent_ops, swapchain);
1011 
1012     if (!(object = heap_alloc_zero(sizeof(*object))))
1013         return E_OUTOFMEMORY;
1014 
1015     hr = swapchain_init(object, device, desc, parent, parent_ops);
1016     if (FAILED(hr))
1017     {
1018         WARN("Failed to initialize swapchain, hr %#x.\n", hr);
1019         heap_free(object);
1020         return hr;
1021     }
1022 
1023     TRACE("Created swapchain %p.\n", object);
1024     *swapchain = object;
1025 
1026     return WINED3D_OK;
1027 }
1028 
1029 static struct wined3d_context *swapchain_create_context(struct wined3d_swapchain *swapchain)
1030 {
1031     struct wined3d_context **ctx_array;
1032     struct wined3d_context *ctx;
1033 
1034     TRACE("Creating a new context for swapchain %p, thread %u.\n", swapchain, GetCurrentThreadId());
1035 
1036     if (!(ctx = context_create(swapchain, swapchain->front_buffer, swapchain->ds_format)))
1037     {
1038         ERR("Failed to create a new context for the swapchain\n");
1039         return NULL;
1040     }
1041     context_release(ctx);
1042 
1043     if (!(ctx_array = heap_calloc(swapchain->num_contexts + 1, sizeof(*ctx_array))))
1044     {
1045         ERR("Out of memory when trying to allocate a new context array\n");
1046         context_destroy(swapchain->device, ctx);
1047         return NULL;
1048     }
1049     memcpy(ctx_array, swapchain->context, sizeof(*ctx_array) * swapchain->num_contexts);
1050     heap_free(swapchain->context);
1051     ctx_array[swapchain->num_contexts] = ctx;
1052     swapchain->context = ctx_array;
1053     swapchain->num_contexts++;
1054 
1055     TRACE("Returning context %p\n", ctx);
1056     return ctx;
1057 }
1058 
1059 void swapchain_destroy_contexts(struct wined3d_swapchain *swapchain)
1060 {
1061     unsigned int i;
1062 
1063     for (i = 0; i < swapchain->num_contexts; ++i)
1064     {
1065         context_destroy(swapchain->device, swapchain->context[i]);
1066     }
1067     heap_free(swapchain->context);
1068     swapchain->num_contexts = 0;
1069     swapchain->context = NULL;
1070 }
1071 
1072 struct wined3d_context *swapchain_get_context(struct wined3d_swapchain *swapchain)
1073 {
1074     DWORD tid = GetCurrentThreadId();
1075     unsigned int i;
1076 
1077     for (i = 0; i < swapchain->num_contexts; ++i)
1078     {
1079         if (swapchain->context[i]->tid == tid)
1080             return swapchain->context[i];
1081     }
1082 
1083     /* Create a new context for the thread */
1084     return swapchain_create_context(swapchain);
1085 }
1086 
1087 HDC swapchain_get_backup_dc(struct wined3d_swapchain *swapchain)
1088 {
1089     if (!swapchain->backup_dc)
1090     {
1091         TRACE("Creating the backup window for swapchain %p.\n", swapchain);
1092 
1093         if (!(swapchain->backup_wnd = CreateWindowA(WINED3D_OPENGL_WINDOW_CLASS_NAME, "WineD3D fake window",
1094                 WS_OVERLAPPEDWINDOW, 10, 10, 10, 10, NULL, NULL, NULL, NULL)))
1095         {
1096             ERR("Failed to create a window.\n");
1097             return NULL;
1098         }
1099 
1100         if (!(swapchain->backup_dc = GetDC(swapchain->backup_wnd)))
1101         {
1102             ERR("Failed to get a DC.\n");
1103             DestroyWindow(swapchain->backup_wnd);
1104             swapchain->backup_wnd = NULL;
1105             return NULL;
1106         }
1107     }
1108 
1109     return swapchain->backup_dc;
1110 }
1111 
1112 void swapchain_update_draw_bindings(struct wined3d_swapchain *swapchain)
1113 {
1114     UINT i;
1115 
1116     wined3d_resource_update_draw_binding(&swapchain->front_buffer->resource);
1117 
1118     for (i = 0; i < swapchain->desc.backbuffer_count; ++i)
1119     {
1120         wined3d_resource_update_draw_binding(&swapchain->back_buffers[i]->resource);
1121     }
1122 }
1123 
1124 void wined3d_swapchain_activate(struct wined3d_swapchain *swapchain, BOOL activate)
1125 {
1126     struct wined3d_device *device = swapchain->device;
1127     BOOL filter_messages = device->filter_messages;
1128 
1129     /* This code is not protected by the wined3d mutex, so it may run while
1130      * wined3d_device_reset is active. Testing on Windows shows that changing
1131      * focus during resets and resetting during focus change events causes
1132      * the application to crash with an invalid memory access. */
1133 
1134     device->filter_messages = !(device->wined3d->flags & WINED3D_FOCUS_MESSAGES);
1135 
1136     if (activate)
1137     {
1138         if (!(device->create_parms.flags & WINED3DCREATE_NOWINDOWCHANGES))
1139         {
1140             /* The d3d versions do not agree on the exact messages here. D3d8 restores
1141              * the window but leaves the size untouched, d3d9 sets the size on an
1142              * invisible window, generates messages but doesn't change the window
1143              * properties. The implementation follows d3d9.
1144              *
1145              * Guild Wars 1 wants a WINDOWPOSCHANGED message on the device window to
1146              * resume drawing after a focus loss. */
1147             SetWindowPos(swapchain->device_window, NULL, 0, 0,
1148                     swapchain->desc.backbuffer_width, swapchain->desc.backbuffer_height,
1149                     SWP_NOACTIVATE | SWP_NOZORDER);
1150         }
1151 
1152         if (device->wined3d->flags & WINED3D_RESTORE_MODE_ON_ACTIVATE)
1153         {
1154             if (FAILED(wined3d_set_adapter_display_mode(device->wined3d,
1155                     device->adapter->ordinal, &swapchain->d3d_mode)))
1156                 ERR("Failed to set display mode.\n");
1157         }
1158     }
1159     else
1160     {
1161         if (FAILED(wined3d_set_adapter_display_mode(device->wined3d,
1162                 device->adapter->ordinal, NULL)))
1163             ERR("Failed to set display mode.\n");
1164 
1165         swapchain->reapply_mode = TRUE;
1166 
1167         if (!(device->create_parms.flags & WINED3DCREATE_NOWINDOWCHANGES)
1168                 && IsWindowVisible(swapchain->device_window))
1169             ShowWindow(swapchain->device_window, SW_MINIMIZE);
1170     }
1171 
1172     device->filter_messages = filter_messages;
1173 }
1174 
1175 HRESULT CDECL wined3d_swapchain_resize_buffers(struct wined3d_swapchain *swapchain, unsigned int buffer_count,
1176         unsigned int width, unsigned int height, enum wined3d_format_id format_id,
1177         enum wined3d_multisample_type multisample_type, unsigned int multisample_quality)
1178 {
1179     struct wined3d_device *device = swapchain->device;
1180     BOOL update_desc = FALSE;
1181 
1182     TRACE("swapchain %p, buffer_count %u, width %u, height %u, format %s, "
1183             "multisample_type %#x, multisample_quality %#x.\n",
1184             swapchain, buffer_count, width, height, debug_d3dformat(format_id),
1185             multisample_type, multisample_quality);
1186 
1187     wined3d_swapchain_apply_sample_count_override(swapchain, format_id, &multisample_type, &multisample_quality);
1188 
1189     if (buffer_count && buffer_count != swapchain->desc.backbuffer_count)
1190         FIXME("Cannot change the back buffer count yet.\n");
1191 
1192     device->cs->ops->finish(device->cs, WINED3D_CS_QUEUE_DEFAULT);
1193 
1194     if (!width || !height)
1195     {
1196         /* The application is requesting that either the swapchain width or
1197          * height be set to the corresponding dimension in the window's
1198          * client rect. */
1199 
1200         RECT client_rect;
1201 
1202         if (!swapchain->desc.windowed)
1203             return WINED3DERR_INVALIDCALL;
1204 
1205         if (!GetClientRect(swapchain->device_window, &client_rect))
1206         {
1207             ERR("Failed to get client rect, last error %#x.\n", GetLastError());
1208             return WINED3DERR_INVALIDCALL;
1209         }
1210 
1211         if (!width)
1212             width = client_rect.right;
1213 
1214         if (!height)
1215             height = client_rect.bottom;
1216     }
1217 
1218     if (width != swapchain->desc.backbuffer_width
1219             || height != swapchain->desc.backbuffer_height)
1220     {
1221         swapchain->desc.backbuffer_width = width;
1222         swapchain->desc.backbuffer_height = height;
1223         update_desc = TRUE;
1224     }
1225 
1226     if (format_id == WINED3DFMT_UNKNOWN)
1227     {
1228         if (!swapchain->desc.windowed)
1229             return WINED3DERR_INVALIDCALL;
1230         format_id = swapchain->original_mode.format_id;
1231     }
1232 
1233     if (format_id != swapchain->desc.backbuffer_format)
1234     {
1235         swapchain->desc.backbuffer_format = format_id;
1236         update_desc = TRUE;
1237     }
1238 
1239     if (multisample_type != swapchain->desc.multisample_type
1240             || multisample_quality != swapchain->desc.multisample_quality)
1241     {
1242         swapchain->desc.multisample_type = multisample_type;
1243         swapchain->desc.multisample_quality = multisample_quality;
1244         update_desc = TRUE;
1245     }
1246 
1247     if (update_desc)
1248     {
1249         HRESULT hr;
1250         UINT i;
1251 
1252         if (FAILED(hr = wined3d_texture_update_desc(swapchain->front_buffer, swapchain->desc.backbuffer_width,
1253                 swapchain->desc.backbuffer_height, swapchain->desc.backbuffer_format,
1254                 swapchain->desc.multisample_type, swapchain->desc.multisample_quality, NULL, 0)))
1255             return hr;
1256 
1257         for (i = 0; i < swapchain->desc.backbuffer_count; ++i)
1258         {
1259             if (FAILED(hr = wined3d_texture_update_desc(swapchain->back_buffers[i], swapchain->desc.backbuffer_width,
1260                     swapchain->desc.backbuffer_height, swapchain->desc.backbuffer_format,
1261                     swapchain->desc.multisample_type, swapchain->desc.multisample_quality, NULL, 0)))
1262                 return hr;
1263         }
1264     }
1265 
1266     swapchain_update_render_to_fbo(swapchain);
1267     swapchain_update_draw_bindings(swapchain);
1268 
1269     return WINED3D_OK;
1270 }
1271 
1272 static HRESULT wined3d_swapchain_set_display_mode(struct wined3d_swapchain *swapchain,
1273         struct wined3d_display_mode *mode)
1274 {
1275     struct wined3d_device *device = swapchain->device;
1276     HRESULT hr;
1277 
1278     if (swapchain->desc.flags & WINED3D_SWAPCHAIN_USE_CLOSEST_MATCHING_MODE)
1279     {
1280         if (FAILED(hr = wined3d_find_closest_matching_adapter_mode(device->wined3d,
1281                 device->adapter->ordinal, mode)))
1282         {
1283             WARN("Failed to find closest matching mode, hr %#x.\n", hr);
1284         }
1285     }
1286 
1287     if (FAILED(hr = wined3d_set_adapter_display_mode(device->wined3d,
1288             device->adapter->ordinal, mode)))
1289     {
1290         WARN("Failed to set display mode, hr %#x.\n", hr);
1291         return WINED3DERR_INVALIDCALL;
1292     }
1293 
1294     return WINED3D_OK;
1295 }
1296 
1297 HRESULT CDECL wined3d_swapchain_resize_target(struct wined3d_swapchain *swapchain,
1298         const struct wined3d_display_mode *mode)
1299 {
1300     struct wined3d_device *device = swapchain->device;
1301     struct wined3d_display_mode actual_mode;
1302     RECT original_window_rect, window_rect;
1303     HRESULT hr;
1304 
1305     TRACE("swapchain %p, mode %p.\n", swapchain, mode);
1306 
1307     if (swapchain->desc.windowed)
1308     {
1309         SetRect(&window_rect, 0, 0, mode->width, mode->height);
1310         AdjustWindowRectEx(&window_rect,
1311                 GetWindowLongW(swapchain->device_window, GWL_STYLE), FALSE,
1312                 GetWindowLongW(swapchain->device_window, GWL_EXSTYLE));
1313         SetRect(&window_rect, 0, 0,
1314                 window_rect.right - window_rect.left, window_rect.bottom - window_rect.top);
1315         GetWindowRect(swapchain->device_window, &original_window_rect);
1316         OffsetRect(&window_rect, original_window_rect.left, original_window_rect.top);
1317     }
1318     else if (swapchain->desc.flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH)
1319     {
1320         actual_mode = *mode;
1321         if (FAILED(hr = wined3d_swapchain_set_display_mode(swapchain, &actual_mode)))
1322             return hr;
1323         SetRect(&window_rect, 0, 0, actual_mode.width, actual_mode.height);
1324     }
1325     else
1326     {
1327         if (FAILED(hr = wined3d_get_adapter_display_mode(device->wined3d, device->adapter->ordinal,
1328                 &actual_mode, NULL)))
1329         {
1330             ERR("Failed to get display mode, hr %#x.\n", hr);
1331             return WINED3DERR_INVALIDCALL;
1332         }
1333 
1334         SetRect(&window_rect, 0, 0, actual_mode.width, actual_mode.height);
1335     }
1336 
1337     MoveWindow(swapchain->device_window, window_rect.left, window_rect.top,
1338             window_rect.right - window_rect.left,
1339             window_rect.bottom - window_rect.top, TRUE);
1340 
1341     return WINED3D_OK;
1342 }
1343 
1344 HRESULT CDECL wined3d_swapchain_set_fullscreen(struct wined3d_swapchain *swapchain,
1345         const struct wined3d_swapchain_desc *swapchain_desc, const struct wined3d_display_mode *mode)
1346 {
1347     struct wined3d_device *device = swapchain->device;
1348     struct wined3d_display_mode actual_mode;
1349     HRESULT hr;
1350 
1351     TRACE("swapchain %p, desc %p, mode %p.\n", swapchain, swapchain_desc, mode);
1352 
1353     if (swapchain->desc.flags & WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH)
1354     {
1355         if (mode)
1356         {
1357             actual_mode = *mode;
1358         }
1359         else
1360         {
1361             if (!swapchain_desc->windowed)
1362             {
1363                 actual_mode.width = swapchain_desc->backbuffer_width;
1364                 actual_mode.height = swapchain_desc->backbuffer_height;
1365                 actual_mode.refresh_rate = swapchain_desc->refresh_rate;
1366                 actual_mode.format_id = swapchain_desc->backbuffer_format;
1367                 actual_mode.scanline_ordering = WINED3D_SCANLINE_ORDERING_UNKNOWN;
1368             }
1369             else
1370             {
1371                 actual_mode = swapchain->original_mode;
1372             }
1373         }
1374 
1375         if (FAILED(hr = wined3d_swapchain_set_display_mode(swapchain, &actual_mode)))
1376             return hr;
1377     }
1378     else
1379     {
1380         if (mode)
1381             WARN("WINED3D_SWAPCHAIN_ALLOW_MODE_SWITCH is not set, ignoring mode.\n");
1382 
1383         if (FAILED(hr = wined3d_get_adapter_display_mode(device->wined3d, device->adapter->ordinal,
1384                 &actual_mode, NULL)))
1385         {
1386             ERR("Failed to get display mode, hr %#x.\n", hr);
1387             return WINED3DERR_INVALIDCALL;
1388         }
1389     }
1390 
1391     if (!swapchain_desc->windowed)
1392     {
1393         unsigned int width = actual_mode.width;
1394         unsigned int height = actual_mode.height;
1395 
1396         if (swapchain->desc.windowed)
1397         {
1398             /* Switch from windowed to fullscreen */
1399             HWND focus_window = device->create_parms.focus_window;
1400             if (!focus_window)
1401                 focus_window = swapchain->device_window;
1402             if (FAILED(hr = wined3d_device_acquire_focus_window(device, focus_window)))
1403             {
1404                 ERR("Failed to acquire focus window, hr %#x.\n", hr);
1405                 return hr;
1406             }
1407 
1408             wined3d_device_setup_fullscreen_window(device, swapchain->device_window, width, height);
1409         }
1410         else
1411         {
1412             /* Fullscreen -> fullscreen mode change */
1413             BOOL filter_messages = device->filter_messages;
1414             device->filter_messages = TRUE;
1415 
1416             MoveWindow(swapchain->device_window, 0, 0, width, height, TRUE);
1417             ShowWindow(swapchain->device_window, SW_SHOW);
1418 
1419             device->filter_messages = filter_messages;
1420         }
1421         swapchain->d3d_mode = actual_mode;
1422     }
1423     else if (!swapchain->desc.windowed)
1424     {
1425         /* Fullscreen -> windowed switch */
1426         RECT *window_rect = NULL;
1427         if (swapchain->desc.flags & WINED3D_SWAPCHAIN_RESTORE_WINDOW_RECT)
1428             window_rect = &swapchain->original_window_rect;
1429         wined3d_device_restore_fullscreen_window(device, swapchain->device_window, window_rect);
1430         wined3d_device_release_focus_window(device);
1431     }
1432 
1433     swapchain->desc.windowed = swapchain_desc->windowed;
1434 
1435     return WINED3D_OK;
1436 }
1437