Validate that the texture is no larger than the graphics cards largest
[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 %d\n", (int)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     }
321         TRACE("returning\n");
322     return D3D_OK;
323 }
324
325 HRESULT WINAPI IWineD3DSwapChainImpl_GetFrontBufferData(IWineD3DSwapChain *iface, IWineD3DSurface *pDestSurface) {
326     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
327     IWineD3DSurfaceImpl *surface = (IWineD3DSurfaceImpl *)pDestSurface;
328     GLenum format;
329     GLenum type;
330
331     TRACE("(%p) : iface(%p) pDestSurface(%p) \n", This, iface, pDestSurface);
332     ENTER_GL();
333
334     /* check to see if it's the backbuffer or the frontbuffer being requested (to make sure the data is up to date) */
335     format = D3DFmt2GLFmt(This->wineD3DDevice, surface->resource.format);
336     type   = D3DFmt2GLType(This->wineD3DDevice, surface->resource.format);
337     glReadBuffer(GL_FRONT);
338     glReadPixels(0,
339                 0,
340                 surface->currentDesc.Width,
341                 surface->currentDesc.Height,
342                 format,
343                 type,
344                 surface->resource.allocatedMemory);
345     LEAVE_GL();
346     return D3D_OK;
347 }
348
349 HRESULT WINAPI IWineD3DSwapChainImpl_GetBackBuffer(IWineD3DSwapChain *iface, UINT iBackBuffer, D3DBACKBUFFER_TYPE Type, IWineD3DSurface **ppBackBuffer) {
350
351     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
352
353     *ppBackBuffer = This->backBuffer;
354     TRACE("(%p) : BackBuf %d Type %d  returning %p\n", This, iBackBuffer, Type, *ppBackBuffer);
355
356     if (iBackBuffer > This->presentParms.BackBufferCount - 1) {
357         FIXME("Only one backBuffer currently supported\n");
358         return D3DERR_INVALIDCALL;
359     }
360
361     /* Note inc ref on returned surface */
362     IWineD3DSurface_AddRef(*ppBackBuffer);
363     return D3D_OK;
364
365 }
366
367 HRESULT WINAPI IWineD3DSwapChainImpl_GetRasterStatus(IWineD3DSwapChain *iface, D3DRASTER_STATUS*pRasterStatus) {
368     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
369     pRasterStatus->InVBlank = TRUE;
370     pRasterStatus->ScanLine = 0;
371     FIXME("(%p) : stub\n", This);
372     return D3D_OK;
373 }
374
375 HRESULT WINAPI IWineD3DSwapChainImpl_GetDisplayMode(IWineD3DSwapChain *iface, D3DDISPLAYMODE*pMode) {
376     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
377     HDC                 hdc;
378     int                 bpp = 0;
379
380     pMode->Width        = GetSystemMetrics(SM_CXSCREEN);
381     pMode->Height       = GetSystemMetrics(SM_CYSCREEN);
382     pMode->RefreshRate  = 85; /* FIXME: How to identify? */
383
384     hdc = CreateDCA("DISPLAY", NULL, NULL, NULL);
385     bpp = GetDeviceCaps(hdc, BITSPIXEL);
386     DeleteDC(hdc);
387
388     switch (bpp) {
389     case  8: pMode->Format       = D3DFMT_R8G8B8; break;
390     case 16: pMode->Format       = D3DFMT_R5G6B5; break;
391     case 24: /*pMode->Format       = D3DFMT_R8G8B8; break; */ /* 32bpp and 24bpp can be aliased for X */
392     case 32: pMode->Format       = D3DFMT_A8R8G8B8; break;
393     default:
394        FIXME("Unrecognized display mode format\n");
395        pMode->Format       = D3DFMT_UNKNOWN;
396     }
397
398     TRACE("(%p) : returning w(%d) h(%d) rr(%d) fmt(%u,%s)\n", This, pMode->Width, pMode->Height, pMode->RefreshRate,
399     pMode->Format, debug_d3dformat(pMode->Format));
400     return D3D_OK;
401 }
402
403 HRESULT WINAPI IWineD3DSwapChainImpl_GetDevice(IWineD3DSwapChain *iface, IWineD3DDevice**ppDevice) {
404     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
405
406     *ppDevice = (IWineD3DDevice *) This->wineD3DDevice;
407
408     /* Note  Calling this method will increase the internal reference count
409        on the IDirect3DDevice9 interface. */
410     IWineD3DDevice_AddRef(*ppDevice);
411     TRACE("(%p) : returning %p\n", This, *ppDevice);
412     return D3D_OK;
413 }
414
415 HRESULT WINAPI IWineD3DSwapChainImpl_GetPresentParameters(IWineD3DSwapChain *iface, D3DPRESENT_PARAMETERS *pPresentationParameters) {
416     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
417     FIXME("(%p) : copy\n", This);
418     memcpy(pPresentationParameters, &This->presentParms, sizeof(D3DPRESENT_PARAMETERS));
419     return D3D_OK;
420 }
421
422 HRESULT WINAPI IWineD3DSwapChainImpl_SetGammaRamp(IWineD3DSwapChain *iface, DWORD Flags, CONST D3DGAMMARAMP *pRamp){
423
424     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
425     HDC hDC;
426     TRACE("(%p) : pRamp@%p flags(%ld) \n", This, pRamp, Flags);
427     hDC = GetDC(This->win_handle);
428     SetDeviceGammaRamp(hDC, (LPVOID)pRamp);
429     ReleaseDC(This->win_handle, hDC);
430     return D3D_OK;
431
432 }
433
434 HRESULT WINAPI IWineD3DSwapChainImpl_GetGammaRamp(IWineD3DSwapChain *iface, D3DGAMMARAMP *pRamp){
435
436     IWineD3DSwapChainImpl *This = (IWineD3DSwapChainImpl *)iface;
437     HDC hDC;
438     TRACE("(%p) : pRamp@%p\n", This, pRamp);
439     hDC = GetDC(This->win_handle);
440     GetDeviceGammaRamp(hDC, pRamp);
441     ReleaseDC(This->win_handle, hDC);
442     return D3D_OK;
443
444 }
445
446
447 IWineD3DSwapChainVtbl IWineD3DSwapChain_Vtbl =
448 {
449     /* IUnknown */
450     IWineD3DSwapChainImpl_QueryInterface,
451     IWineD3DSwapChainImpl_AddRef,
452     IWineD3DSwapChainImpl_Release,
453     /* IWineD3DSwapChain */
454     IWineD3DSwapChainImpl_GetParent,
455     IWineD3DSwapChainImpl_GetDevice,
456     IWineD3DSwapChainImpl_Present,
457     IWineD3DSwapChainImpl_GetFrontBufferData,
458     IWineD3DSwapChainImpl_GetBackBuffer,
459     IWineD3DSwapChainImpl_GetRasterStatus,
460     IWineD3DSwapChainImpl_GetDisplayMode,
461     IWineD3DSwapChainImpl_GetPresentParameters,
462     IWineD3DSwapChainImpl_SetGammaRamp,
463     IWineD3DSwapChainImpl_GetGammaRamp
464 };