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