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