wined3d: Rename GlPixelFormatDesc to wined3d_format_desc.
[wine] / dlls / wined3d / swapchain.c
1 /*
2  *IDirect3DSwapChain9 implementation
3  *
4  *Copyright 2002-2003 Jason Edmeades
5  *Copyright 2002-2003 Raphael Junqueira
6  *Copyright 2005 Oliver Stieber
7  *Copyright 2007-2008 Stefan Dösinger for CodeWeavers
8  *
9  *This library is free software; you can redistribute it and/or
10  *modify it under the terms of the GNU Lesser General Public
11  *License as published by the Free Software Foundation; either
12  *version 2.1 of the License, or (at your option) any later version.
13  *
14  *This library is distributed in the hope that it will be useful,
15  *but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *Lesser General Public License for more details.
18  *
19  *You should have received a copy of the GNU Lesser General Public
20  *License along with this library; if not, write to the Free Software
21  *Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include "config.h"
25 #include "wined3d_private.h"
26
27
28 /*TODO: some of the additional parameters may be required to
29     set the gamma ramp (for some weird reason microsoft have left swap gammaramp in device
30     but it operates on a swapchain, it may be a good idea to move it to IWineD3DSwapChain for IWineD3D)*/
31
32
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
34 WINE_DECLARE_DEBUG_CHANNEL(fps);
35
36 #define GLINFO_LOCATION This->device->adapter->gl_info
37
38 /*IWineD3DSwapChain parts follow: */
39 static void WINAPI IWineD3DSwapChainImpl_Destroy(IWineD3DSwapChain *iface)
40 {
41     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
42     WINED3DDISPLAYMODE mode;
43     unsigned int i;
44
45     TRACE("Destroying swapchain %p\n", iface);
46
47     IWineD3DSwapChain_SetGammaRamp(iface, 0, &This->orig_gamma);
48
49     /* Release the swapchain's draw buffers. Make sure This->backBuffer[0] is
50      * the last buffer to be destroyed, FindContext() depends on that. */
51     if (This->frontBuffer)
52     {
53         IWineD3DSurface_SetContainer(This->frontBuffer, 0);
54         if (IWineD3DSurface_Release(This->frontBuffer))
55         {
56             WARN("(%p) Something's still holding the front buffer (%p).\n",
57                     This, This->frontBuffer);
58         }
59         This->frontBuffer = NULL;
60     }
61
62     if (This->backBuffer)
63     {
64         UINT i = This->presentParms.BackBufferCount;
65
66         while (i--)
67         {
68             IWineD3DSurface_SetContainer(This->backBuffer[i], 0);
69             if (IWineD3DSurface_Release(This->backBuffer[i]))
70                 WARN("(%p) Something's still holding back buffer %u (%p).\n",
71                         This, i, This->backBuffer[i]);
72         }
73         HeapFree(GetProcessHeap(), 0, This->backBuffer);
74         This->backBuffer = NULL;
75     }
76
77     for (i = 0; i < This->num_contexts; ++i)
78     {
79         context_destroy(This->device, This->context[i]);
80     }
81     /* Restore the screen resolution if we rendered in fullscreen
82      * This will restore the screen resolution to what it was before creating the swapchain. In case of d3d8 and d3d9
83      * this will be the original desktop resolution. In case of d3d7 this will be a NOP because ddraw sets the resolution
84      * before starting up Direct3D, thus orig_width and orig_height will be equal to the modes in the presentation params
85      */
86     if(This->presentParms.Windowed == FALSE && This->presentParms.AutoRestoreDisplayMode) {
87         mode.Width = This->orig_width;
88         mode.Height = This->orig_height;
89         mode.RefreshRate = 0;
90         mode.Format = This->orig_fmt;
91         IWineD3DDevice_SetDisplayMode((IWineD3DDevice *)This->device, 0, &mode);
92     }
93
94     HeapFree(GetProcessHeap(), 0, This->context);
95     HeapFree(GetProcessHeap(), 0, This);
96 }
97
98 /* A GL context is provided by the caller */
99 static void swapchain_blit(IWineD3DSwapChainImpl *This, struct wined3d_context *context,
100         const RECT *src_rect, const RECT *dst_rect)
101 {
102     IWineD3DDeviceImpl *device = This->device;
103     IWineD3DSurfaceImpl *backbuffer = ((IWineD3DSurfaceImpl *) This->backBuffer[0]);
104     UINT src_w = src_rect->right - src_rect->left;
105     UINT src_h = src_rect->bottom - src_rect->top;
106     GLenum gl_filter;
107     const struct wined3d_gl_info *gl_info = context->gl_info;
108
109     TRACE("swapchain %p, context %p, src_rect %s, dst_rect %s.\n",
110             This, context, wine_dbgstr_rect(src_rect), wine_dbgstr_rect(dst_rect));
111
112     if (src_w == dst_rect->right - dst_rect->left && src_h == dst_rect->bottom - dst_rect->top)
113         gl_filter = GL_NEAREST;
114     else
115         gl_filter = GL_LINEAR;
116
117     if (gl_info->fbo_ops.glBlitFramebuffer && is_identity_fixup(backbuffer->resource.format_desc->color_fixup))
118     {
119         ENTER_GL();
120         context_bind_fbo(context, GL_READ_FRAMEBUFFER, &context->src_fbo);
121         context_attach_surface_fbo(context, GL_READ_FRAMEBUFFER, 0, This->backBuffer[0]);
122         context_attach_depth_stencil_fbo(context, GL_READ_FRAMEBUFFER, NULL, FALSE);
123
124         context_bind_fbo(context, GL_DRAW_FRAMEBUFFER, NULL);
125         context_set_draw_buffer(context, GL_BACK);
126
127         glDisable(GL_SCISSOR_TEST);
128         IWineD3DDeviceImpl_MarkStateDirty(This->device, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
129
130         /* Note that the texture is upside down */
131         gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
132                                            dst_rect->left, dst_rect->bottom, dst_rect->right, dst_rect->top,
133                                            GL_COLOR_BUFFER_BIT, gl_filter);
134         checkGLcall("Swapchain present blit(EXT_framebuffer_blit)\n");
135         LEAVE_GL();
136     }
137     else
138     {
139         struct wined3d_context *context2;
140         float tex_left = src_rect->left;
141         float tex_top = src_rect->top;
142         float tex_right = src_rect->right;
143         float tex_bottom = src_rect->bottom;
144
145         context2 = context_acquire(This->device, This->backBuffer[0], CTXUSAGE_BLIT);
146
147         if(backbuffer->Flags & SFLAG_NORMCOORD)
148         {
149             tex_left /= src_w;
150             tex_right /= src_w;
151             tex_top /= src_h;
152             tex_bottom /= src_h;
153         }
154
155         if (is_complex_fixup(backbuffer->resource.format_desc->color_fixup))
156             gl_filter = GL_NEAREST;
157
158         ENTER_GL();
159         context_bind_fbo(context2, GL_DRAW_FRAMEBUFFER, NULL);
160
161         /* Set up the texture. The surface is not in a IWineD3D*Texture container,
162          * so there are no d3d texture settings to dirtify
163          */
164         device->blitter->set_shader((IWineD3DDevice *) device, backbuffer->resource.format_desc,
165                                     backbuffer->texture_target, backbuffer->pow2Width,
166                                     backbuffer->pow2Height);
167         glTexParameteri(backbuffer->texture_target, GL_TEXTURE_MIN_FILTER, gl_filter);
168         glTexParameteri(backbuffer->texture_target, GL_TEXTURE_MAG_FILTER, gl_filter);
169
170         context_set_draw_buffer(context, GL_BACK);
171
172         /* Set the viewport to the destination rectandle, disable any projection
173          * transformation set up by CTXUSAGE_BLIT, and draw a (-1,-1)-(1,1) quad.
174          *
175          * Back up viewport and matrix to avoid breaking last_was_blit
176          *
177          * Note that CTXUSAGE_BLIT set up viewport and ortho to match the surface
178          * size - we want the GL drawable(=window) size.
179          */
180         glPushAttrib(GL_VIEWPORT_BIT);
181         glViewport(dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom);
182         glMatrixMode(GL_PROJECTION);
183         glPushMatrix();
184         glLoadIdentity();
185
186         glBegin(GL_QUADS);
187             /* bottom left */
188             glTexCoord2f(tex_left, tex_bottom);
189             glVertex2i(-1, -1);
190
191             /* top left */
192             glTexCoord2f(tex_left, tex_top);
193             glVertex2i(-1, 1);
194
195             /* top right */
196             glTexCoord2f(tex_right, tex_top);
197             glVertex2i(1, 1);
198
199             /* bottom right */
200             glTexCoord2f(tex_right, tex_bottom);
201             glVertex2i(1, -1);
202         glEnd();
203
204         glPopMatrix();
205         glPopAttrib();
206
207         device->blitter->unset_shader((IWineD3DDevice *) device);
208         checkGLcall("Swapchain present blit(manual)\n");
209         LEAVE_GL();
210
211         context_release(context2);
212     }
213 }
214
215 static HRESULT WINAPI IWineD3DSwapChainImpl_Present(IWineD3DSwapChain *iface, CONST RECT *pSourceRect, CONST RECT *pDestRect, HWND hDestWindowOverride, CONST RGNDATA *pDirtyRegion, DWORD dwFlags) {
216     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
217     struct wined3d_context *context;
218     RECT src_rect, dst_rect;
219     BOOL render_to_fbo;
220     unsigned int sync;
221     int retval;
222
223     IWineD3DSwapChain_SetDestWindowOverride(iface, hDestWindowOverride);
224
225     context = context_acquire(This->device, This->backBuffer[0], CTXUSAGE_RESOURCELOAD);
226     if (!context->valid)
227     {
228         context_release(context);
229         WARN("Invalid context, skipping present.\n");
230         return WINED3D_OK;
231     }
232
233     /* Render the cursor onto the back buffer, using our nifty directdraw blitting code :-) */
234     if (This->device->bCursorVisible && This->device->cursorTexture)
235     {
236         IWineD3DSurfaceImpl cursor;
237         RECT destRect =
238         {
239             This->device->xScreenSpace - This->device->xHotSpot,
240             This->device->yScreenSpace - This->device->yHotSpot,
241             This->device->xScreenSpace + This->device->cursorWidth - This->device->xHotSpot,
242             This->device->yScreenSpace + This->device->cursorHeight - This->device->yHotSpot,
243         };
244         TRACE("Rendering the cursor. Creating fake surface at %p\n", &cursor);
245         /* Build a fake surface to call the Blitting code. It is not possible to use the interface passed by
246          * the application because we are only supposed to copy the information out. Using a fake surface
247          * allows to use the Blitting engine and avoid copying the whole texture -> render target blitting code.
248          */
249         memset(&cursor, 0, sizeof(cursor));
250         cursor.lpVtbl = &IWineD3DSurface_Vtbl;
251         cursor.resource.ref = 1;
252         cursor.resource.device = This->device;
253         cursor.resource.pool = WINED3DPOOL_SCRATCH;
254         cursor.resource.format_desc = getFormatDescEntry(WINED3DFMT_B8G8R8A8_UNORM, context->gl_info);
255         cursor.resource.resourceType = WINED3DRTYPE_SURFACE;
256         cursor.texture_name = This->device->cursorTexture;
257         cursor.texture_target = GL_TEXTURE_2D;
258         cursor.texture_level = 0;
259         cursor.currentDesc.Width = This->device->cursorWidth;
260         cursor.currentDesc.Height = This->device->cursorHeight;
261         cursor.glRect.left = 0;
262         cursor.glRect.top = 0;
263         cursor.glRect.right = cursor.currentDesc.Width;
264         cursor.glRect.bottom = cursor.currentDesc.Height;
265         /* The cursor must have pow2 sizes */
266         cursor.pow2Width = cursor.currentDesc.Width;
267         cursor.pow2Height = cursor.currentDesc.Height;
268         /* The surface is in the texture */
269         cursor.Flags |= SFLAG_INTEXTURE;
270         /* DDBLT_KEYSRC will cause BltOverride to enable the alpha test with GL_NOTEQUAL, 0.0,
271          * which is exactly what we want :-)
272          */
273         if (This->presentParms.Windowed) {
274             MapWindowPoints(NULL, This->win_handle, (LPPOINT)&destRect, 2);
275         }
276         IWineD3DSurface_Blt(This->backBuffer[0], &destRect, (IWineD3DSurface *)&cursor,
277                 NULL, WINEDDBLT_KEYSRC, NULL, WINED3DTEXF_POINT);
278     }
279
280     if (This->device->logo_surface)
281     {
282         /* Blit the logo into the upper left corner of the drawable. */
283         IWineD3DSurface_BltFast(This->backBuffer[0], 0, 0, This->device->logo_surface, NULL, WINEDDBLTFAST_SRCCOLORKEY);
284     }
285
286     TRACE("Presenting HDC %p.\n", context->hdc);
287
288     render_to_fbo = This->render_to_fbo;
289
290     if (pSourceRect)
291     {
292         src_rect = *pSourceRect;
293         if (!render_to_fbo && (src_rect.left || src_rect.top
294                 || src_rect.right != This->presentParms.BackBufferWidth
295                 || src_rect.bottom != This->presentParms.BackBufferHeight))
296         {
297             render_to_fbo = TRUE;
298         }
299     }
300     else
301     {
302         src_rect.left = 0;
303         src_rect.top = 0;
304         src_rect.right = This->presentParms.BackBufferWidth;
305         src_rect.bottom = This->presentParms.BackBufferHeight;
306     }
307
308     if (pDestRect) dst_rect = *pDestRect;
309     else GetClientRect(This->win_handle, &dst_rect);
310
311     if (!render_to_fbo && (dst_rect.left || dst_rect.top
312             || dst_rect.right != This->presentParms.BackBufferWidth
313             || dst_rect.bottom != This->presentParms.BackBufferHeight))
314     {
315         render_to_fbo = TRUE;
316     }
317
318     /* Rendering to a window of different size, presenting partial rectangles,
319      * or rendering to a different window needs help from FBO_blit or a textured
320      * draw. Render the swapchain to a FBO in the future.
321      *
322      * Note that FBO_blit from the backbuffer to the frontbuffer cannot solve
323      * all these issues - this fails if the window is smaller than the backbuffer.
324      */
325     if (!This->render_to_fbo && render_to_fbo && wined3d_settings.offscreen_rendering_mode == ORM_FBO)
326     {
327         IWineD3DSurface_LoadLocation(This->backBuffer[0], SFLAG_INTEXTURE, NULL);
328         IWineD3DSurface_ModifyLocation(This->backBuffer[0], SFLAG_INDRAWABLE, FALSE);
329         This->render_to_fbo = TRUE;
330
331         /* Force the context manager to update the render target configuration next draw. */
332         context->current_rt = NULL;
333     }
334
335     if(This->render_to_fbo)
336     {
337         /* This codepath should only be hit with the COPY swapeffect. Otherwise a backbuffer-
338          * window size mismatch is impossible(fullscreen) and src and dst rectangles are
339          * not allowed(they need the COPY swapeffect)
340          *
341          * The DISCARD swap effect is ok as well since any backbuffer content is allowed after
342          * the swap
343          */
344         if(This->presentParms.SwapEffect == WINED3DSWAPEFFECT_FLIP )
345         {
346             FIXME("Render-to-fbo with WINED3DSWAPEFFECT_FLIP\n");
347         }
348
349         swapchain_blit(This, context, &src_rect, &dst_rect);
350     }
351
352     if (This->num_contexts > 1) wglFinish();
353     SwapBuffers(context->hdc); /* TODO: cycle through the swapchain buffers */
354
355     TRACE("SwapBuffers called, Starting new frame\n");
356     /* FPS support */
357     if (TRACE_ON(fps))
358     {
359         DWORD time = GetTickCount();
360         This->frames++;
361         /* every 1.5 seconds */
362         if (time - This->prev_time > 1500) {
363             TRACE_(fps)("%p @ approx %.2ffps\n", This, 1000.0*This->frames/(time - This->prev_time));
364             This->prev_time = time;
365             This->frames = 0;
366         }
367     }
368
369 #if defined(FRAME_DEBUGGING)
370 {
371     if (GetFileAttributesA("C:\\D3DTRACE") != INVALID_FILE_ATTRIBUTES) {
372         if (!isOn) {
373             isOn = TRUE;
374             FIXME("Enabling D3D Trace\n");
375             __WINE_SET_DEBUGGING(__WINE_DBCL_TRACE, __wine_dbch_d3d, 1);
376 #if defined(SHOW_FRAME_MAKEUP)
377             FIXME("Singe Frame snapshots Starting\n");
378             isDumpingFrames = TRUE;
379             ENTER_GL();
380             glClear(GL_COLOR_BUFFER_BIT);
381             LEAVE_GL();
382 #endif
383
384 #if defined(SINGLE_FRAME_DEBUGGING)
385         } else {
386 #if defined(SHOW_FRAME_MAKEUP)
387             FIXME("Singe Frame snapshots Finishing\n");
388             isDumpingFrames = FALSE;
389 #endif
390             FIXME("Singe Frame trace complete\n");
391             DeleteFileA("C:\\D3DTRACE");
392             __WINE_SET_DEBUGGING(__WINE_DBCL_TRACE, __wine_dbch_d3d, 0);
393 #endif
394         }
395     } else {
396         if (isOn) {
397             isOn = FALSE;
398 #if defined(SHOW_FRAME_MAKEUP)
399             FIXME("Single Frame snapshots Finishing\n");
400             isDumpingFrames = FALSE;
401 #endif
402             FIXME("Disabling D3D Trace\n");
403             __WINE_SET_DEBUGGING(__WINE_DBCL_TRACE, __wine_dbch_d3d, 0);
404         }
405     }
406 }
407 #endif
408
409     /* This is disabled, but the code left in for debug purposes.
410      *
411      * Since we're allowed to modify the new back buffer on a D3DSWAPEFFECT_DISCARD flip,
412      * we can clear it with some ugly color to make bad drawing visible and ease debugging.
413      * The Debug runtime does the same on Windows. However, a few games do not redraw the
414      * screen properly, like Max Payne 2, which leaves a few pixels undefined.
415      *
416      * Tests show that the content of the back buffer after a discard flip is indeed not
417      * reliable, so no game can depend on the exact content. However, it resembles the
418      * old contents in some way, for example by showing fragments at other locations. In
419      * general, the color theme is still intact. So Max payne, which draws rather dark scenes
420      * gets a dark background image. If we clear it with a bright ugly color, the game's
421      * bug shows up much more than it does on Windows, and the players see single pixels
422      * with wrong colors.
423      * (The Max Payne bug has been confirmed on Windows with the debug runtime)
424      */
425     if (FALSE && This->presentParms.SwapEffect == WINED3DSWAPEFFECT_DISCARD) {
426         TRACE("Clearing the color buffer with cyan color\n");
427
428         IWineD3DDevice_Clear((IWineD3DDevice *)This->device, 0, NULL,
429                 WINED3DCLEAR_TARGET, 0xff00ffff, 1.0f, 0);
430     }
431
432     if(!This->render_to_fbo &&
433        ( ((IWineD3DSurfaceImpl *) This->frontBuffer)->Flags   & SFLAG_INSYSMEM ||
434          ((IWineD3DSurfaceImpl *) This->backBuffer[0])->Flags & SFLAG_INSYSMEM ) ) {
435         /* Both memory copies of the surfaces are ok, flip them around too instead of dirtifying
436          * Doesn't work with render_to_fbo because we're not flipping
437          */
438         IWineD3DSurfaceImpl *front = (IWineD3DSurfaceImpl *) This->frontBuffer;
439         IWineD3DSurfaceImpl *back = (IWineD3DSurfaceImpl *) This->backBuffer[0];
440
441         if(front->resource.size == back->resource.size) {
442             DWORD fbflags;
443             flip_surface(front, back);
444
445             /* Tell the front buffer surface that is has been modified. However,
446              * the other locations were preserved during that, so keep the flags.
447              * This serves to update the emulated overlay, if any
448              */
449             fbflags = front->Flags;
450             IWineD3DSurface_ModifyLocation(This->frontBuffer, SFLAG_INDRAWABLE, TRUE);
451             front->Flags = fbflags;
452         } else {
453             IWineD3DSurface_ModifyLocation((IWineD3DSurface *) front, SFLAG_INDRAWABLE, TRUE);
454             IWineD3DSurface_ModifyLocation((IWineD3DSurface *) back, SFLAG_INDRAWABLE, TRUE);
455         }
456     } else {
457         IWineD3DSurface_ModifyLocation(This->frontBuffer, SFLAG_INDRAWABLE, TRUE);
458         /* If the swapeffect is DISCARD, the back buffer is undefined. That means the SYSMEM
459          * and INTEXTURE copies can keep their old content if they have any defined content.
460          * If the swapeffect is COPY, the content remains the same. If it is FLIP however,
461          * the texture / sysmem copy needs to be reloaded from the drawable
462          */
463         if(This->presentParms.SwapEffect == WINED3DSWAPEFFECT_FLIP) {
464             IWineD3DSurface_ModifyLocation(This->backBuffer[0], SFLAG_INDRAWABLE, TRUE);
465         }
466     }
467
468     if (This->device->stencilBufferTarget)
469     {
470         if (This->presentParms.Flags & WINED3DPRESENTFLAG_DISCARD_DEPTHSTENCIL
471                 || ((IWineD3DSurfaceImpl *)This->device->stencilBufferTarget)->Flags & SFLAG_DISCARD)
472         {
473             surface_modify_ds_location(This->device->stencilBufferTarget, SFLAG_DS_DISCARDED);
474         }
475     }
476
477     if (This->presentParms.PresentationInterval != WINED3DPRESENT_INTERVAL_IMMEDIATE
478             && context->gl_info->supported[SGI_VIDEO_SYNC])
479     {
480         retval = GL_EXTCALL(glXGetVideoSyncSGI(&sync));
481         if(retval != 0) {
482             ERR("glXGetVideoSyncSGI failed(retval = %d\n", retval);
483         }
484
485         switch(This->presentParms.PresentationInterval) {
486             case WINED3DPRESENT_INTERVAL_DEFAULT:
487             case WINED3DPRESENT_INTERVAL_ONE:
488                 if(sync <= This->vSyncCounter) {
489                     retval = GL_EXTCALL(glXWaitVideoSyncSGI(1, 0, &This->vSyncCounter));
490                 } else {
491                     This->vSyncCounter = sync;
492                 }
493                 break;
494             case WINED3DPRESENT_INTERVAL_TWO:
495                 if(sync <= This->vSyncCounter + 1) {
496                     retval = GL_EXTCALL(glXWaitVideoSyncSGI(2, This->vSyncCounter & 0x1, &This->vSyncCounter));
497                 } else {
498                     This->vSyncCounter = sync;
499                 }
500                 break;
501             case WINED3DPRESENT_INTERVAL_THREE:
502                 if(sync <= This->vSyncCounter + 2) {
503                     retval = GL_EXTCALL(glXWaitVideoSyncSGI(3, This->vSyncCounter % 0x3, &This->vSyncCounter));
504                 } else {
505                     This->vSyncCounter = sync;
506                 }
507                 break;
508             case WINED3DPRESENT_INTERVAL_FOUR:
509                 if(sync <= This->vSyncCounter + 3) {
510                     retval = GL_EXTCALL(glXWaitVideoSyncSGI(4, This->vSyncCounter & 0x3, &This->vSyncCounter));
511                 } else {
512                     This->vSyncCounter = sync;
513                 }
514                 break;
515             default:
516                 FIXME("Unknown presentation interval %08x\n", This->presentParms.PresentationInterval);
517         }
518     }
519
520     context_release(context);
521
522     TRACE("returning\n");
523     return WINED3D_OK;
524 }
525
526 static HRESULT WINAPI IWineD3DSwapChainImpl_SetDestWindowOverride(IWineD3DSwapChain *iface, HWND window)
527 {
528     IWineD3DSwapChainImpl *swapchain = (IWineD3DSwapChainImpl *)iface;
529
530     if (!window) window = swapchain->device_window;
531     if (window == swapchain->win_handle) return WINED3D_OK;
532
533     TRACE("Setting swapchain %p window from %p to %p\n", swapchain, swapchain->win_handle, window);
534     swapchain->win_handle = window;
535
536     return WINED3D_OK;
537 }
538
539 static const IWineD3DSwapChainVtbl IWineD3DSwapChain_Vtbl =
540 {
541     /* IUnknown */
542     IWineD3DBaseSwapChainImpl_QueryInterface,
543     IWineD3DBaseSwapChainImpl_AddRef,
544     IWineD3DBaseSwapChainImpl_Release,
545     /* IWineD3DSwapChain */
546     IWineD3DBaseSwapChainImpl_GetParent,
547     IWineD3DSwapChainImpl_Destroy,
548     IWineD3DBaseSwapChainImpl_GetDevice,
549     IWineD3DSwapChainImpl_Present,
550     IWineD3DSwapChainImpl_SetDestWindowOverride,
551     IWineD3DBaseSwapChainImpl_GetFrontBufferData,
552     IWineD3DBaseSwapChainImpl_GetBackBuffer,
553     IWineD3DBaseSwapChainImpl_GetRasterStatus,
554     IWineD3DBaseSwapChainImpl_GetDisplayMode,
555     IWineD3DBaseSwapChainImpl_GetPresentParameters,
556     IWineD3DBaseSwapChainImpl_SetGammaRamp,
557     IWineD3DBaseSwapChainImpl_GetGammaRamp
558 };
559
560 static LONG fullscreen_style(LONG style)
561 {
562     /* Make sure the window is managed, otherwise we won't get keyboard input. */
563     style |= WS_POPUP | WS_SYSMENU;
564     style &= ~(WS_CAPTION | WS_THICKFRAME);
565
566     return style;
567 }
568
569 static LONG fullscreen_exstyle(LONG exstyle)
570 {
571     /* Filter out window decorations. */
572     exstyle &= ~(WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE);
573
574     return exstyle;
575 }
576
577 void swapchain_setup_fullscreen_window(IWineD3DSwapChainImpl *swapchain, UINT w, UINT h)
578 {
579     IWineD3DDeviceImpl *device = swapchain->device;
580     HWND window = swapchain->device_window;
581     BOOL filter_messages;
582     LONG style, exstyle;
583
584     TRACE("Setting up window %p for fullscreen mode.\n", window);
585
586     if (device->style || device->exStyle)
587     {
588         ERR("Changing the window style for window %p, but another style (%08x, %08x) is already stored.\n",
589                 window, device->style, device->exStyle);
590     }
591
592     device->style = GetWindowLongW(window, GWL_STYLE);
593     device->exStyle = GetWindowLongW(window, GWL_EXSTYLE);
594
595     style = fullscreen_style(device->style);
596     exstyle = fullscreen_exstyle(device->exStyle);
597
598     TRACE("Old style was %08x, %08x, setting to %08x, %08x.\n",
599             device->style, device->exStyle, style, exstyle);
600
601     filter_messages = device->filter_messages;
602     device->filter_messages = TRUE;
603
604     SetWindowLongW(window, GWL_STYLE, style);
605     SetWindowLongW(window, GWL_EXSTYLE, exstyle);
606     SetWindowPos(window, HWND_TOP, 0, 0, w, h, SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOACTIVATE);
607
608     device->filter_messages = filter_messages;
609 }
610
611 void swapchain_restore_fullscreen_window(IWineD3DSwapChainImpl *swapchain)
612 {
613     IWineD3DDeviceImpl *device = swapchain->device;
614     HWND window = swapchain->device_window;
615     BOOL filter_messages;
616     LONG style, exstyle;
617
618     if (!device->style && !device->exStyle) return;
619
620     TRACE("Restoring window style of window %p to %08x, %08x.\n",
621             window, device->style, device->exStyle);
622
623     style = GetWindowLongW(window, GWL_STYLE);
624     exstyle = GetWindowLongW(window, GWL_EXSTYLE);
625
626     filter_messages = device->filter_messages;
627     device->filter_messages = TRUE;
628
629     /* Only restore the style if the application didn't modify it during the
630      * fullscreen phase. Some applications change it before calling Reset()
631      * when switching between windowed and fullscreen modes (HL2), some
632      * depend on the original style (Eve Online). */
633     if (style == fullscreen_style(device->style) && exstyle == fullscreen_exstyle(device->exStyle))
634     {
635         SetWindowLongW(window, GWL_STYLE, device->style);
636         SetWindowLongW(window, GWL_EXSTYLE, device->exStyle);
637     }
638     SetWindowPos(window, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
639
640     device->filter_messages = filter_messages;
641
642     /* Delete the old values. */
643     device->style = 0;
644     device->exStyle = 0;
645 }
646
647
648 HRESULT swapchain_init(IWineD3DSwapChainImpl *swapchain, WINED3DSURFTYPE surface_type,
649         IWineD3DDeviceImpl *device, WINED3DPRESENT_PARAMETERS *present_parameters, IUnknown *parent)
650 {
651     const struct wined3d_adapter *adapter = device->adapter;
652     const struct wined3d_format_desc *format_desc;
653     BOOL displaymode_set = FALSE;
654     WINED3DDISPLAYMODE mode;
655     RECT client_rect;
656     HWND window;
657     HRESULT hr;
658     UINT i;
659
660     if (present_parameters->BackBufferCount > WINED3DPRESENT_BACK_BUFFER_MAX)
661     {
662         FIXME("The application requested %u back buffers, this is not supported.\n",
663                 present_parameters->BackBufferCount);
664         return WINED3DERR_INVALIDCALL;
665     }
666
667     if (present_parameters->BackBufferCount > 1)
668     {
669         FIXME("The application requested more than one back buffer, this is not properly supported.\n"
670                 "Please configure the application to use double buffering (1 back buffer) if possible.\n");
671     }
672
673     switch (surface_type)
674     {
675         case SURFACE_GDI:
676             swapchain->lpVtbl = &IWineGDISwapChain_Vtbl;
677             break;
678
679         case SURFACE_OPENGL:
680             swapchain->lpVtbl = &IWineD3DSwapChain_Vtbl;
681             break;
682
683         case SURFACE_UNKNOWN:
684             FIXME("Caller tried to create a SURFACE_UNKNOWN swapchain.\n");
685             return WINED3DERR_INVALIDCALL;
686     }
687
688     window = present_parameters->hDeviceWindow ? present_parameters->hDeviceWindow : device->createParms.hFocusWindow;
689
690     swapchain->device = device;
691     swapchain->parent = parent;
692     swapchain->ref = 1;
693     swapchain->win_handle = window;
694     swapchain->device_window = window;
695
696     if (!present_parameters->Windowed && window)
697     {
698         swapchain_setup_fullscreen_window(swapchain, present_parameters->BackBufferWidth,
699                 present_parameters->BackBufferHeight);
700     }
701
702     IWineD3D_GetAdapterDisplayMode(device->wined3d, adapter->ordinal, &mode);
703     swapchain->orig_width = mode.Width;
704     swapchain->orig_height = mode.Height;
705     swapchain->orig_fmt = mode.Format;
706     format_desc = getFormatDescEntry(mode.Format, &adapter->gl_info);
707
708     GetClientRect(window, &client_rect);
709     if (present_parameters->Windowed
710             && (!present_parameters->BackBufferWidth || !present_parameters->BackBufferHeight
711             || present_parameters->BackBufferFormat == WINED3DFMT_UNKNOWN))
712     {
713
714         if (!present_parameters->BackBufferWidth)
715         {
716             present_parameters->BackBufferWidth = client_rect.right;
717             TRACE("Updating width to %u.\n", present_parameters->BackBufferWidth);
718         }
719
720         if (!present_parameters->BackBufferHeight)
721         {
722             present_parameters->BackBufferHeight = client_rect.bottom;
723             TRACE("Updating height to %u.\n", present_parameters->BackBufferHeight);
724         }
725
726         if (present_parameters->BackBufferFormat == WINED3DFMT_UNKNOWN)
727         {
728             present_parameters->BackBufferFormat = swapchain->orig_fmt;
729             TRACE("Updating format to %s.\n", debug_d3dformat(swapchain->orig_fmt));
730         }
731     }
732     swapchain->presentParms = *present_parameters;
733
734     if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
735             && present_parameters->BackBufferCount
736             && (present_parameters->BackBufferWidth != client_rect.right
737             || present_parameters->BackBufferHeight != client_rect.bottom))
738     {
739         TRACE("Rendering to FBO. Backbuffer %ux%u, window %ux%u.\n",
740                 present_parameters->BackBufferWidth,
741                 present_parameters->BackBufferHeight,
742                 client_rect.right, client_rect.bottom);
743         swapchain->render_to_fbo = TRUE;
744     }
745
746     TRACE("Creating front buffer.\n");
747     hr = IWineD3DDeviceParent_CreateRenderTarget(device->device_parent, parent,
748             swapchain->presentParms.BackBufferWidth, swapchain->presentParms.BackBufferHeight,
749             swapchain->presentParms.BackBufferFormat, swapchain->presentParms.MultiSampleType,
750             swapchain->presentParms.MultiSampleQuality, TRUE /* Lockable */, &swapchain->frontBuffer);
751     if (FAILED(hr))
752     {
753         WARN("Failed to create front buffer, hr %#x.\n", hr);
754         goto err;
755     }
756
757     IWineD3DSurface_SetContainer(swapchain->frontBuffer, (IWineD3DBase *)swapchain);
758     ((IWineD3DSurfaceImpl *)swapchain->frontBuffer)->Flags |= SFLAG_SWAPCHAIN;
759     if (surface_type == SURFACE_OPENGL)
760     {
761         IWineD3DSurface_ModifyLocation(swapchain->frontBuffer, SFLAG_INDRAWABLE, TRUE);
762     }
763
764     /* MSDN says we're only allowed a single fullscreen swapchain per device,
765      * so we should really check to see if there is a fullscreen swapchain
766      * already. Does a single head count as full screen? */
767
768     if (!present_parameters->Windowed)
769     {
770         WINED3DDISPLAYMODE mode;
771
772         /* Change the display settings */
773         mode.Width = present_parameters->BackBufferWidth;
774         mode.Height = present_parameters->BackBufferHeight;
775         mode.Format = present_parameters->BackBufferFormat;
776         mode.RefreshRate = present_parameters->FullScreen_RefreshRateInHz;
777
778         hr = IWineD3DDevice_SetDisplayMode((IWineD3DDevice *)device, 0, &mode);
779         if (FAILED(hr))
780         {
781             WARN("Failed to set display mode, hr %#x.\n", hr);
782             goto err;
783         }
784         displaymode_set = TRUE;
785     }
786
787     swapchain->context = HeapAlloc(GetProcessHeap(), 0, sizeof(swapchain->context));
788     if (!swapchain->context)
789     {
790         ERR("Failed to create the context array.\n");
791         hr = E_OUTOFMEMORY;
792         goto err;
793     }
794     swapchain->num_contexts = 1;
795
796     if (surface_type == SURFACE_OPENGL)
797     {
798         if (!(swapchain->context[0] = context_create(swapchain, (IWineD3DSurfaceImpl *)swapchain->frontBuffer)))
799         {
800             WARN("Failed to create context.\n");
801             hr = WINED3DERR_NOTAVAILABLE;
802             goto err;
803         }
804         context_release(swapchain->context[0]);
805     }
806     else
807     {
808         swapchain->context[0] = NULL;
809     }
810
811     if (swapchain->presentParms.BackBufferCount > 0)
812     {
813         swapchain->backBuffer = HeapAlloc(GetProcessHeap(), 0,
814                 sizeof(*swapchain->backBuffer) * swapchain->presentParms.BackBufferCount);
815         if (!swapchain->backBuffer)
816         {
817             ERR("Failed to allocate backbuffer array memory.\n");
818             hr = E_OUTOFMEMORY;
819             goto err;
820         }
821
822         for (i = 0; i < swapchain->presentParms.BackBufferCount; ++i)
823         {
824             TRACE("Creating back buffer %u.\n", i);
825             hr = IWineD3DDeviceParent_CreateRenderTarget(device->device_parent, parent,
826                     swapchain->presentParms.BackBufferWidth, swapchain->presentParms.BackBufferHeight,
827                     swapchain->presentParms.BackBufferFormat, swapchain->presentParms.MultiSampleType,
828                     swapchain->presentParms.MultiSampleQuality, TRUE /* Lockable */, &swapchain->backBuffer[i]);
829             if (FAILED(hr))
830             {
831                 WARN("Failed to create back buffer %u, hr %#x.\n", i, hr);
832                 goto err;
833             }
834
835             IWineD3DSurface_SetContainer(swapchain->backBuffer[i], (IWineD3DBase *)swapchain);
836             ((IWineD3DSurfaceImpl *)swapchain->backBuffer[i])->Flags |= SFLAG_SWAPCHAIN;
837         }
838     }
839
840     /* Swapchains share the depth/stencil buffer, so only create a single depthstencil surface. */
841     if (present_parameters->EnableAutoDepthStencil && surface_type == SURFACE_OPENGL)
842     {
843         TRACE("Creating depth/stencil buffer.\n");
844         if (!device->auto_depth_stencil_buffer)
845         {
846             hr = IWineD3DDeviceParent_CreateDepthStencilSurface(device->device_parent, parent,
847                     swapchain->presentParms.BackBufferWidth, swapchain->presentParms.BackBufferHeight,
848                     swapchain->presentParms.AutoDepthStencilFormat, swapchain->presentParms.MultiSampleType,
849                     swapchain->presentParms.MultiSampleQuality, FALSE /* FIXME: Discard */,
850                     &device->auto_depth_stencil_buffer);
851             if (FAILED(hr))
852             {
853                 WARN("Failed to create the auto depth stencil, hr %#x.\n", hr);
854                 goto err;
855             }
856
857             IWineD3DSurface_SetContainer(device->auto_depth_stencil_buffer, NULL);
858         }
859     }
860
861     IWineD3DSwapChain_GetGammaRamp((IWineD3DSwapChain *)swapchain, &swapchain->orig_gamma);
862
863     return WINED3D_OK;
864
865 err:
866     if (displaymode_set)
867     {
868         DEVMODEW devmode;
869
870         ClipCursor(NULL);
871
872         /* Change the display settings */
873         memset(&devmode, 0, sizeof(devmode));
874         devmode.dmSize = sizeof(devmode);
875         devmode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
876         devmode.dmBitsPerPel = format_desc->byte_count * 8;
877         devmode.dmPelsWidth = swapchain->orig_width;
878         devmode.dmPelsHeight = swapchain->orig_height;
879         ChangeDisplaySettingsExW(adapter->DeviceName, &devmode, NULL, CDS_FULLSCREEN, NULL);
880     }
881
882     if (swapchain->backBuffer)
883     {
884         for (i = 0; i < swapchain->presentParms.BackBufferCount; ++i)
885         {
886             if (swapchain->backBuffer[i]) IWineD3DSurface_Release(swapchain->backBuffer[i]);
887         }
888         HeapFree(GetProcessHeap(), 0, swapchain->backBuffer);
889     }
890
891     if (swapchain->context)
892     {
893         if (swapchain->context[0])
894         {
895             context_release(swapchain->context[0]);
896             context_destroy(device, swapchain->context[0]);
897             swapchain->num_contexts = 0;
898         }
899         HeapFree(GetProcessHeap(), 0, swapchain->context);
900     }
901
902     if (swapchain->frontBuffer) IWineD3DSurface_Release(swapchain->frontBuffer);
903
904     return hr;
905 }
906
907 struct wined3d_context *swapchain_create_context_for_thread(IWineD3DSwapChain *iface)
908 {
909     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *) iface;
910     struct wined3d_context **newArray;
911     struct wined3d_context *ctx;
912
913     TRACE("Creating a new context for swapchain %p, thread %d\n", This, GetCurrentThreadId());
914
915     if (!(ctx = context_create(This, (IWineD3DSurfaceImpl *)This->frontBuffer)))
916     {
917         ERR("Failed to create a new context for the swapchain\n");
918         return NULL;
919     }
920     context_release(ctx);
921
922     newArray = HeapAlloc(GetProcessHeap(), 0, sizeof(*newArray) * This->num_contexts + 1);
923     if(!newArray) {
924         ERR("Out of memory when trying to allocate a new context array\n");
925         context_destroy(This->device, ctx);
926         return NULL;
927     }
928     memcpy(newArray, This->context, sizeof(*newArray) * This->num_contexts);
929     HeapFree(GetProcessHeap(), 0, This->context);
930     newArray[This->num_contexts] = ctx;
931     This->context = newArray;
932     This->num_contexts++;
933
934     TRACE("Returning context %p\n", ctx);
935     return ctx;
936 }
937
938 void get_drawable_size_swapchain(struct wined3d_context *context, UINT *width, UINT *height)
939 {
940     IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)context->current_rt;
941     /* The drawable size of an onscreen drawable is the surface size.
942      * (Actually: The window size, but the surface is created in window size) */
943     *width = surface->currentDesc.Width;
944     *height = surface->currentDesc.Height;
945 }