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