wined3d: Get rid of the WINED3DSWAPEFFECT typedef.
[wine] / dlls / wined3d / swapchain.c
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 "wined3d_private.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
27 WINE_DECLARE_DEBUG_CHANNEL(fps);
28
29 /* Do not call while under the GL lock. */
30 static void swapchain_cleanup(struct wined3d_swapchain *swapchain)
31 {
32     struct wined3d_display_mode mode;
33     UINT i;
34
35     TRACE("Destroying swapchain %p.\n", swapchain);
36
37     wined3d_swapchain_set_gamma_ramp(swapchain, 0, &swapchain->orig_gamma);
38
39     /* Release the swapchain's draw buffers. Make sure swapchain->back_buffers[0]
40      * is the last buffer to be destroyed, FindContext() depends on that. */
41     if (swapchain->front_buffer)
42     {
43         surface_set_container(swapchain->front_buffer, WINED3D_CONTAINER_NONE, NULL);
44         if (wined3d_surface_decref(swapchain->front_buffer))
45             WARN("Something's still holding the front buffer (%p).\n", swapchain->front_buffer);
46         swapchain->front_buffer = NULL;
47     }
48
49     if (swapchain->back_buffers)
50     {
51         i = swapchain->desc.backbuffer_count;
52
53         while (i--)
54         {
55             surface_set_container(swapchain->back_buffers[i], WINED3D_CONTAINER_NONE, NULL);
56             if (wined3d_surface_decref(swapchain->back_buffers[i]))
57                 WARN("Something's still holding back buffer %u (%p).\n", i, swapchain->back_buffers[i]);
58         }
59         HeapFree(GetProcessHeap(), 0, swapchain->back_buffers);
60         swapchain->back_buffers = NULL;
61     }
62
63     for (i = 0; i < swapchain->num_contexts; ++i)
64     {
65         context_destroy(swapchain->device, swapchain->context[i]);
66     }
67     HeapFree(GetProcessHeap(), 0, swapchain->context);
68
69     /* Restore the screen resolution if we rendered in fullscreen.
70      * This will restore the screen resolution to what it was before creating
71      * the swapchain. In case of d3d8 and d3d9 this will be the original
72      * desktop resolution. In case of d3d7 this will be a NOP because ddraw
73      * sets the resolution before starting up Direct3D, thus orig_width and
74      * orig_height will be equal to the modes in the presentation params. */
75     if (!swapchain->desc.windowed && swapchain->desc.auto_restore_display_mode)
76     {
77         mode.width = swapchain->orig_width;
78         mode.height = swapchain->orig_height;
79         mode.refresh_rate = 0;
80         mode.format_id = swapchain->orig_fmt;
81         wined3d_device_set_display_mode(swapchain->device, 0, &mode);
82     }
83
84     if (swapchain->backup_dc)
85     {
86         TRACE("Destroying backup wined3d window %p, dc %p.\n", swapchain->backup_wnd, swapchain->backup_dc);
87
88         ReleaseDC(swapchain->backup_wnd, swapchain->backup_dc);
89         DestroyWindow(swapchain->backup_wnd);
90     }
91 }
92
93 ULONG CDECL wined3d_swapchain_incref(struct wined3d_swapchain *swapchain)
94 {
95     ULONG refcount = InterlockedIncrement(&swapchain->ref);
96
97     TRACE("%p increasing refcount to %u.\n", swapchain, refcount);
98
99     return refcount;
100 }
101
102 /* Do not call while under the GL lock. */
103 ULONG CDECL wined3d_swapchain_decref(struct wined3d_swapchain *swapchain)
104 {
105     ULONG refcount = InterlockedDecrement(&swapchain->ref);
106
107     TRACE("%p decreasing refcount to %u.\n", swapchain, refcount);
108
109     if (!refcount)
110     {
111         swapchain_cleanup(swapchain);
112         swapchain->parent_ops->wined3d_object_destroyed(swapchain->parent);
113         HeapFree(GetProcessHeap(), 0, swapchain);
114     }
115
116     return refcount;
117 }
118
119 void * CDECL wined3d_swapchain_get_parent(const struct wined3d_swapchain *swapchain)
120 {
121     TRACE("swapchain %p.\n", swapchain);
122
123     return swapchain->parent;
124 }
125
126 HRESULT CDECL wined3d_swapchain_set_window(struct wined3d_swapchain *swapchain, HWND window)
127 {
128     if (!window)
129         window = swapchain->device_window;
130     if (window == swapchain->win_handle)
131         return WINED3D_OK;
132
133     TRACE("Setting swapchain %p window from %p to %p.\n",
134             swapchain, swapchain->win_handle, window);
135     swapchain->win_handle = window;
136
137     return WINED3D_OK;
138 }
139
140 HRESULT CDECL wined3d_swapchain_present(struct wined3d_swapchain *swapchain,
141         const RECT *src_rect, const RECT *dst_rect, HWND dst_window_override,
142         const RGNDATA *dirty_region, DWORD flags)
143 {
144     TRACE("swapchain %p, src_rect %s, dst_rect %s, dst_window_override %p, dirty_region %p, flags %#x.\n",
145             swapchain, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect),
146             dst_window_override, dirty_region, flags);
147
148     wined3d_swapchain_set_window(swapchain, dst_window_override);
149
150     return swapchain->swapchain_ops->swapchain_present(swapchain,
151             src_rect, dst_rect, dirty_region, flags);
152 }
153
154 HRESULT CDECL wined3d_swapchain_get_front_buffer_data(const struct wined3d_swapchain *swapchain,
155         struct wined3d_surface *dst_surface)
156 {
157     struct wined3d_surface *src_surface;
158     RECT src_rect, dst_rect;
159
160     TRACE("swapchain %p, dst_surface %p.\n", swapchain, dst_surface);
161
162     src_surface = swapchain->front_buffer;
163     SetRect(&src_rect, 0, 0, src_surface->resource.width, src_surface->resource.height);
164     dst_rect = src_rect;
165
166     if (swapchain->desc.windowed)
167     {
168         MapWindowPoints(swapchain->win_handle, NULL, (POINT *)&dst_rect, 2);
169         FIXME("Using destination rect %s in windowed mode, this is likely wrong.\n",
170                 wine_dbgstr_rect(&dst_rect));
171     }
172
173     return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, &src_rect, 0, NULL, WINED3DTEXF_POINT);
174 }
175
176 HRESULT CDECL wined3d_swapchain_get_back_buffer(const struct wined3d_swapchain *swapchain,
177         UINT back_buffer_idx, enum wined3d_backbuffer_type type, struct wined3d_surface **back_buffer)
178 {
179     TRACE("swapchain %p, back_buffer_idx %u, type %#x, back_buffer %p.\n",
180             swapchain, back_buffer_idx, type, back_buffer);
181
182     /* Return invalid if there is no backbuffer array, otherwise it will
183      * crash when ddraw is used (there swapchain->back_buffers is always
184      * NULL). We need this because this function is called from
185      * stateblock_init_default_state() to get the default scissorrect
186      * dimensions. */
187     if (!swapchain->back_buffers || back_buffer_idx >= swapchain->desc.backbuffer_count)
188     {
189         WARN("Invalid back buffer index.\n");
190         /* Native d3d9 doesn't set NULL here, just as wine's d3d9. But set it
191          * here in wined3d to avoid problems in other libs. */
192         *back_buffer = NULL;
193         return WINED3DERR_INVALIDCALL;
194     }
195
196     *back_buffer = swapchain->back_buffers[back_buffer_idx];
197     if (*back_buffer)
198         wined3d_surface_incref(*back_buffer);
199
200     TRACE("Returning back buffer %p.\n", *back_buffer);
201
202     return WINED3D_OK;
203 }
204
205 HRESULT CDECL wined3d_swapchain_get_raster_status(const struct wined3d_swapchain *swapchain,
206         struct wined3d_raster_status *raster_status)
207 {
208     static BOOL warned;
209     /* No OpenGL equivalent */
210     if (!warned)
211     {
212         FIXME("swapchain %p, raster_status %p stub!\n", swapchain, raster_status);
213         warned = TRUE;
214     }
215
216     /* Obtaining the raster status is a widely implemented but optional
217      * feature. When this method returns OK StarCraft 2 expects the
218      * raster_status->InVBlank value to actually change over time. To prevent
219      * StarCraft 2 from running in an infinite loop at startup this method
220      * returns INVALIDCALL. */
221     return WINED3DERR_INVALIDCALL;
222 }
223
224 HRESULT CDECL wined3d_swapchain_get_display_mode(const struct wined3d_swapchain *swapchain,
225         struct wined3d_display_mode *mode)
226 {
227     HRESULT hr;
228
229     TRACE("swapchain %p, mode %p.\n", swapchain, mode);
230
231     hr = wined3d_get_adapter_display_mode(swapchain->device->wined3d, swapchain->device->adapter->ordinal, mode);
232
233     TRACE("Returning w %u, h %u, refresh rate %u, format %s.\n",
234             mode->width, mode->height, mode->refresh_rate, debug_d3dformat(mode->format_id));
235
236     return hr;
237 }
238
239 struct wined3d_device * CDECL wined3d_swapchain_get_device(const struct wined3d_swapchain *swapchain)
240 {
241     TRACE("swapchain %p.\n", swapchain);
242
243     return swapchain->device;
244 }
245
246 HRESULT CDECL wined3d_swapchain_get_desc(const struct wined3d_swapchain *swapchain,
247         struct wined3d_swapchain_desc *desc)
248 {
249     TRACE("swapchain %p, desc %p.\n", swapchain, desc);
250
251     *desc = swapchain->desc;
252
253     return WINED3D_OK;
254 }
255
256 HRESULT CDECL wined3d_swapchain_set_gamma_ramp(const struct wined3d_swapchain *swapchain,
257         DWORD flags, const struct wined3d_gamma_ramp *ramp)
258 {
259     HDC dc;
260
261     TRACE("swapchain %p, flags %#x, ramp %p.\n", swapchain, flags, ramp);
262
263     if (flags)
264         FIXME("Ignoring flags %#x.\n", flags);
265
266     dc = GetDC(swapchain->device_window);
267     SetDeviceGammaRamp(dc, (void *)ramp);
268     ReleaseDC(swapchain->device_window, dc);
269
270     return WINED3D_OK;
271 }
272
273 HRESULT CDECL wined3d_swapchain_get_gamma_ramp(const struct wined3d_swapchain *swapchain,
274         struct wined3d_gamma_ramp *ramp)
275 {
276     HDC dc;
277
278     TRACE("swapchain %p, ramp %p.\n", swapchain, ramp);
279
280     dc = GetDC(swapchain->device_window);
281     GetDeviceGammaRamp(dc, ramp);
282     ReleaseDC(swapchain->device_window, dc);
283
284     return WINED3D_OK;
285 }
286
287 /* A GL context is provided by the caller */
288 static void swapchain_blit(const struct wined3d_swapchain *swapchain,
289         struct wined3d_context *context, const RECT *src_rect, const RECT *dst_rect)
290 {
291     struct wined3d_surface *backbuffer = swapchain->back_buffers[0];
292     UINT src_w = src_rect->right - src_rect->left;
293     UINT src_h = src_rect->bottom - src_rect->top;
294     GLenum gl_filter;
295     const struct wined3d_gl_info *gl_info = context->gl_info;
296     RECT win_rect;
297     UINT win_h;
298
299     TRACE("swapchain %p, context %p, src_rect %s, dst_rect %s.\n",
300             swapchain, context, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect));
301
302     if (src_w == dst_rect->right - dst_rect->left && src_h == dst_rect->bottom - dst_rect->top)
303         gl_filter = GL_NEAREST;
304     else
305         gl_filter = GL_LINEAR;
306
307     GetClientRect(swapchain->win_handle, &win_rect);
308     win_h = win_rect.bottom - win_rect.top;
309
310     if (gl_info->fbo_ops.glBlitFramebuffer && is_identity_fixup(backbuffer->resource.format->color_fixup))
311     {
312         DWORD location = SFLAG_INTEXTURE;
313
314         if (backbuffer->resource.multisample_type)
315         {
316             location = SFLAG_INRB_RESOLVED;
317             surface_load_location(backbuffer, location, NULL);
318         }
319
320         ENTER_GL();
321         context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, backbuffer, NULL, location);
322         glReadBuffer(GL_COLOR_ATTACHMENT0);
323         context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
324
325         context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
326         context_set_draw_buffer(context, GL_BACK);
327         context_invalidate_state(context, STATE_FRAMEBUFFER);
328
329         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
330         context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
331         context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
332         context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
333         context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
334
335         glDisable(GL_SCISSOR_TEST);
336         context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
337
338         /* Note that the texture is upside down */
339         gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
340                 dst_rect->left, win_h - dst_rect->top, dst_rect->right, win_h - dst_rect->bottom,
341                 GL_COLOR_BUFFER_BIT, gl_filter);
342         checkGLcall("Swapchain present blit(EXT_framebuffer_blit)\n");
343         LEAVE_GL();
344     }
345     else
346     {
347         struct wined3d_device *device = swapchain->device;
348         struct wined3d_context *context2;
349         float tex_left = src_rect->left;
350         float tex_top = src_rect->top;
351         float tex_right = src_rect->right;
352         float tex_bottom = src_rect->bottom;
353
354         context2 = context_acquire(device, swapchain->back_buffers[0]);
355         context_apply_blit_state(context2, device);
356
357         if (backbuffer->flags & SFLAG_NORMCOORD)
358         {
359             tex_left /= src_w;
360             tex_right /= src_w;
361             tex_top /= src_h;
362             tex_bottom /= src_h;
363         }
364
365         if (is_complex_fixup(backbuffer->resource.format->color_fixup))
366             gl_filter = GL_NEAREST;
367
368         ENTER_GL();
369         context_apply_fbo_state_blit(context2, GL_FRAMEBUFFER, swapchain->front_buffer, NULL, SFLAG_INDRAWABLE);
370
371         /* Set up the texture. The surface is not in a wined3d_texture
372          * container, so there are no D3D texture settings to dirtify. */
373         device->blitter->set_shader(device->blit_priv, context2, backbuffer);
374         glTexParameteri(backbuffer->texture_target, GL_TEXTURE_MIN_FILTER, gl_filter);
375         glTexParameteri(backbuffer->texture_target, GL_TEXTURE_MAG_FILTER, gl_filter);
376
377         context_set_draw_buffer(context, GL_BACK);
378
379         /* Set the viewport to the destination rectandle, disable any projection
380          * transformation set up by context_apply_blit_state(), and draw a
381          * (-1,-1)-(1,1) quad.
382          *
383          * Back up viewport and matrix to avoid breaking last_was_blit
384          *
385          * Note that context_apply_blit_state() set up viewport and ortho to
386          * match the surface size - we want the GL drawable(=window) size. */
387         glPushAttrib(GL_VIEWPORT_BIT);
388         glViewport(dst_rect->left, win_h - dst_rect->bottom, dst_rect->right, win_h - dst_rect->top);
389         glMatrixMode(GL_PROJECTION);
390         glPushMatrix();
391         glLoadIdentity();
392
393         glBegin(GL_QUADS);
394             /* bottom left */
395             glTexCoord2f(tex_left, tex_bottom);
396             glVertex2i(-1, -1);
397
398             /* top left */
399             glTexCoord2f(tex_left, tex_top);
400             glVertex2i(-1, 1);
401
402             /* top right */
403             glTexCoord2f(tex_right, tex_top);
404             glVertex2i(1, 1);
405
406             /* bottom right */
407             glTexCoord2f(tex_right, tex_bottom);
408             glVertex2i(1, -1);
409         glEnd();
410
411         glPopMatrix();
412         glPopAttrib();
413
414         device->blitter->unset_shader(context->gl_info);
415         checkGLcall("Swapchain present blit(manual)\n");
416         LEAVE_GL();
417
418         context_release(context2);
419     }
420 }
421
422 static HRESULT swapchain_gl_present(struct wined3d_swapchain *swapchain, const RECT *src_rect_in,
423         const RECT *dst_rect_in, const RGNDATA *dirty_region, DWORD flags)
424 {
425     const struct wined3d_fb_state *fb = &swapchain->device->fb;
426     const struct wined3d_gl_info *gl_info;
427     struct wined3d_context *context;
428     RECT src_rect, dst_rect;
429     BOOL render_to_fbo;
430
431     context = context_acquire(swapchain->device, swapchain->back_buffers[0]);
432     if (!context->valid)
433     {
434         context_release(context);
435         WARN("Invalid context, skipping present.\n");
436         return WINED3D_OK;
437     }
438
439     gl_info = context->gl_info;
440
441     /* Render the cursor onto the back buffer, using our nifty directdraw blitting code :-) */
442     if (swapchain->device->bCursorVisible &&
443         swapchain->device->cursorTexture &&
444         !swapchain->device->hardwareCursor)
445     {
446         struct wined3d_surface cursor;
447         RECT destRect =
448         {
449             swapchain->device->xScreenSpace - swapchain->device->xHotSpot,
450             swapchain->device->yScreenSpace - swapchain->device->yHotSpot,
451             swapchain->device->xScreenSpace + swapchain->device->cursorWidth - swapchain->device->xHotSpot,
452             swapchain->device->yScreenSpace + swapchain->device->cursorHeight - swapchain->device->yHotSpot,
453         };
454         TRACE("Rendering the cursor. Creating fake surface at %p\n", &cursor);
455         /* Build a fake surface to call the Blitting code. It is not possible to use the interface passed by
456          * the application because we are only supposed to copy the information out. Using a fake surface
457          * allows to use the Blitting engine and avoid copying the whole texture -> render target blitting code.
458          */
459         memset(&cursor, 0, sizeof(cursor));
460         cursor.resource.ref = 1;
461         cursor.resource.device = swapchain->device;
462         cursor.resource.pool = WINED3DPOOL_SCRATCH;
463         cursor.resource.format = wined3d_get_format(gl_info, WINED3DFMT_B8G8R8A8_UNORM);
464         cursor.resource.resourceType = WINED3DRTYPE_SURFACE;
465         cursor.texture_name = swapchain->device->cursorTexture;
466         cursor.texture_target = GL_TEXTURE_2D;
467         cursor.texture_level = 0;
468         cursor.resource.width = swapchain->device->cursorWidth;
469         cursor.resource.height = swapchain->device->cursorHeight;
470         /* The cursor must have pow2 sizes */
471         cursor.pow2Width = cursor.resource.width;
472         cursor.pow2Height = cursor.resource.height;
473         /* The surface is in the texture */
474         cursor.flags |= SFLAG_INTEXTURE;
475         /* DDBLT_KEYSRC will cause BltOverride to enable the alpha test with GL_NOTEQUAL, 0.0,
476          * which is exactly what we want :-)
477          */
478         if (swapchain->desc.windowed)
479             MapWindowPoints(NULL, swapchain->win_handle, (LPPOINT)&destRect, 2);
480         wined3d_surface_blt(swapchain->back_buffers[0], &destRect,
481                 &cursor, NULL, WINEDDBLT_KEYSRC, NULL, WINED3DTEXF_POINT);
482     }
483
484     if (swapchain->device->logo_surface)
485     {
486         struct wined3d_surface *src_surface = swapchain->device->logo_surface;
487         RECT rect = {0, 0, src_surface->resource.width, src_surface->resource.height};
488
489         /* Blit the logo into the upper left corner of the drawable. */
490         wined3d_surface_blt(swapchain->back_buffers[0], &rect, src_surface, &rect,
491                  WINEDDBLT_KEYSRC, NULL, WINED3DTEXF_POINT);
492     }
493
494     TRACE("Presenting HDC %p.\n", context->hdc);
495
496     render_to_fbo = swapchain->render_to_fbo;
497
498     if (src_rect_in)
499     {
500         src_rect = *src_rect_in;
501         if (!render_to_fbo && (src_rect.left || src_rect.top
502                 || src_rect.right != swapchain->desc.backbuffer_width
503                 || src_rect.bottom != swapchain->desc.backbuffer_height))
504         {
505             render_to_fbo = TRUE;
506         }
507     }
508     else
509     {
510         src_rect.left = 0;
511         src_rect.top = 0;
512         src_rect.right = swapchain->desc.backbuffer_width;
513         src_rect.bottom = swapchain->desc.backbuffer_height;
514     }
515
516     if (dst_rect_in)
517         dst_rect = *dst_rect_in;
518     else
519         GetClientRect(swapchain->win_handle, &dst_rect);
520
521     if (!render_to_fbo && (dst_rect.left || dst_rect.top
522             || dst_rect.right != swapchain->desc.backbuffer_width
523             || dst_rect.bottom != swapchain->desc.backbuffer_height))
524         render_to_fbo = TRUE;
525
526     /* Rendering to a window of different size, presenting partial rectangles,
527      * or rendering to a different window needs help from FBO_blit or a textured
528      * draw. Render the swapchain to a FBO in the future.
529      *
530      * Note that FBO_blit from the backbuffer to the frontbuffer cannot solve
531      * all these issues - this fails if the window is smaller than the backbuffer.
532      */
533     if (!swapchain->render_to_fbo && render_to_fbo && wined3d_settings.offscreen_rendering_mode == ORM_FBO)
534     {
535         surface_load_location(swapchain->back_buffers[0], SFLAG_INTEXTURE, NULL);
536         surface_modify_location(swapchain->back_buffers[0], SFLAG_INDRAWABLE, FALSE);
537         swapchain->render_to_fbo = TRUE;
538         swapchain_update_draw_bindings(swapchain);
539     }
540
541     if (swapchain->render_to_fbo)
542     {
543         /* This codepath should only be hit with the COPY swapeffect. Otherwise a backbuffer-
544          * window size mismatch is impossible(fullscreen) and src and dst rectangles are
545          * not allowed(they need the COPY swapeffect)
546          *
547          * The DISCARD swap effect is ok as well since any backbuffer content is allowed after
548          * the swap. */
549         if (swapchain->desc.swap_effect == WINED3D_SWAP_EFFECT_FLIP)
550             FIXME("Render-to-fbo with WINED3D_SWAP_EFFECT_FLIP\n");
551
552         swapchain_blit(swapchain, context, &src_rect, &dst_rect);
553     }
554
555     if (swapchain->num_contexts > 1)
556         wglFinish();
557     SwapBuffers(context->hdc); /* TODO: cycle through the swapchain buffers */
558
559     TRACE("SwapBuffers called, Starting new frame\n");
560     /* FPS support */
561     if (TRACE_ON(fps))
562     {
563         DWORD time = GetTickCount();
564         ++swapchain->frames;
565
566         /* every 1.5 seconds */
567         if (time - swapchain->prev_time > 1500)
568         {
569             TRACE_(fps)("%p @ approx %.2ffps\n",
570                     swapchain, 1000.0 * swapchain->frames / (time - swapchain->prev_time));
571             swapchain->prev_time = time;
572             swapchain->frames = 0;
573         }
574     }
575
576     /* This is disabled, but the code left in for debug purposes.
577      *
578      * Since we're allowed to modify the new back buffer on a D3DSWAPEFFECT_DISCARD flip,
579      * we can clear it with some ugly color to make bad drawing visible and ease debugging.
580      * The Debug runtime does the same on Windows. However, a few games do not redraw the
581      * screen properly, like Max Payne 2, which leaves a few pixels undefined.
582      *
583      * Tests show that the content of the back buffer after a discard flip is indeed not
584      * reliable, so no game can depend on the exact content. However, it resembles the
585      * old contents in some way, for example by showing fragments at other locations. In
586      * general, the color theme is still intact. So Max payne, which draws rather dark scenes
587      * gets a dark background image. If we clear it with a bright ugly color, the game's
588      * bug shows up much more than it does on Windows, and the players see single pixels
589      * with wrong colors.
590      * (The Max Payne bug has been confirmed on Windows with the debug runtime) */
591     if (FALSE && swapchain->desc.swap_effect == WINED3D_SWAP_EFFECT_DISCARD)
592     {
593         static const struct wined3d_color cyan = {0.0f, 1.0f, 1.0f, 1.0f};
594
595         TRACE("Clearing the color buffer with cyan color\n");
596
597         wined3d_device_clear(swapchain->device, 0, NULL,
598                 WINED3DCLEAR_TARGET, &cyan, 1.0f, 0);
599     }
600
601     if (!swapchain->render_to_fbo && ((swapchain->front_buffer->flags & SFLAG_INSYSMEM)
602             || (swapchain->back_buffers[0]->flags & SFLAG_INSYSMEM)))
603     {
604         /* Both memory copies of the surfaces are ok, flip them around too instead of dirtifying
605          * Doesn't work with render_to_fbo because we're not flipping
606          */
607         struct wined3d_surface *front = swapchain->front_buffer;
608         struct wined3d_surface *back = swapchain->back_buffers[0];
609
610         if(front->resource.size == back->resource.size) {
611             DWORD fbflags;
612             flip_surface(front, back);
613
614             /* Tell the front buffer surface that is has been modified. However,
615              * the other locations were preserved during that, so keep the flags.
616              * This serves to update the emulated overlay, if any. */
617             fbflags = front->flags;
618             surface_modify_location(front, SFLAG_INDRAWABLE, TRUE);
619             front->flags = fbflags;
620         }
621         else
622         {
623             surface_modify_location(front, SFLAG_INDRAWABLE, TRUE);
624             surface_modify_location(back, SFLAG_INDRAWABLE, TRUE);
625         }
626     }
627     else
628     {
629         surface_modify_location(swapchain->front_buffer, SFLAG_INDRAWABLE, TRUE);
630         /* If the swapeffect is DISCARD, the back buffer is undefined. That means the SYSMEM
631          * and INTEXTURE copies can keep their old content if they have any defined content.
632          * If the swapeffect is COPY, the content remains the same. If it is FLIP however,
633          * the texture / sysmem copy needs to be reloaded from the drawable
634          */
635         if (swapchain->desc.swap_effect == WINED3D_SWAP_EFFECT_FLIP)
636             surface_modify_location(swapchain->back_buffers[0], swapchain->back_buffers[0]->draw_binding, TRUE);
637     }
638
639     if (fb->depth_stencil)
640     {
641         if (swapchain->desc.flags & WINED3DPRESENTFLAG_DISCARD_DEPTHSTENCIL
642                 || fb->depth_stencil->flags & SFLAG_DISCARD)
643         {
644             surface_modify_ds_location(fb->depth_stencil, SFLAG_DS_DISCARDED,
645                     fb->depth_stencil->resource.width,
646                     fb->depth_stencil->resource.height);
647             if (fb->depth_stencil == swapchain->device->onscreen_depth_stencil)
648             {
649                 wined3d_surface_decref(swapchain->device->onscreen_depth_stencil);
650                 swapchain->device->onscreen_depth_stencil = NULL;
651             }
652         }
653     }
654
655     context_release(context);
656
657     TRACE("returning\n");
658     return WINED3D_OK;
659 }
660
661 static const struct wined3d_swapchain_ops swapchain_gl_ops =
662 {
663     swapchain_gl_present,
664 };
665
666 /* Helper function that blits the front buffer contents to the target window. */
667 void x11_copy_to_screen(const struct wined3d_swapchain *swapchain, const RECT *rect)
668 {
669     const struct wined3d_surface *front;
670     POINT offset = {0, 0};
671     HDC src_dc, dst_dc;
672     RECT draw_rect;
673     HWND window;
674
675     TRACE("swapchain %p, rect %s.\n", swapchain, wine_dbgstr_rect(rect));
676
677     front = swapchain->front_buffer;
678     if (!(front->resource.usage & WINED3DUSAGE_RENDERTARGET))
679         return;
680
681     if (front->flags & SFLAG_LOCKED)
682         ERR("Trying to blit a mapped surface.\n");
683
684     TRACE("Copying surface %p to screen.\n", front);
685
686     src_dc = front->hDC;
687     window = swapchain->win_handle;
688     dst_dc = GetDCEx(window, 0, DCX_CLIPSIBLINGS | DCX_CACHE);
689
690     /* Front buffer coordinates are screen coordinates. Map them to the
691      * destination window if not fullscreened. */
692     if (swapchain->desc.windowed)
693         ClientToScreen(window, &offset);
694
695     TRACE("offset %s.\n", wine_dbgstr_point(&offset));
696
697     draw_rect.left = 0;
698     draw_rect.right = front->resource.width;
699     draw_rect.top = 0;
700     draw_rect.bottom = front->resource.height;
701
702     if (rect)
703         IntersectRect(&draw_rect, &draw_rect, rect);
704
705     BitBlt(dst_dc, draw_rect.left - offset.x, draw_rect.top - offset.y,
706             draw_rect.right - draw_rect.left, draw_rect.bottom - draw_rect.top,
707             src_dc, draw_rect.left, draw_rect.top, SRCCOPY);
708     ReleaseDC(window, dst_dc);
709 }
710
711 static HRESULT swapchain_gdi_present(struct wined3d_swapchain *swapchain, const RECT *src_rect_in,
712         const RECT *dst_rect_in, const RGNDATA *dirty_region, DWORD flags)
713 {
714     struct wined3d_surface *front, *back;
715
716     if (!swapchain->back_buffers)
717     {
718         WARN("Swapchain doesn't have a backbuffer, returning WINED3DERR_INVALIDCALL\n");
719         return WINED3DERR_INVALIDCALL;
720     }
721     front = swapchain->front_buffer;
722     back = swapchain->back_buffers[0];
723
724     /* Flip the DC. */
725     {
726         HDC tmp;
727         tmp = front->hDC;
728         front->hDC = back->hDC;
729         back->hDC = tmp;
730     }
731
732     /* Flip the DIBsection. */
733     {
734         HBITMAP tmp;
735         tmp = front->dib.DIBsection;
736         front->dib.DIBsection = back->dib.DIBsection;
737         back->dib.DIBsection = tmp;
738     }
739
740     /* Flip the surface data. */
741     {
742         void *tmp;
743
744         tmp = front->dib.bitmap_data;
745         front->dib.bitmap_data = back->dib.bitmap_data;
746         back->dib.bitmap_data = tmp;
747
748         tmp = front->resource.allocatedMemory;
749         front->resource.allocatedMemory = back->resource.allocatedMemory;
750         back->resource.allocatedMemory = tmp;
751
752         if (front->resource.heapMemory)
753             ERR("GDI Surface %p has heap memory allocated.\n", front);
754
755         if (back->resource.heapMemory)
756             ERR("GDI Surface %p has heap memory allocated.\n", back);
757     }
758
759     /* FPS support */
760     if (TRACE_ON(fps))
761     {
762         static LONG prev_time, frames;
763         DWORD time = GetTickCount();
764
765         ++frames;
766
767         /* every 1.5 seconds */
768         if (time - prev_time > 1500)
769         {
770             TRACE_(fps)("@ approx %.2ffps\n", 1000.0 * frames / (time - prev_time));
771             prev_time = time;
772             frames = 0;
773         }
774     }
775
776     x11_copy_to_screen(swapchain, NULL);
777
778     return WINED3D_OK;
779 }
780
781 static const struct wined3d_swapchain_ops swapchain_gdi_ops =
782 {
783     swapchain_gdi_present,
784 };
785
786 void swapchain_update_render_to_fbo(struct wined3d_swapchain *swapchain)
787 {
788     RECT client_rect;
789
790     if (wined3d_settings.offscreen_rendering_mode != ORM_FBO)
791         return;
792
793     if (!swapchain->desc.backbuffer_count)
794     {
795         TRACE("Single buffered rendering.\n");
796         swapchain->render_to_fbo = FALSE;
797         return;
798     }
799
800     GetClientRect(swapchain->win_handle, &client_rect);
801
802     TRACE("Backbuffer %ux%u, window %ux%u.\n",
803             swapchain->desc.backbuffer_width,
804             swapchain->desc.backbuffer_height,
805             client_rect.right, client_rect.bottom);
806     TRACE("Multisample type %#x, quality %#x.\n",
807             swapchain->desc.multisample_type,
808             swapchain->desc.multisample_quality);
809
810     if (!wined3d_settings.always_offscreen && !swapchain->desc.multisample_type
811             && swapchain->desc.backbuffer_width == client_rect.right
812             && swapchain->desc.backbuffer_height == client_rect.bottom)
813     {
814         TRACE("Backbuffer dimensions match window dimensions, rendering onscreen.\n");
815         swapchain->render_to_fbo = FALSE;
816         return;
817     }
818
819     TRACE("Rendering to FBO.\n");
820     swapchain->render_to_fbo = TRUE;
821 }
822
823 /* Do not call while under the GL lock. */
824 static HRESULT swapchain_init(struct wined3d_swapchain *swapchain, WINED3DSURFTYPE surface_type,
825         struct wined3d_device *device, struct wined3d_swapchain_desc *desc,
826         void *parent, const struct wined3d_parent_ops *parent_ops)
827 {
828     const struct wined3d_adapter *adapter = device->adapter;
829     const struct wined3d_format *format;
830     struct wined3d_display_mode mode;
831     BOOL displaymode_set = FALSE;
832     RECT client_rect;
833     HWND window;
834     HRESULT hr;
835     UINT i;
836
837     if (desc->backbuffer_count > WINED3DPRESENT_BACK_BUFFER_MAX)
838     {
839         FIXME("The application requested %u back buffers, this is not supported.\n",
840                 desc->backbuffer_count);
841         return WINED3DERR_INVALIDCALL;
842     }
843
844     if (desc->backbuffer_count > 1)
845     {
846         FIXME("The application requested more than one back buffer, this is not properly supported.\n"
847                 "Please configure the application to use double buffering (1 back buffer) if possible.\n");
848     }
849
850     switch (surface_type)
851     {
852         case SURFACE_GDI:
853             swapchain->swapchain_ops = &swapchain_gdi_ops;
854             break;
855
856         case SURFACE_OPENGL:
857             swapchain->swapchain_ops = &swapchain_gl_ops;
858             break;
859
860         default:
861             ERR("Invalid surface type %#x.\n", surface_type);
862             return WINED3DERR_INVALIDCALL;
863     }
864
865     window = desc->device_window ? desc->device_window : device->create_parms.focus_window;
866
867     swapchain->device = device;
868     swapchain->parent = parent;
869     swapchain->parent_ops = parent_ops;
870     swapchain->ref = 1;
871     swapchain->win_handle = window;
872     swapchain->device_window = window;
873
874     wined3d_get_adapter_display_mode(device->wined3d, adapter->ordinal, &mode);
875     swapchain->orig_width = mode.width;
876     swapchain->orig_height = mode.height;
877     swapchain->orig_fmt = mode.format_id;
878     format = wined3d_get_format(&adapter->gl_info, mode.format_id);
879
880     GetClientRect(window, &client_rect);
881     if (desc->windowed
882             && (!desc->backbuffer_width || !desc->backbuffer_height
883             || desc->backbuffer_format == WINED3DFMT_UNKNOWN))
884     {
885
886         if (!desc->backbuffer_width)
887         {
888             desc->backbuffer_width = client_rect.right;
889             TRACE("Updating width to %u.\n", desc->backbuffer_width);
890         }
891
892         if (!desc->backbuffer_height)
893         {
894             desc->backbuffer_height = client_rect.bottom;
895             TRACE("Updating height to %u.\n", desc->backbuffer_height);
896         }
897
898         if (desc->backbuffer_format == WINED3DFMT_UNKNOWN)
899         {
900             desc->backbuffer_format = swapchain->orig_fmt;
901             TRACE("Updating format to %s.\n", debug_d3dformat(swapchain->orig_fmt));
902         }
903     }
904     swapchain->desc = *desc;
905     swapchain_update_render_to_fbo(swapchain);
906
907     TRACE("Creating front buffer.\n");
908     hr = device->device_parent->ops->create_rendertarget(device->device_parent, parent,
909             swapchain->desc.backbuffer_width, swapchain->desc.backbuffer_height,
910             swapchain->desc.backbuffer_format, swapchain->desc.multisample_type,
911             swapchain->desc.multisample_quality, TRUE /* Lockable */,
912             &swapchain->front_buffer);
913     if (FAILED(hr))
914     {
915         WARN("Failed to create front buffer, hr %#x.\n", hr);
916         goto err;
917     }
918
919     surface_set_container(swapchain->front_buffer, WINED3D_CONTAINER_SWAPCHAIN, swapchain);
920     if (surface_type == SURFACE_OPENGL)
921     {
922         surface_modify_location(swapchain->front_buffer, SFLAG_INDRAWABLE, TRUE);
923     }
924
925     /* MSDN says we're only allowed a single fullscreen swapchain per device,
926      * so we should really check to see if there is a fullscreen swapchain
927      * already. Does a single head count as full screen? */
928
929     if (!desc->windowed)
930     {
931         struct wined3d_display_mode mode;
932
933         /* Change the display settings */
934         mode.width = desc->backbuffer_width;
935         mode.height = desc->backbuffer_height;
936         mode.format_id = desc->backbuffer_format;
937         mode.refresh_rate = desc->refresh_rate;
938
939         hr = wined3d_device_set_display_mode(device, 0, &mode);
940         if (FAILED(hr))
941         {
942             WARN("Failed to set display mode, hr %#x.\n", hr);
943             goto err;
944         }
945         displaymode_set = TRUE;
946     }
947
948     if (surface_type == SURFACE_OPENGL)
949     {
950         static const enum wined3d_format_id formats[] =
951         {
952             WINED3DFMT_D24_UNORM_S8_UINT,
953             WINED3DFMT_D32_UNORM,
954             WINED3DFMT_R24_UNORM_X8_TYPELESS,
955             WINED3DFMT_D16_UNORM,
956             WINED3DFMT_S1_UINT_D15_UNORM
957         };
958
959         const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
960
961         swapchain->context = HeapAlloc(GetProcessHeap(), 0, sizeof(*swapchain->context));
962         if (!swapchain->context)
963         {
964             ERR("Failed to create the context array.\n");
965             hr = E_OUTOFMEMORY;
966             goto err;
967         }
968         swapchain->num_contexts = 1;
969
970         /* In WGL both color, depth and stencil are features of a pixel format. In case of D3D they are separate.
971          * You are able to add a depth + stencil surface at a later stage when you need it.
972          * In order to support this properly in WineD3D we need the ability to recreate the opengl context and
973          * drawable when this is required. This is very tricky as we need to reapply ALL opengl states for the new
974          * context, need torecreate shaders, textures and other resources.
975          *
976          * The context manager already takes care of the state problem and for the other tasks code from Reset
977          * can be used. These changes are way to risky during the 1.0 code freeze which is taking place right now.
978          * Likely a lot of other new bugs will be exposed. For that reason request a depth stencil surface all the
979          * time. It can cause a slight performance hit but fixes a lot of regressions. A fixme reminds of that this
980          * issue needs to be fixed. */
981         for (i = 0; i < (sizeof(formats) / sizeof(*formats)); i++)
982         {
983             swapchain->ds_format = wined3d_get_format(gl_info, formats[i]);
984             swapchain->context[0] = context_create(swapchain, swapchain->front_buffer, swapchain->ds_format);
985             if (swapchain->context[0]) break;
986             TRACE("Depth stencil format %s is not supported, trying next format\n",
987                   debug_d3dformat(formats[i]));
988         }
989
990         if (!swapchain->context[0])
991         {
992             WARN("Failed to create context.\n");
993             hr = WINED3DERR_NOTAVAILABLE;
994             goto err;
995         }
996
997         if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
998                 && (!desc->enable_auto_depth_stencil
999                 || swapchain->desc.auto_depth_stencil_format != swapchain->ds_format->id))
1000         {
1001             FIXME("Add OpenGL context recreation support to context_validate_onscreen_formats\n");
1002         }
1003         context_release(swapchain->context[0]);
1004     }
1005
1006     if (swapchain->desc.backbuffer_count > 0)
1007     {
1008         swapchain->back_buffers = HeapAlloc(GetProcessHeap(), 0,
1009                 sizeof(*swapchain->back_buffers) * swapchain->desc.backbuffer_count);
1010         if (!swapchain->back_buffers)
1011         {
1012             ERR("Failed to allocate backbuffer array memory.\n");
1013             hr = E_OUTOFMEMORY;
1014             goto err;
1015         }
1016
1017         for (i = 0; i < swapchain->desc.backbuffer_count; ++i)
1018         {
1019             TRACE("Creating back buffer %u.\n", i);
1020             hr = device->device_parent->ops->create_rendertarget(device->device_parent, parent,
1021                     swapchain->desc.backbuffer_width, swapchain->desc.backbuffer_height,
1022                     swapchain->desc.backbuffer_format, swapchain->desc.multisample_type,
1023                     swapchain->desc.multisample_quality, TRUE /* Lockable */,
1024                     &swapchain->back_buffers[i]);
1025             if (FAILED(hr))
1026             {
1027                 WARN("Failed to create back buffer %u, hr %#x.\n", i, hr);
1028                 goto err;
1029             }
1030
1031             surface_set_container(swapchain->back_buffers[i], WINED3D_CONTAINER_SWAPCHAIN, swapchain);
1032         }
1033     }
1034
1035     /* Swapchains share the depth/stencil buffer, so only create a single depthstencil surface. */
1036     if (desc->enable_auto_depth_stencil && surface_type == SURFACE_OPENGL)
1037     {
1038         TRACE("Creating depth/stencil buffer.\n");
1039         if (!device->auto_depth_stencil)
1040         {
1041             hr = device->device_parent->ops->create_depth_stencil(device->device_parent,
1042                     swapchain->desc.backbuffer_width, swapchain->desc.backbuffer_height,
1043                     swapchain->desc.auto_depth_stencil_format, swapchain->desc.multisample_type,
1044                     swapchain->desc.multisample_quality, FALSE /* FIXME: Discard */,
1045                     &device->auto_depth_stencil);
1046             if (FAILED(hr))
1047             {
1048                 WARN("Failed to create the auto depth stencil, hr %#x.\n", hr);
1049                 goto err;
1050             }
1051
1052             surface_set_container(device->auto_depth_stencil, WINED3D_CONTAINER_NONE, NULL);
1053         }
1054     }
1055
1056     wined3d_swapchain_get_gamma_ramp(swapchain, &swapchain->orig_gamma);
1057
1058     return WINED3D_OK;
1059
1060 err:
1061     if (displaymode_set)
1062     {
1063         DEVMODEW devmode;
1064
1065         ClipCursor(NULL);
1066
1067         /* Change the display settings */
1068         memset(&devmode, 0, sizeof(devmode));
1069         devmode.dmSize = sizeof(devmode);
1070         devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
1071         devmode.dmBitsPerPel = format->byte_count * CHAR_BIT;
1072         devmode.dmPelsWidth = swapchain->orig_width;
1073         devmode.dmPelsHeight = swapchain->orig_height;
1074         ChangeDisplaySettingsExW(adapter->DeviceName, &devmode, NULL, CDS_FULLSCREEN, NULL);
1075     }
1076
1077     if (swapchain->back_buffers)
1078     {
1079         for (i = 0; i < swapchain->desc.backbuffer_count; ++i)
1080         {
1081             if (swapchain->back_buffers[i])
1082             {
1083                 surface_set_container(swapchain->back_buffers[i], WINED3D_CONTAINER_NONE, NULL);
1084                 wined3d_surface_decref(swapchain->back_buffers[i]);
1085             }
1086         }
1087         HeapFree(GetProcessHeap(), 0, swapchain->back_buffers);
1088     }
1089
1090     if (swapchain->context)
1091     {
1092         if (swapchain->context[0])
1093         {
1094             context_release(swapchain->context[0]);
1095             context_destroy(device, swapchain->context[0]);
1096             swapchain->num_contexts = 0;
1097         }
1098         HeapFree(GetProcessHeap(), 0, swapchain->context);
1099     }
1100
1101     if (swapchain->front_buffer)
1102     {
1103         surface_set_container(swapchain->front_buffer, WINED3D_CONTAINER_NONE, NULL);
1104         wined3d_surface_decref(swapchain->front_buffer);
1105     }
1106
1107     return hr;
1108 }
1109
1110 /* Do not call while under the GL lock. */
1111 HRESULT CDECL wined3d_swapchain_create(struct wined3d_device *device,
1112         struct wined3d_swapchain_desc *desc, WINED3DSURFTYPE surface_type,
1113         void *parent, const struct wined3d_parent_ops *parent_ops,
1114         struct wined3d_swapchain **swapchain)
1115 {
1116     struct wined3d_swapchain *object;
1117     HRESULT hr;
1118
1119     TRACE("device %p, desc %p, swapchain %p, parent %p, surface_type %#x.\n",
1120             device, desc, swapchain, parent, surface_type);
1121
1122     object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object));
1123     if (!object)
1124     {
1125         ERR("Failed to allocate swapchain memory.\n");
1126         return E_OUTOFMEMORY;
1127     }
1128
1129     hr = swapchain_init(object, surface_type, device, desc, parent, parent_ops);
1130     if (FAILED(hr))
1131     {
1132         WARN("Failed to initialize swapchain, hr %#x.\n", hr);
1133         HeapFree(GetProcessHeap(), 0, object);
1134         return hr;
1135     }
1136
1137     TRACE("Created swapchain %p.\n", object);
1138     *swapchain = object;
1139
1140     return WINED3D_OK;
1141 }
1142
1143 /* Do not call while under the GL lock. */
1144 static struct wined3d_context *swapchain_create_context(struct wined3d_swapchain *swapchain)
1145 {
1146     struct wined3d_context **newArray;
1147     struct wined3d_context *ctx;
1148
1149     TRACE("Creating a new context for swapchain %p, thread %u.\n", swapchain, GetCurrentThreadId());
1150
1151     if (!(ctx = context_create(swapchain, swapchain->front_buffer, swapchain->ds_format)))
1152     {
1153         ERR("Failed to create a new context for the swapchain\n");
1154         return NULL;
1155     }
1156     context_release(ctx);
1157
1158     newArray = HeapAlloc(GetProcessHeap(), 0, sizeof(*newArray) * (swapchain->num_contexts + 1));
1159     if(!newArray) {
1160         ERR("Out of memory when trying to allocate a new context array\n");
1161         context_destroy(swapchain->device, ctx);
1162         return NULL;
1163     }
1164     memcpy(newArray, swapchain->context, sizeof(*newArray) * swapchain->num_contexts);
1165     HeapFree(GetProcessHeap(), 0, swapchain->context);
1166     newArray[swapchain->num_contexts] = ctx;
1167     swapchain->context = newArray;
1168     swapchain->num_contexts++;
1169
1170     TRACE("Returning context %p\n", ctx);
1171     return ctx;
1172 }
1173
1174 void swapchain_destroy_contexts(struct wined3d_swapchain *swapchain)
1175 {
1176     unsigned int i;
1177
1178     for (i = 0; i < swapchain->num_contexts; ++i)
1179     {
1180         context_destroy(swapchain->device, swapchain->context[i]);
1181     }
1182     swapchain->num_contexts = 0;
1183 }
1184
1185 struct wined3d_context *swapchain_get_context(struct wined3d_swapchain *swapchain)
1186 {
1187     DWORD tid = GetCurrentThreadId();
1188     unsigned int i;
1189
1190     for (i = 0; i < swapchain->num_contexts; ++i)
1191     {
1192         if (swapchain->context[i]->tid == tid)
1193             return swapchain->context[i];
1194     }
1195
1196     /* Create a new context for the thread */
1197     return swapchain_create_context(swapchain);
1198 }
1199
1200 void get_drawable_size_swapchain(const struct wined3d_context *context, UINT *width, UINT *height)
1201 {
1202     /* The drawable size of an onscreen drawable is the surface size.
1203      * (Actually: The window size, but the surface is created in window size) */
1204     *width = context->current_rt->resource.width;
1205     *height = context->current_rt->resource.height;
1206 }
1207
1208 HDC swapchain_get_backup_dc(struct wined3d_swapchain *swapchain)
1209 {
1210     if (!swapchain->backup_dc)
1211     {
1212         TRACE("Creating the backup window for swapchain %p.\n", swapchain);
1213
1214         if (!(swapchain->backup_wnd = CreateWindowA(WINED3D_OPENGL_WINDOW_CLASS_NAME, "WineD3D fake window",
1215                 WS_OVERLAPPEDWINDOW, 10, 10, 10, 10, NULL, NULL, NULL, NULL)))
1216         {
1217             ERR("Failed to create a window.\n");
1218             return NULL;
1219         }
1220
1221         if (!(swapchain->backup_dc = GetDC(swapchain->backup_wnd)))
1222         {
1223             ERR("Failed to get a DC.\n");
1224             DestroyWindow(swapchain->backup_wnd);
1225             swapchain->backup_wnd = NULL;
1226             return NULL;
1227         }
1228     }
1229
1230     return swapchain->backup_dc;
1231 }
1232
1233 void swapchain_update_draw_bindings(struct wined3d_swapchain *swapchain)
1234 {
1235     UINT i;
1236
1237     surface_update_draw_binding(swapchain->front_buffer);
1238
1239     for (i = 0; i < swapchain->desc.backbuffer_count; ++i)
1240     {
1241         surface_update_draw_binding(swapchain->back_buffers[i]);
1242     }
1243 }