Clear the z and stencil buffers at the end of the scene, this fixes a
[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  *
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "config.h"
24 #include "wined3d_private.h"
25
26
27 /* TODO: move to shared header (or context manager )*/
28 /* x11drv GDI escapes */
29 #define X11DRV_ESCAPE 6789
30 enum x11drv_escape_codes
31 {
32     X11DRV_GET_DISPLAY,   /* get X11 display for a DC */
33     X11DRV_GET_DRAWABLE,  /* get current drawable for a DC */
34     X11DRV_GET_FONT,      /* get current X font for a DC */
35 };
36
37 /* retrieve the X display to use on a given DC */
38 inline static Display *get_display( HDC hdc )
39 {
40     Display *display;
41     enum x11drv_escape_codes escape = X11DRV_GET_DISPLAY;
42
43     if (!ExtEscape( hdc, X11DRV_ESCAPE, sizeof(escape), (LPCSTR)&escape,
44                     sizeof(display), (LPSTR)&display )) display = NULL;
45     return display;
46 }
47
48 /*TODO: some of the additional parameters may be required to
49     set the gamma ramp (for some weird reason microsoft have left swap gammaramp in device
50     but it operates on a swapchain, it may be a good idea to move it to IWineD3DSwapChain for IWineD3D)*/
51
52
53 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
54 WINE_DECLARE_DEBUG_CHANNEL(d3d_fps);
55
56
57 /* IDirect3DSwapChain IUnknown parts follow: */
58 ULONG WINAPI IWineD3DSwapChainImpl_AddRef(IWineD3DSwapChain *iface) {
59     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
60     DWORD refCount = InterlockedIncrement(&This->ref);
61     TRACE("(%p) : AddRef increasing from %ld\n", This, refCount - 1);
62     return refCount;
63 }
64
65 HRESULT WINAPI IWineD3DSwapChainImpl_QueryInterface(IWineD3DSwapChain *iface, REFIID riid, LPVOID *ppobj)
66 {
67     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
68     TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppobj);
69     if (IsEqualGUID(riid, &IID_IUnknown)
70         || IsEqualGUID(riid, &IID_IWineD3DSwapChain)){
71         IWineD3DSwapChainImpl_AddRef(iface);
72         if(ppobj == NULL){
73             ERR("Query interface called but now data allocated\n");
74             return E_NOINTERFACE;
75         }
76         *ppobj = This;
77         return D3D_OK;
78     }
79     return E_NOINTERFACE;
80 }
81
82
83 ULONG WINAPI IWineD3DSwapChainImpl_Release(IWineD3DSwapChain *iface) {
84     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
85     DWORD refCount;
86     refCount = InterlockedDecrement(&This->ref);
87     TRACE("(%p) : ReleaseRef to %ld\n", This, refCount);
88     if (refCount == 0) {
89         IUnknown* bufferParent;
90
91         /* tell the device that we've been released */
92         IWineD3DDevice_SwapChainReleased((IWineD3DDevice *)This->wineD3DDevice, iface);
93
94         /* release the ref to the front and back buffer parents */
95         IWineD3DSurface_GetParent(This->frontBuffer, &bufferParent);
96         IUnknown_Release(bufferParent); /* once for the get parent */
97         if(IUnknown_Release(bufferParent) > 0){
98             FIXME("(%p) Something's still holding the front buffer\n",This);
99         }
100
101         IWineD3DSurface_GetParent(This->backBuffer, &bufferParent);
102         IUnknown_Release(bufferParent); /* once for the get parent */
103         if(IUnknown_Release(bufferParent) > 0){
104             FIXME("(%p) Something's still holding the back buffer\n",This);
105         }
106         /* Clean up the context */
107         /* check that we are the current context first */
108         if(glXGetCurrentContext() == This->glCtx){
109             glXMakeCurrent(This->display, None, NULL);
110         }
111         glXDestroyContext(This->display, This->glCtx);
112         /* IUnknown_Release(This->parent); This should only apply to the primary swapchain,
113          all others are crated by the caller, so releasing the parent should cause
114          the child to be released, not the other way around!
115          */
116         HeapFree(GetProcessHeap(), 0, This);
117     }
118     return refCount;
119 }
120
121 HRESULT WINAPI IWineD3DSwapChainImpl_GetParent(IWineD3DSwapChain *iface, IUnknown ** ppParent){
122     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
123     *ppParent = This->parent;
124     IUnknown_AddRef(*ppParent);
125     TRACE("(%p) returning %p\n", This , *ppParent);
126     return D3D_OK;
127 }
128
129 /*IWineD3DSwapChain parts follow: */
130 HRESULT WINAPI IWineD3DSwapChainImpl_Present(IWineD3DSwapChain *iface, CONST RECT *pSourceRect, CONST RECT *pDestRect, HWND hDestWindowOverride, CONST RGNDATA *pDirtyRegion, DWORD dwFlags) {
131     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
132
133     ENTER_GL();
134
135     if (pSourceRect || pDestRect) FIXME("Unhandled present options %p/%p\n", pSourceRect, pDestRect);
136     /* TODO: If only source rect or dest rect are supplied then clip the window to match */
137     TRACE("preseting display %p, drawable %ld\n", This->display, This->drawable);
138
139     /* Don't call checkGLcall, as glGetError is not applicable here */
140     if (hDestWindowOverride && This->win_handle != hDestWindowOverride) {
141         /* Set this swapchain up to point to the new destination.. */
142 #ifdef USE_CONTEXT_MANAGER
143             /* TODO: use a context mamager */
144 #endif
145
146             /* FIXME: Never access */
147             IWineD3DSwapChainImpl *swapChainImpl;
148             IWineD3DDevice_GetSwapChain((IWineD3DDevice *)This->wineD3DDevice, 0 , (IWineD3DSwapChain **)&swapChainImpl);
149             FIXME("Unable to render to a destination window %p\n", hDestWindowOverride );
150             if(This == swapChainImpl){
151                 /* FIXME: this will be fixed by moving to a context management system */
152                 FIXME("Cannot change the target of the implicit swapchain\n");
153             }else{
154                 HDC               hDc;
155                 XVisualInfo       template;
156                 int               num;
157                 Display          *oldDisplay = This->display;
158                 GLXContext        oldContext = This->glCtx;
159                 IUnknown*         tmp;
160                 GLXContext        currentContext;
161                 Drawable          currentDrawable;
162                 hDc                          = GetDC(hDestWindowOverride);
163                 This->win_handle             = hDestWindowOverride;
164                 This->win                    = (Window)GetPropA( hDestWindowOverride, "__wine_x11_whole_window" );
165
166                 TRACE("Creating a new context for the window %p\n", hDestWindowOverride);
167                 ENTER_GL();
168                 TRACE("Desctroying context %p %p\n", This->display, This->render_ctx);
169
170
171
172                 LEAVE_GL();
173                 ENTER_GL();
174
175                 This->display    = get_display(hDc);
176                 TRACE("Got display%p  for  %p %p\n",  This->display, hDc, hDestWindowOverride);
177                 ReleaseDC(hDestWindowOverride, hDc);
178                 template.visualid = (VisualID)GetPropA(GetDesktopWindow(), "__wine_x11_visual_id");
179                 This->visInfo   = XGetVisualInfo(This->display, VisualIDMask, &template, &num);
180                 if (NULL == This->visInfo) {
181                     ERR("cannot really get XVisual\n");
182                     LEAVE_GL();
183                     return D3DERR_NOTAVAILABLE;
184                 }
185                 /* Now we have problems? well not really we just need to know what the implicit context is */
186                 /* now destroy the old context and create a new one (we should really copy the buffers over, and do the whole make current thing! */
187                 /* destroy the active context?*/
188                 TRACE("Creating new context for %p %p %p\n",This->display, This->visInfo, swapChainImpl->glCtx);
189                 This->glCtx = glXCreateContext(This->display, This->visInfo, swapChainImpl->glCtx, GL_TRUE);
190
191                 if (NULL == This->glCtx) {
192                     ERR("cannot create glxContext\n");
193                 }
194                 This->drawable     = This->win;
195                 This->render_ctx   = This->glCtx;
196                 /* Setup some default states TODO: apply the stateblock to the new context */
197                 /** save current context and drawable **/
198                 currentContext  =   glXGetCurrentContext();
199                 currentDrawable =   glXGetCurrentDrawable();
200
201                 if (glXMakeCurrent(This->display, This->win, This->glCtx) == False) {
202                     ERR("Error in setting current context (display %p context %p drawable %ld)!\n", This->display, This->glCtx, This->win);
203                 }
204
205                 checkGLcall("glXMakeCurrent");
206
207                 /* Clear the screen */
208                 glClearColor(0.0, 0.0, 0.0, 0.0);
209                 checkGLcall("glClearColor");
210                 glClearIndex(0);
211                 glClearDepth(1);
212                 glClearStencil(0);
213
214                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
215                 checkGLcall("glClear");
216
217                 glColor3f(1.0, 1.0, 1.0);
218                 checkGLcall("glColor3f");
219
220                 glEnable(GL_LIGHTING);
221                 checkGLcall("glEnable");
222
223                 glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
224                 checkGLcall("glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);");
225
226                 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
227                 checkGLcall("glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);");
228
229                 glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
230                 checkGLcall("glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);");
231
232                 /* If this swapchain is currently the active context then make this swapchain active */
233                 if(IWineD3DSurface_GetContainer(This->wineD3DDevice->renderTarget, &IID_IWineD3DSwapChain, (void **)&tmp) == D3D_OK){
234                     if(tmp != (IUnknown *)This){
235                         glXMakeCurrent(This->display, currentDrawable, currentContext);
236                         checkGLcall("glXMakeCurrent");
237                     }
238                     IUnknown_Release(tmp);
239                 }else{
240                     /* reset the context */
241                     glXMakeCurrent(This->display, currentDrawable, currentContext);
242                     checkGLcall("glXMakeCurrent");
243                 }
244                 /* delete the old contxt*/
245                 glXDestroyContext(oldDisplay, oldContext); /* Should this happen on an active context? seems a bad idea */
246                 LEAVE_GL();
247             }
248             IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapChainImpl);
249
250         }
251
252
253         /* TODO: The slow way, save the data to memory, create a new context for the destination window, transfer the data cleanup, it may be a good idea to the move this swapchain over to the using the target winows context so that it runs faster in feature. */
254
255     glXSwapBuffers(This->display, This->drawable); /* TODO: cycle through the swapchain buffers */
256
257     TRACE("glXSwapBuffers called, Starting new frame\n");
258     /* FPS support */
259     if (TRACE_ON(d3d_fps))
260     {
261         static long prev_time, frames;
262
263         DWORD time = GetTickCount();
264         frames++;
265         /* every 1.5 seconds */
266         if (time - prev_time > 1500) {
267             TRACE_(d3d_fps)("@ approx %.2ffps\n", 1000.0*frames/(time - prev_time));
268             prev_time = time;
269             frames = 0;
270         }
271     }
272
273 #if defined(FRAME_DEBUGGING)
274 {
275     if (GetFileAttributesA("C:\\D3DTRACE") != INVALID_FILE_ATTRIBUTES) {
276         if (!isOn) {
277             isOn = TRUE;
278             FIXME("Enabling D3D Trace\n");
279             __WINE_SET_DEBUGGING(__WINE_DBCL_TRACE, __wine_dbch_d3d, 1);
280 #if defined(SHOW_FRAME_MAKEUP)
281             FIXME("Singe Frame snapshots Starting\n");
282             isDumpingFrames = TRUE;
283             glClear(GL_COLOR_BUFFER_BIT);
284 #endif
285
286 #if defined(SINGLE_FRAME_DEBUGGING)
287         } else {
288 #if defined(SHOW_FRAME_MAKEUP)
289             FIXME("Singe Frame snapshots Finishing\n");
290             isDumpingFrames = FALSE;
291 #endif
292             FIXME("Singe Frame trace complete\n");
293             DeleteFileA("C:\\D3DTRACE");
294             __WINE_SET_DEBUGGING(__WINE_DBCL_TRACE, __wine_dbch_d3d, 0);
295 #endif
296         }
297     } else {
298         if (isOn) {
299             isOn = FALSE;
300 #if defined(SHOW_FRAME_MAKEUP)
301             FIXME("Single Frame snapshots Finishing\n");
302             isDumpingFrames = FALSE;
303 #endif
304             FIXME("Disabling D3D Trace\n");
305             __WINE_SET_DEBUGGING(__WINE_DBCL_TRACE, __wine_dbch_d3d, 0);
306         }
307     }
308 }
309 #endif
310
311     LEAVE_GL();
312     /* Although this is not strictly required, a simple demo showed this does occur
313        on (at least non-debug) d3d                                                  */
314     if (This->presentParms.SwapEffect & D3DSWAPEFFECT_DISCARD) {
315
316         TRACE("Clearing\n");
317
318         IWineD3DDevice_Clear((IWineD3DDevice*)This->wineD3DDevice, 0, NULL, D3DCLEAR_STENCIL|D3DCLEAR_ZBUFFER|D3DCLEAR_TARGET, 0x00, 1.0, 0);
319
320     } else {
321         TRACE("Clearing z/stencil buffer\n");
322
323         IWineD3DDevice_Clear((IWineD3DDevice*)This->wineD3DDevice, 0, NULL, D3DCLEAR_STENCIL|D3DCLEAR_ZBUFFER, 0x00, 1.0, 0);
324     }
325
326     TRACE("returning\n");
327     return D3D_OK;
328 }
329
330 HRESULT WINAPI IWineD3DSwapChainImpl_GetFrontBufferData(IWineD3DSwapChain *iface, IWineD3DSurface *pDestSurface) {
331     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
332     WINED3DFORMAT d3dformat;
333     UINT width;
334     UINT height;
335     WINED3DSURFACE_DESC desc;
336     glDescriptor *glDescription;
337
338     TRACE("(%p) : iface(%p) pDestSurface(%p)\n", This, iface, pDestSurface);
339     ENTER_GL();
340     memset(&desc, 0, sizeof(desc));
341     desc.Width =  &width;
342     desc.Height = &height;
343     desc.Format = &d3dformat;
344 #if 0 /* TODO: make sure that this swapchains context is active */
345     IWineD3DDevice_ActivateSwapChainContext(This->wineD3DDevice, iface);
346 #endif
347     IWineD3DSurface_GetDesc(pDestSurface, &desc);
348     /* make sure that the front buffer is the active read buffer */
349     glReadBuffer(GL_FRONT);
350     /* Read the pixels from the buffer into the surfaces memory */
351     IWineD3DSurface_GetGlDesc(pDestSurface, &glDescription);
352     glReadPixels(glDescription->target,
353                 glDescription->level,
354                 width,
355                 height,
356                 glDescription->glFormat,
357                 glDescription->glType,
358                 (void *)IWineD3DSurface_GetData(pDestSurface));
359     LEAVE_GL();
360     return D3D_OK;
361 }
362
363 HRESULT WINAPI IWineD3DSwapChainImpl_GetBackBuffer(IWineD3DSwapChain *iface, UINT iBackBuffer, D3DBACKBUFFER_TYPE Type, IWineD3DSurface **ppBackBuffer) {
364
365     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
366
367     *ppBackBuffer = This->backBuffer;
368     TRACE("(%p) : BackBuf %d Type %d  returning %p\n", This, iBackBuffer, Type, *ppBackBuffer);
369
370     if (iBackBuffer > This->presentParms.BackBufferCount - 1) {
371         FIXME("Only one backBuffer currently supported\n");
372         return D3DERR_INVALIDCALL;
373     }
374
375     /* Note inc ref on returned surface */
376     IWineD3DSurface_AddRef(*ppBackBuffer);
377     return D3D_OK;
378
379 }
380
381 HRESULT WINAPI IWineD3DSwapChainImpl_GetRasterStatus(IWineD3DSwapChain *iface, D3DRASTER_STATUS*pRasterStatus) {
382     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
383     static BOOL showFixmes = TRUE;
384     pRasterStatus->InVBlank = TRUE;
385     pRasterStatus->ScanLine = 0;
386     /* No openGL equivalent */
387     if(showFixmes) {
388         FIXME("(%p) : stub (once)\n", This);
389         showFixmes = FALSE;
390     }
391     return D3D_OK;
392 }
393
394 HRESULT WINAPI IWineD3DSwapChainImpl_GetDisplayMode(IWineD3DSwapChain *iface, D3DDISPLAYMODE*pMode) {
395     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
396     HDC                 hdc;
397     int                 bpp = 0;
398
399     pMode->Width        = GetSystemMetrics(SM_CXSCREEN);
400     pMode->Height       = GetSystemMetrics(SM_CYSCREEN);
401     pMode->RefreshRate  = 85; /* FIXME: How to identify? */
402
403     hdc = CreateDCA("DISPLAY", NULL, NULL, NULL);
404     bpp = GetDeviceCaps(hdc, BITSPIXEL);
405     DeleteDC(hdc);
406
407     switch (bpp) {
408     case  8: pMode->Format       = D3DFMT_R8G8B8; break;
409     case 16: pMode->Format       = D3DFMT_R5G6B5; break;
410     case 24: /*pMode->Format       = D3DFMT_R8G8B8; break; */ /* 32bpp and 24bpp can be aliased for X */
411     case 32: pMode->Format       = D3DFMT_A8R8G8B8; break;
412     default:
413        FIXME("Unrecognized display mode format\n");
414        pMode->Format       = D3DFMT_UNKNOWN;
415     }
416
417     TRACE("(%p) : returning w(%d) h(%d) rr(%d) fmt(%u,%s)\n", This, pMode->Width, pMode->Height, pMode->RefreshRate,
418     pMode->Format, debug_d3dformat(pMode->Format));
419     return D3D_OK;
420 }
421
422 HRESULT WINAPI IWineD3DSwapChainImpl_GetDevice(IWineD3DSwapChain *iface, IWineD3DDevice**ppDevice) {
423     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
424
425     *ppDevice = (IWineD3DDevice *) This->wineD3DDevice;
426
427     /* Note  Calling this method will increase the internal reference count
428        on the IDirect3DDevice9 interface. */
429     IWineD3DDevice_AddRef(*ppDevice);
430     TRACE("(%p) : returning %p\n", This, *ppDevice);
431     return D3D_OK;
432 }
433
434 HRESULT WINAPI IWineD3DSwapChainImpl_GetPresentParameters(IWineD3DSwapChain *iface, D3DPRESENT_PARAMETERS *pPresentationParameters) {
435     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
436     FIXME("(%p) : copy\n", This);
437     memcpy(pPresentationParameters, &This->presentParms, sizeof(D3DPRESENT_PARAMETERS));
438     return D3D_OK;
439 }
440
441 HRESULT WINAPI IWineD3DSwapChainImpl_SetGammaRamp(IWineD3DSwapChain *iface, DWORD Flags, CONST D3DGAMMARAMP *pRamp){
442
443     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
444     HDC hDC;
445     TRACE("(%p) : pRamp@%p flags(%ld)\n", This, pRamp, Flags);
446     hDC = GetDC(This->win_handle);
447     SetDeviceGammaRamp(hDC, (LPVOID)pRamp);
448     ReleaseDC(This->win_handle, hDC);
449     return D3D_OK;
450
451 }
452
453 HRESULT WINAPI IWineD3DSwapChainImpl_GetGammaRamp(IWineD3DSwapChain *iface, D3DGAMMARAMP *pRamp){
454
455     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
456     HDC hDC;
457     TRACE("(%p) : pRamp@%p\n", This, pRamp);
458     hDC = GetDC(This->win_handle);
459     GetDeviceGammaRamp(hDC, pRamp);
460     ReleaseDC(This->win_handle, hDC);
461     return D3D_OK;
462
463 }
464
465
466 IWineD3DSwapChainVtbl IWineD3DSwapChain_Vtbl =
467 {
468     /* IUnknown */
469     IWineD3DSwapChainImpl_QueryInterface,
470     IWineD3DSwapChainImpl_AddRef,
471     IWineD3DSwapChainImpl_Release,
472     /* IWineD3DSwapChain */
473     IWineD3DSwapChainImpl_GetParent,
474     IWineD3DSwapChainImpl_GetDevice,
475     IWineD3DSwapChainImpl_Present,
476     IWineD3DSwapChainImpl_GetFrontBufferData,
477     IWineD3DSwapChainImpl_GetBackBuffer,
478     IWineD3DSwapChainImpl_GetRasterStatus,
479     IWineD3DSwapChainImpl_GetDisplayMode,
480     IWineD3DSwapChainImpl_GetPresentParameters,
481     IWineD3DSwapChainImpl_SetGammaRamp,
482     IWineD3DSwapChainImpl_GetGammaRamp
483 };