wined3d: Fix LockRect memory location calculation for WINED3DFMT_DXT*.
[wine/testsucceed.git] / dlls / wined3d / surface.c
blob1f4ddb616c4dbc6c5e11723960a308ee8a046ccd
1 /*
2 * IWineD3DSurface Implementation
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006 Stefan Dösinger for CodeWeavers
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "config.h"
28 #include "wine/port.h"
29 #include "wined3d_private.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
32 #define GLINFO_LOCATION ((IWineD3DImpl *)(((IWineD3DDeviceImpl *)This->resource.wineD3DDevice)->wineD3D))->gl_info
34 typedef enum {
35 NO_CONVERSION,
36 CONVERT_PALETTED,
37 CONVERT_PALETTED_CK,
38 CONVERT_CK_565,
39 CONVERT_CK_5551,
40 CONVERT_CK_4444,
41 CONVERT_CK_4444_ARGB,
42 CONVERT_CK_1555,
43 CONVERT_555,
44 CONVERT_CK_RGB24,
45 CONVERT_CK_8888,
46 CONVERT_CK_8888_ARGB,
47 CONVERT_RGB32_888,
48 CONVERT_V8U8
49 } CONVERT_TYPES;
51 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf);
53 static void surface_download_data(IWineD3DSurfaceImpl *This) {
54 if (This->resource.format == WINED3DFMT_DXT1 ||
55 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
56 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
57 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
58 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
59 } else {
60 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
61 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
63 ENTER_GL();
65 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
66 checkGLcall("glGetCompressedTexImageARB()");
68 LEAVE_GL();
70 } else {
71 void *mem;
72 int src_pitch = 0;
73 int dst_pitch = 0;
75 if(This->Flags & SFLAG_CONVERTED) {
76 FIXME("Read back converted textures unsupported\n");
77 return;
80 if (This->Flags & SFLAG_NONPOW2) {
81 src_pitch = This->bytesPerPixel * This->pow2Width;
82 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
83 src_pitch = (src_pitch + SURFACE_ALIGNMENT - 1) & ~(SURFACE_ALIGNMENT - 1);
84 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
85 } else {
86 mem = This->resource.allocatedMemory;
89 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
90 This->glDescription.glFormat, This->glDescription.glType, mem);
92 ENTER_GL();
94 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
95 This->glDescription.glType, mem);
96 checkGLcall("glGetTexImage()");
98 LEAVE_GL();
100 if (This->Flags & SFLAG_NONPOW2) {
101 LPBYTE src_data, dst_data;
102 int y;
104 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
105 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
106 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
108 * We're doing this...
110 * instead of boxing the texture :
111 * |<-texture width ->| -->pow2width| /\
112 * |111111111111111111| | |
113 * |222 Texture 222222| boxed empty | texture height
114 * |3333 Data 33333333| | |
115 * |444444444444444444| | \/
116 * ----------------------------------- |
117 * | boxed empty | boxed empty | pow2height
118 * | | | \/
119 * -----------------------------------
122 * we're repacking the data to the expected texture width
124 * |<-texture width ->| -->pow2width| /\
125 * |111111111111111111222222222222222| |
126 * |222333333333333333333444444444444| texture height
127 * |444444 | |
128 * | | \/
129 * | | |
130 * | empty | pow2height
131 * | | \/
132 * -----------------------------------
134 * == is the same as
136 * |<-texture width ->| /\
137 * |111111111111111111|
138 * |222222222222222222|texture height
139 * |333333333333333333|
140 * |444444444444444444| \/
141 * --------------------
143 * this also means that any references to allocatedMemory should work with the data as if were a
144 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
146 * internally the texture is still stored in a boxed format so any references to textureName will
147 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
149 * Performance should not be an issue, because applications normally do not lock the surfaces when
150 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
151 * and doesn't have to be re-read.
153 src_data = mem;
154 dst_data = This->resource.allocatedMemory;
155 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
156 for (y = 1 ; y < This->currentDesc.Height; y++) {
157 /* skip the first row */
158 src_data += src_pitch;
159 dst_data += dst_pitch;
160 memcpy(dst_data, src_data, dst_pitch);
163 HeapFree(GetProcessHeap(), 0, mem);
168 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
169 if (This->resource.format == WINED3DFMT_DXT1 ||
170 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
171 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
172 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
173 FIXME("Using DXT1/3/5 without advertized support\n");
174 } else {
175 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
176 ENTER_GL();
177 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
178 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
179 * function uses glCompressedTexImage2D instead of the SubImage call
181 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, This->glDescription.glFormatInternal,
182 width, height, 0 /* border */, This->resource.size, data));
183 checkGLcall("glCompressedTexSubImage2D");
184 LEAVE_GL();
186 } else {
187 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
188 ENTER_GL();
189 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
190 checkGLcall("glTexSubImage2D");
191 LEAVE_GL();
195 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
196 TRACE("(%p) : Creating surface (target %#x) level %d, d3d format %s, internal format %#x, width %d, height %d, gl format %#x, gl type=%#x\n", This,
197 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
199 if (This->resource.format == WINED3DFMT_DXT1 ||
200 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
201 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
202 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
203 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
204 return;
207 ENTER_GL();
209 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, NULL);
210 checkGLcall("glTexImage2D");
212 LEAVE_GL();
215 /* *******************************************
216 IWineD3DSurface IUnknown parts follow
217 ******************************************* */
218 HRESULT WINAPI IWineD3DSurfaceImpl_QueryInterface(IWineD3DSurface *iface, REFIID riid, LPVOID *ppobj)
220 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
221 /* Warn ,but be nice about things */
222 TRACE("(%p)->(%s,%p)\n", This,debugstr_guid(riid),ppobj);
224 if (IsEqualGUID(riid, &IID_IUnknown)
225 || IsEqualGUID(riid, &IID_IWineD3DBase)
226 || IsEqualGUID(riid, &IID_IWineD3DResource)
227 || IsEqualGUID(riid, &IID_IWineD3DSurface)) {
228 IUnknown_AddRef((IUnknown*)iface);
229 *ppobj = This;
230 return S_OK;
232 *ppobj = NULL;
233 return E_NOINTERFACE;
236 ULONG WINAPI IWineD3DSurfaceImpl_AddRef(IWineD3DSurface *iface) {
237 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
238 ULONG ref = InterlockedIncrement(&This->resource.ref);
239 TRACE("(%p) : AddRef increasing from %d\n", This,ref - 1);
240 return ref;
243 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
244 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
245 ULONG ref = InterlockedDecrement(&This->resource.ref);
246 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
247 if (ref == 0) {
248 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) This->resource.wineD3DDevice;
249 TRACE("(%p) : cleaning up\n", This);
251 if(iface == device->lastActiveRenderTarget) {
252 IWineD3DSwapChainImpl *swapchain = device->swapchains ? (IWineD3DSwapChainImpl *) device->swapchains[0] : NULL;
254 TRACE("Last active render target destroyed\n");
255 /* Find a replacement surface for the currently active back buffer. The context manager does not do NULL
256 * checks, so switch to a valid target as long as the currently set surface is still valid. Use the
257 * surface of the implicit swpchain. If that is the same as the destroyed surface the device is destroyed
258 * and the lastActiveRenderTarget member shouldn't matter
260 if(swapchain) {
261 if(swapchain->backBuffer && swapchain->backBuffer[0] != iface) {
262 TRACE("Activating primary back buffer\n");
263 ActivateContext(device, swapchain->backBuffer[0], CTXUSAGE_RESOURCELOAD);
264 } else if(!swapchain->backBuffer && swapchain->frontBuffer != iface) {
265 /* Single buffering environment */
266 TRACE("Activating primary front buffer\n");
267 ActivateContext(device, swapchain->frontBuffer, CTXUSAGE_RESOURCELOAD);
268 } else {
269 TRACE("Device is being destroyed, setting lastActiveRenderTarget = 0xdeadbabe\n");
270 /* Implicit render target destroyed, that means the device is being destroyed
271 * whatever we set here, it shouldn't matter
273 device->lastActiveRenderTarget = (IWineD3DSurface *) 0xdeadbabe;
275 } else {
276 /* May happen during ddraw uninitialization */
277 TRACE("Render target set, but swapchain does not exist!\n");
278 device->lastActiveRenderTarget = (IWineD3DSurface *) 0xdeadcafe;
282 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
283 ENTER_GL();
284 TRACE("Deleting texture %d\n", This->glDescription.textureName);
285 glDeleteTextures(1, &This->glDescription.textureName);
286 LEAVE_GL();
289 if(This->Flags & SFLAG_DIBSECTION) {
290 /* Release the DC */
291 SelectObject(This->hDC, This->dib.holdbitmap);
292 DeleteDC(This->hDC);
293 /* Release the DIB section */
294 DeleteObject(This->dib.DIBsection);
295 This->dib.bitmap_data = NULL;
296 This->resource.allocatedMemory = NULL;
298 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
300 HeapFree(GetProcessHeap(), 0, This->palette9);
302 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
303 if(iface == device->ddraw_primary)
304 device->ddraw_primary = NULL;
306 TRACE("(%p) Released\n", This);
307 HeapFree(GetProcessHeap(), 0, This);
310 return ref;
313 /* ****************************************************
314 IWineD3DSurface IWineD3DResource parts follow
315 **************************************************** */
316 HRESULT WINAPI IWineD3DSurfaceImpl_GetDevice(IWineD3DSurface *iface, IWineD3DDevice** ppDevice) {
317 return IWineD3DResourceImpl_GetDevice((IWineD3DResource *)iface, ppDevice);
320 HRESULT WINAPI IWineD3DSurfaceImpl_SetPrivateData(IWineD3DSurface *iface, REFGUID refguid, CONST void* pData, DWORD SizeOfData, DWORD Flags) {
321 return IWineD3DResourceImpl_SetPrivateData((IWineD3DResource *)iface, refguid, pData, SizeOfData, Flags);
324 HRESULT WINAPI IWineD3DSurfaceImpl_GetPrivateData(IWineD3DSurface *iface, REFGUID refguid, void* pData, DWORD* pSizeOfData) {
325 return IWineD3DResourceImpl_GetPrivateData((IWineD3DResource *)iface, refguid, pData, pSizeOfData);
328 HRESULT WINAPI IWineD3DSurfaceImpl_FreePrivateData(IWineD3DSurface *iface, REFGUID refguid) {
329 return IWineD3DResourceImpl_FreePrivateData((IWineD3DResource *)iface, refguid);
332 DWORD WINAPI IWineD3DSurfaceImpl_SetPriority(IWineD3DSurface *iface, DWORD PriorityNew) {
333 return IWineD3DResourceImpl_SetPriority((IWineD3DResource *)iface, PriorityNew);
336 DWORD WINAPI IWineD3DSurfaceImpl_GetPriority(IWineD3DSurface *iface) {
337 return IWineD3DResourceImpl_GetPriority((IWineD3DResource *)iface);
340 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
341 /* TODO: re-write the way textures and managed,
342 * use a 'opengl context manager' to manage RenderTarget surfaces
343 ** *********************************************************/
345 /* TODO: check for locks */
346 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
347 IWineD3DBaseTexture *baseTexture = NULL;
348 TRACE("(%p)Checking to see if the container is a base texture\n", This);
349 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
350 TRACE("Passing to conatiner\n");
351 IWineD3DBaseTexture_PreLoad(baseTexture);
352 IWineD3DBaseTexture_Release(baseTexture);
353 } else {
354 TRACE("(%p) : About to load surface\n", This);
355 ENTER_GL();
356 #if 0 /* TODO: context manager support */
357 IWineD3DContextManager_PushState(This->contextManager, GL_TEXTURE_2D, ENABLED, NOW /* make sure the state is applied now */);
358 #endif
359 glEnable(This->glDescription.target);/* make sure texture support is enabled in this context */
360 if (!This->glDescription.level) {
361 if (!This->glDescription.textureName) {
362 glGenTextures(1, &This->glDescription.textureName);
363 checkGLcall("glGenTextures");
364 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
366 glBindTexture(This->glDescription.target, This->glDescription.textureName);
367 checkGLcall("glBindTexture");
368 IWineD3DSurface_LoadTexture(iface);
369 /* This is where we should be reducing the amount of GLMemoryUsed */
370 } else if (This->glDescription.textureName) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
371 /* assume this is a coding error not a real error for now */
372 FIXME("Mipmap surface has a glTexture bound to it!\n");
374 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
375 /* Tell opengl to try and keep this texture in video ram (well mostly) */
376 GLclampf tmp;
377 tmp = 0.9f;
378 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
380 /* TODO: disable texture support, if it wastn't enabled when we entered. */
381 #if 0 /* TODO: context manager support */
382 IWineD3DContextManager_PopState(This->contextManager, GL_TEXTURE_2D, DISABLED,DELAYED
383 /* we don't care when the state is disabled(if atall) */);
384 #endif
385 LEAVE_GL();
387 return;
390 WINED3DRESOURCETYPE WINAPI IWineD3DSurfaceImpl_GetType(IWineD3DSurface *iface) {
391 TRACE("(%p) : calling resourceimpl_GetType\n", iface);
392 return IWineD3DResourceImpl_GetType((IWineD3DResource *)iface);
395 HRESULT WINAPI IWineD3DSurfaceImpl_GetParent(IWineD3DSurface *iface, IUnknown **pParent) {
396 TRACE("(%p) : calling resourceimpl_GetParent\n", iface);
397 return IWineD3DResourceImpl_GetParent((IWineD3DResource *)iface, pParent);
400 /* ******************************************************
401 IWineD3DSurface IWineD3DSurface parts follow
402 ****************************************************** */
404 HRESULT WINAPI IWineD3DSurfaceImpl_GetContainer(IWineD3DSurface* iface, REFIID riid, void** ppContainer) {
405 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
406 IWineD3DBase *container = 0;
408 TRACE("(This %p, riid %s, ppContainer %p)\n", This, debugstr_guid(riid), ppContainer);
410 if (!ppContainer) {
411 ERR("Called without a valid ppContainer.\n");
414 /** From MSDN:
415 * If the surface is created using CreateImageSurface/CreateOffscreenPlainSurface, CreateRenderTarget,
416 * or CreateDepthStencilSurface, the surface is considered stand alone. In this case,
417 * GetContainer will return the Direct3D device used to create the surface.
419 if (This->container) {
420 container = This->container;
421 } else {
422 container = (IWineD3DBase *)This->resource.wineD3DDevice;
425 TRACE("Relaying to QueryInterface\n");
426 return IUnknown_QueryInterface(container, riid, ppContainer);
429 HRESULT WINAPI IWineD3DSurfaceImpl_GetDesc(IWineD3DSurface *iface, WINED3DSURFACE_DESC *pDesc) {
430 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
432 TRACE("(%p) : copying into %p\n", This, pDesc);
433 if(pDesc->Format != NULL) *(pDesc->Format) = This->resource.format;
434 if(pDesc->Type != NULL) *(pDesc->Type) = This->resource.resourceType;
435 if(pDesc->Usage != NULL) *(pDesc->Usage) = This->resource.usage;
436 if(pDesc->Pool != NULL) *(pDesc->Pool) = This->resource.pool;
437 if(pDesc->Size != NULL) *(pDesc->Size) = This->resource.size; /* dx8 only */
438 if(pDesc->MultiSampleType != NULL) *(pDesc->MultiSampleType) = This->currentDesc.MultiSampleType;
439 if(pDesc->MultiSampleQuality != NULL) *(pDesc->MultiSampleQuality) = This->currentDesc.MultiSampleQuality;
440 if(pDesc->Width != NULL) *(pDesc->Width) = This->currentDesc.Width;
441 if(pDesc->Height != NULL) *(pDesc->Height) = This->currentDesc.Height;
442 return WINED3D_OK;
445 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
446 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
447 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
448 if (This->glDescription.textureName == 0 && textureName != 0) {
449 This->Flags &= ~SFLAG_INTEXTURE;
450 IWineD3DSurface_AddDirtyRect(iface, NULL);
452 This->glDescription.textureName = textureName;
453 This->glDescription.target = target;
456 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
457 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
458 TRACE("(%p) : returning %p\n", This, &This->glDescription);
459 *glDescription = &This->glDescription;
462 /* TODO: think about moving this down to resource? */
463 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
464 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
465 /* This should only be called for sysmem textures, it may be a good idea to extend this to all pools at some point in the futture */
466 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
467 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
469 return (CONST void*)(This->resource.allocatedMemory);
472 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch, BOOL srcUpsideDown) {
473 BYTE *mem;
474 GLint fmt;
475 GLint type;
476 BYTE *row, *top, *bottom;
477 int i;
478 BOOL bpp;
480 switch(This->resource.format)
482 case WINED3DFMT_P8:
484 /* GL can't return palettized data, so read ARGB pixels into a
485 * separate block of memory and convert them into palettized format
486 * in software. Slow, but if the app means to use palettized render
487 * targets and locks it...
489 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
490 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
491 * for the color channels when palettizing the colors.
493 fmt = GL_RGB;
494 type = GL_UNSIGNED_BYTE;
495 pitch *= 3;
496 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
497 if(!mem) {
498 ERR("Out of memory\n");
499 return;
501 bpp = This->bytesPerPixel * 3;
503 break;
505 default:
506 mem = dest;
507 fmt = This->glDescription.glFormat;
508 type = This->glDescription.glType;
509 bpp = This->bytesPerPixel;
512 glReadPixels(rect->left, rect->top,
513 rect->right - rect->left,
514 rect->bottom - rect->top,
515 fmt, type, mem);
516 vcheckGLcall("glReadPixels");
518 /* TODO: Merge this with the palettization loop below for P8 targets */
520 if(!srcUpsideDown) {
521 UINT len, off;
522 /* glReadPixels returns the image upside down, and there is no way to prevent this.
523 Flip the lines in software */
524 len = (rect->right - rect->left) * bpp;
525 off = rect->left * bpp;
527 row = HeapAlloc(GetProcessHeap(), 0, len);
528 if(!row) {
529 ERR("Out of memory\n");
530 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
531 return;
534 top = mem + pitch * rect->top;
535 bottom = ((BYTE *) mem) + pitch * ( rect->bottom - rect->top - 1);
536 for(i = 0; i < (rect->bottom - rect->top) / 2; i++) {
537 memcpy(row, top + off, len);
538 memcpy(top + off, bottom + off, len);
539 memcpy(bottom + off, row, len);
540 top += pitch;
541 bottom -= pitch;
543 HeapFree(GetProcessHeap(), 0, row);
546 if(This->resource.format == WINED3DFMT_P8) {
547 PALETTEENTRY *pal;
548 DWORD width = pitch / 3;
549 int x, y, c;
550 if(This->palette) {
551 pal = This->palette->palents;
552 } else {
553 pal = This->resource.wineD3DDevice->palettes[This->resource.wineD3DDevice->currentPalette];
556 for(y = rect->top; y < rect->bottom; y++) {
557 for(x = rect->left; x < rect->right; x++) {
558 /* start lines pixels */
559 BYTE *blue = (BYTE *) ((BYTE *) mem) + y * pitch + x * (sizeof(BYTE) * 3);
560 BYTE *green = blue + 1;
561 BYTE *red = green + 1;
563 for(c = 0; c < 256; c++) {
564 if(*red == pal[c].peRed &&
565 *green == pal[c].peGreen &&
566 *blue == pal[c].peBlue)
568 *((BYTE *) dest + y * width + x) = c;
569 break;
574 HeapFree(GetProcessHeap(), 0, mem);
578 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
579 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
580 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
581 IWineD3DSwapChainImpl *swapchain = NULL;
583 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
585 if (!(This->Flags & SFLAG_LOCKABLE)) {
586 /* Note: UpdateTextures calls CopyRects which calls this routine to populate the
587 texture regions, and since the destination is an unlockable region we need
588 to tolerate this */
589 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
590 /*return WINED3DERR_INVALIDCALL; */
593 pLockedRect->Pitch = IWineD3DSurface_GetPitch(iface);
595 /* Mark the surface locked */
596 This->Flags |= SFLAG_LOCKED;
598 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy */
599 if(!This->resource.allocatedMemory) {
600 This->resource.allocatedMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + 4);
601 This->Flags &= ~SFLAG_INSYSMEM; /* This is the marker that surface data has to be downloaded */
604 /* Calculate the correct start address to report */
605 if (NULL == pRect) {
606 pLockedRect->pBits = This->resource.allocatedMemory;
607 This->lockedRect.left = 0;
608 This->lockedRect.top = 0;
609 This->lockedRect.right = This->currentDesc.Width;
610 This->lockedRect.bottom = This->currentDesc.Height;
611 TRACE("Locked Rect (%p) = l %d, t %d, r %d, b %d\n", &This->lockedRect, This->lockedRect.left, This->lockedRect.top, This->lockedRect.right, This->lockedRect.bottom);
612 } else {
613 TRACE("Lock Rect (%p) = l %d, t %d, r %d, b %d\n", pRect, pRect->left, pRect->top, pRect->right, pRect->bottom);
615 if ((pRect->top < 0) ||
616 (pRect->left < 0) ||
617 (pRect->left >= pRect->right) ||
618 (pRect->top >= pRect->bottom) ||
619 (pRect->right > This->currentDesc.Width) ||
620 (pRect->bottom > This->currentDesc.Height))
622 WARN(" Invalid values in pRect !!!\n");
623 return WINED3DERR_INVALIDCALL;
626 /* DXTn textures are based on compressed blocks of 4x4 pixels, each
627 * 16 bytes large (8 bytes in case of DXT1). Because of that Pitch has
628 * slightly different meaning compared to regular textures. For DXTn
629 * textures Pitch is the size of a row of blocks, 4 high and "width"
630 * long. The x offset is calculated differently as well, since moving 4
631 * pixels to the right actually moves an entire 4x4 block to right, ie
632 * 16 bytes (8 in case of DXT1). */
633 if (This->resource.format == WINED3DFMT_DXT1) {
634 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top / 4) + (pRect->left * 2);
635 } else if (This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3
636 || This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
637 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top / 4) + (pRect->left * 4);
638 } else {
639 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top) + (pRect->left * This->bytesPerPixel);
641 This->lockedRect.left = pRect->left;
642 This->lockedRect.top = pRect->top;
643 This->lockedRect.right = pRect->right;
644 This->lockedRect.bottom = pRect->bottom;
647 if (This->Flags & SFLAG_NONPOW2) {
648 TRACE("Locking non-power 2 texture\n");
651 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
652 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
653 * changed
655 if(!(This->Flags & SFLAG_DYNLOCK)) {
656 This->lockCount++;
657 /* MAXLOCKCOUNT is defined in wined3d_private.h */
658 if(This->lockCount > MAXLOCKCOUNT) {
659 TRACE("Surface is locked regularily, not freeing the system memory copy any more\n");
660 This->Flags |= SFLAG_DYNLOCK;
664 if((Flags & WINED3DLOCK_DISCARD) || (This->Flags & SFLAG_INSYSMEM)) {
665 TRACE("WINED3DLOCK_DISCARD flag passed, or local copy is up to date, not downloading data\n");
666 goto lock_end;
669 /* Now download the surface content from opengl
670 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
671 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
673 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
674 if(swapchain || iface == myDevice->render_targets[0]) {
675 BOOL srcIsUpsideDown;
677 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
678 static BOOL warned = FALSE;
679 if(!warned) {
680 ERR("The application tries to lock the render target, but render target locking is disabled\n");
681 warned = TRUE;
683 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
684 return WINED3D_OK;
687 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
688 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
689 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
690 * context->last_was_blit set on the unlock.
692 ENTER_GL();
693 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
695 /* Select the correct read buffer, and give some debug output.
696 * There is no need to keep track of the current read buffer or reset it, every part of the code
697 * that reads sets the read buffer as desired.
699 if(!swapchain) {
700 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
701 * Read from the back buffer
703 TRACE("Locking offscreen render target\n");
704 glReadBuffer(myDevice->offscreenBuffer);
705 srcIsUpsideDown = TRUE;
706 } else {
707 if(iface == swapchain->frontBuffer) {
708 TRACE("Locking the front buffer\n");
709 glReadBuffer(GL_FRONT);
710 } else if(swapchain->backBuffer && iface == swapchain->backBuffer[0]) {
711 TRACE("Locking the back buffer\n");
712 glReadBuffer(GL_BACK);
713 } else {
714 /* Ok, there is an issue: OpenGL does not guarant any back buffer number, so all we can do is to read GL_BACK
715 * and hope it gives what the app wants
717 FIXME("Application is locking a 2nd or higher back buffer\n");
718 glReadBuffer(GL_BACK);
720 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
721 srcIsUpsideDown = FALSE;
724 switch(wined3d_settings.rendertargetlock_mode) {
725 case RTL_AUTO:
726 case RTL_READDRAW:
727 case RTL_READTEX:
728 read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
729 break;
731 case RTL_TEXDRAW:
732 case RTL_TEXTEX:
733 read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
734 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
735 break;
737 LEAVE_GL();
739 /* Mark the local copy up to date if a full download was done */
740 if(This->lockedRect.left == 0 &&
741 This->lockedRect.top == 0 &&
742 This->lockedRect.right == This->currentDesc.Width &&
743 This->lockedRect.bottom == This->currentDesc.Height) {
744 This->Flags |= SFLAG_INSYSMEM;
746 } else if(iface == myDevice->stencilBufferTarget) {
747 /** the depth stencil in openGL has a format of GL_FLOAT
748 * which should be good for WINED3DFMT_D16_LOCKABLE
749 * and WINED3DFMT_D16
750 * it is unclear what format the stencil buffer is in except.
751 * 'Each index is converted to fixed point...
752 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
753 * mappings in the table GL_PIXEL_MAP_S_TO_S.
754 * glReadPixels(This->lockedRect.left,
755 * This->lockedRect.bottom - j - 1,
756 * This->lockedRect.right - This->lockedRect.left,
757 * 1,
758 * GL_DEPTH_COMPONENT,
759 * type,
760 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
762 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
763 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
764 * none of that is the case the problem is not in this function :-)
765 ********************************************/
766 FIXME("Depth stencil locking not supported yet\n");
767 } else {
768 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
769 TRACE("locking an ordinarary surface\n");
771 /* TODO: Make sure that *any* context is active for this thread. It is not important which context that is,
772 * nor that is has any special setup(CTXUSAGE_LOADRESOURCE is fine), but the code below needs a context.
773 * A context is guaranteed to be there in a single threaded environment, but not with multithreading
775 if (0 != This->glDescription.textureName) {
776 /* Now I have to copy thing bits back */
778 /* Make sure that a proper texture unit is selected, bind the texture and dirtify the sampler to restore the texture on the next draw */
779 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
780 ENTER_GL();
781 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
782 checkGLcall("glActiveTextureARB");
783 LEAVE_GL();
785 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(0));
786 IWineD3DSurface_PreLoad(iface);
788 surface_download_data(This);
791 /* The local copy is now up to date to the opengl one because a full download was done */
792 This->Flags |= SFLAG_INSYSMEM;
795 lock_end:
796 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
797 /* Don't dirtify */
798 } else {
799 IWineD3DBaseTexture *pBaseTexture;
801 * Dirtify on lock
802 * as seen in msdn docs
804 IWineD3DSurface_AddDirtyRect(iface, &This->lockedRect);
806 /** Dirtify Container if needed */
807 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
808 TRACE("Making container dirty\n");
809 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
810 IWineD3DBaseTexture_Release(pBaseTexture);
811 } else {
812 TRACE("Surface is standalone, no need to dirty the container\n");
816 TRACE("returning memory@%p, pitch(%d) dirtyfied(%d)\n", pLockedRect->pBits, pLockedRect->Pitch,
817 This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
818 return WINED3D_OK;
821 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This) {
822 GLint prev_store;
823 GLint prev_rasterpos[4];
824 GLint skipBytes = 0;
825 BOOL storechanged = FALSE, memory_allocated = FALSE;
826 GLint fmt, type;
827 BYTE *mem;
828 UINT bpp;
829 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
831 glDisable(GL_TEXTURE_2D);
832 vcheckGLcall("glDisable(GL_TEXTURE_2D)");
834 glFlush();
835 vcheckGLcall("glFlush");
836 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
837 vcheckGLcall("glIntegerv");
838 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
839 vcheckGLcall("glIntegerv");
840 glPixelZoom(1.0, -1.0);
841 vcheckGLcall("glPixelZoom");
843 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
844 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
845 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
847 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
848 vcheckGLcall("glRasterPos2f");
850 /* Some drivers(radeon dri, others?) don't like exceptions during
851 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
852 * after ReleaseDC. Reading it will cause an exception, which x11drv will
853 * catch to put the dib section in InSync mode, which leads to a crash
854 * and a blocked x server on my radeon card.
856 * The following lines read the dib section so it is put in inSync mode
857 * before glDrawPixels is called and the crash is prevented. There won't
858 * be any interfering gdi accesses, because UnlockRect is called from
859 * ReleaseDC, and the app won't use the dc any more afterwards.
861 if(This->Flags & SFLAG_DIBSECTION) {
862 volatile BYTE read;
863 read = This->resource.allocatedMemory[0];
866 switch (This->resource.format) {
867 /* No special care needed */
868 case WINED3DFMT_A4R4G4B4:
869 case WINED3DFMT_R5G6B5:
870 case WINED3DFMT_A1R5G5B5:
871 case WINED3DFMT_R8G8B8:
872 type = This->glDescription.glType;
873 fmt = This->glDescription.glFormat;
874 mem = This->resource.allocatedMemory;
875 bpp = This->bytesPerPixel;
876 break;
878 case WINED3DFMT_X4R4G4B4:
880 int size;
881 unsigned short *data;
882 data = (unsigned short *)This->resource.allocatedMemory;
883 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
884 while(size > 0) {
885 *data |= 0xF000;
886 data++;
887 size--;
889 type = This->glDescription.glType;
890 fmt = This->glDescription.glFormat;
891 mem = This->resource.allocatedMemory;
892 bpp = This->bytesPerPixel;
894 break;
896 case WINED3DFMT_X1R5G5B5:
898 int size;
899 unsigned short *data;
900 data = (unsigned short *)This->resource.allocatedMemory;
901 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
902 while(size > 0) {
903 *data |= 0x8000;
904 data++;
905 size--;
907 type = This->glDescription.glType;
908 fmt = This->glDescription.glFormat;
909 mem = This->resource.allocatedMemory;
910 bpp = This->bytesPerPixel;
912 break;
914 case WINED3DFMT_X8R8G8B8:
916 /* make sure the X byte is set to alpha on, since it
917 could be any random value. This fixes the intro movie in Pirates! */
918 int size;
919 unsigned int *data;
920 data = (unsigned int *)This->resource.allocatedMemory;
921 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
922 while(size > 0) {
923 *data |= 0xFF000000;
924 data++;
925 size--;
928 /* Fall through */
930 case WINED3DFMT_A8R8G8B8:
932 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
933 vcheckGLcall("glPixelStorei");
934 storechanged = TRUE;
935 type = This->glDescription.glType;
936 fmt = This->glDescription.glFormat;
937 mem = This->resource.allocatedMemory;
938 bpp = This->bytesPerPixel;
940 break;
942 case WINED3DFMT_A2R10G10B10:
944 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
945 vcheckGLcall("glPixelStorei");
946 storechanged = TRUE;
947 type = This->glDescription.glType;
948 fmt = This->glDescription.glFormat;
949 mem = This->resource.allocatedMemory;
950 bpp = This->bytesPerPixel;
952 break;
954 case WINED3DFMT_P8:
956 int height = This->glRect.bottom - This->glRect.top;
957 type = GL_UNSIGNED_BYTE;
958 fmt = GL_RGBA;
960 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * sizeof(DWORD));
961 if(!mem) {
962 ERR("Out of memory\n");
963 return;
965 memory_allocated = TRUE;
966 d3dfmt_convert_surface(This->resource.allocatedMemory,
967 mem,
968 pitch,
969 pitch,
970 height,
971 pitch * 4,
972 CONVERT_PALETTED,
973 This);
974 bpp = This->bytesPerPixel * 4;
975 pitch *= 4;
977 break;
979 default:
980 FIXME("Unsupported Format %u in locking func\n", This->resource.format);
982 /* Give it a try */
983 type = This->glDescription.glType;
984 fmt = This->glDescription.glFormat;
985 mem = This->resource.allocatedMemory;
986 bpp = This->bytesPerPixel;
989 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
990 (This->lockedRect.bottom - This->lockedRect.top)-1,
991 fmt, type,
992 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
993 checkGLcall("glDrawPixels");
994 glPixelZoom(1.0,1.0);
995 vcheckGLcall("glPixelZoom");
997 glRasterPos3iv(&prev_rasterpos[0]);
998 vcheckGLcall("glRasterPos3iv");
1000 /* Reset to previous pack row length */
1001 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1002 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1003 if(storechanged) {
1004 glPixelStorei(GL_PACK_SWAP_BYTES, prev_store);
1005 vcheckGLcall("glPixelStorei GL_PACK_SWAP_BYTES");
1008 /* Blitting environment requires that 2D texturing is enabled. It was turned off before,
1009 * turn it on again
1011 glEnable(GL_TEXTURE_2D);
1012 checkGLcall("glEnable(GL_TEXTURE_2D)");
1014 if(memory_allocated) HeapFree(GetProcessHeap(), 0, mem);
1015 return;
1018 static void flush_to_framebuffer_texture(IWineD3DSurfaceImpl *This) {
1019 float glTexCoord[4];
1021 glTexCoord[0] = (float) This->lockedRect.left / (float) This->pow2Width; /* left */
1022 glTexCoord[1] = (float) This->lockedRect.right / (float) This->pow2Width; /* right */
1023 glTexCoord[2] = (float) This->lockedRect.top / (float) This->pow2Height; /* top */
1024 glTexCoord[3] = (float) This->lockedRect.bottom / (float) This->pow2Height; /* bottom */
1026 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
1028 ENTER_GL();
1030 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
1031 checkGLcall("glEnable glBindTexture");
1033 /* No filtering for blts */
1034 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1035 checkGLcall("glTexParameteri");
1036 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1037 checkGLcall("glTexParameteri");
1039 /* Start drawing a quad */
1040 glBegin(GL_QUADS);
1042 glColor3d(1.0f, 1.0f, 1.0f);
1043 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
1044 glVertex3f(This->lockedRect.left, This->lockedRect.top, 0.0);
1046 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
1047 glVertex3f(This->lockedRect.left, This->lockedRect.bottom, 0.0);
1049 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
1050 glVertex3d(This->lockedRect.right, This->lockedRect.bottom, 0.0);
1052 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
1053 glVertex3f(This->lockedRect.right, This->lockedRect.top, 0.0);
1055 glEnd();
1056 checkGLcall("glEnd");
1058 /* Unbind the texture */
1059 glBindTexture(GL_TEXTURE_2D, 0);
1060 checkGLcall("glEnable glBindTexture");
1062 LEAVE_GL();
1065 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1066 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1067 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1068 IWineD3DSwapChainImpl *swapchain = NULL;
1070 if (!(This->Flags & SFLAG_LOCKED)) {
1071 WARN("trying to Unlock an unlocked surf@%p\n", This);
1072 return WINED3DERR_INVALIDCALL;
1075 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1077 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1078 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1079 goto unlock_end;
1082 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1083 if(swapchain || iface == myDevice->render_targets[0]) {
1084 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1085 static BOOL warned = FALSE;
1086 if(!warned) {
1087 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1088 warned = TRUE;
1090 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1091 goto unlock_end;
1094 /* Activate the correct context for the render target */
1095 ENTER_GL();
1096 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1098 if(!swapchain) {
1099 /* Primary offscreen render target */
1100 TRACE("Offscreen render target\n");
1101 glDrawBuffer(myDevice->offscreenBuffer);
1102 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1103 } else {
1104 if(iface == swapchain->frontBuffer) {
1105 TRACE("Onscreen front buffer\n");
1106 glDrawBuffer(GL_FRONT);
1107 checkGLcall("glDrawBuffer(GL_FRONT)");
1108 } else if(iface == swapchain->backBuffer[0]) {
1109 TRACE("Onscreen back buffer\n");
1110 glDrawBuffer(GL_BACK);
1111 checkGLcall("glDrawBuffer(GL_BACK)");
1112 } else {
1113 FIXME("Unlocking a higher back buffer\n");
1114 glDrawBuffer(GL_BACK);
1115 checkGLcall("glDrawBuffer(GL_BACK)");
1117 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1120 switch(wined3d_settings.rendertargetlock_mode) {
1121 case RTL_AUTO:
1122 case RTL_READDRAW:
1123 case RTL_TEXDRAW:
1124 flush_to_framebuffer_drawpixels(This);
1125 break;
1127 case RTL_READTEX:
1128 case RTL_TEXTEX:
1129 flush_to_framebuffer_texture(This);
1130 break;
1132 if(!swapchain) {
1133 glDrawBuffer(myDevice->offscreenBuffer);
1134 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1135 } else if(swapchain->backBuffer) {
1136 glDrawBuffer(GL_BACK);
1137 checkGLcall("glDrawBuffer(GL_BACK)");
1138 } else {
1139 glDrawBuffer(GL_FRONT);
1140 checkGLcall("glDrawBuffer(GL_FRONT)");
1142 LEAVE_GL();
1144 This->dirtyRect.left = This->currentDesc.Width;
1145 This->dirtyRect.top = This->currentDesc.Height;
1146 This->dirtyRect.right = 0;
1147 This->dirtyRect.bottom = 0;
1148 This->Flags |= SFLAG_INDRAWABLE;
1149 } else if(iface == myDevice->stencilBufferTarget) {
1150 FIXME("Depth Stencil buffer locking is not implemented\n");
1151 } else {
1152 /* The rest should be a normal texture */
1153 IWineD3DBaseTextureImpl *impl;
1154 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1155 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1156 * states need resetting
1158 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1159 if(impl->baseTexture.bindCount) {
1160 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1162 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1166 unlock_end:
1167 This->Flags &= ~SFLAG_LOCKED;
1168 memset(&This->lockedRect, 0, sizeof(RECT));
1169 return WINED3D_OK;
1172 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1173 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1174 WINED3DLOCKED_RECT lock;
1175 UINT usage;
1176 BITMAPINFO* b_info;
1177 HDC ddc;
1178 DWORD *masks;
1179 HRESULT hr;
1180 RGBQUAD col[256];
1181 const PixelFormatDesc *formatEntry = getFormatDescEntry(This->resource.format);
1183 TRACE("(%p)->(%p)\n",This,pHDC);
1185 if(This->Flags & SFLAG_USERPTR) {
1186 ERR("Not supported on surfaces with an application-provided surfaces\n");
1187 return DDERR_NODC;
1190 /* Give more detailed info for ddraw */
1191 if (This->Flags & SFLAG_DCINUSE)
1192 return DDERR_DCALREADYCREATED;
1194 /* Can't GetDC if the surface is locked */
1195 if (This->Flags & SFLAG_LOCKED)
1196 return WINED3DERR_INVALIDCALL;
1198 memset(&lock, 0, sizeof(lock)); /* To be sure */
1200 /* Create a DIB section if there isn't a hdc yet */
1201 if(!This->hDC) {
1202 int extraline = 0;
1203 SYSTEM_INFO sysInfo;
1205 switch (This->bytesPerPixel) {
1206 case 2:
1207 case 4:
1208 /* Allocate extra space to store the RGB bit masks. */
1209 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
1210 break;
1212 case 3:
1213 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
1214 break;
1216 default:
1217 /* Allocate extra space for a palette. */
1218 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1219 sizeof(BITMAPINFOHEADER)
1220 + sizeof(RGBQUAD)
1221 * (1 << (This->bytesPerPixel * 8)));
1222 break;
1225 if (!b_info)
1226 return E_OUTOFMEMORY;
1228 /* Some apps access the surface in via DWORDs, and do not take the necessary care at the end of the
1229 * surface. So we need at least extra 4 bytes at the end of the surface. Check against the page size,
1230 * if the last page used for the surface has at least 4 spare bytes we're safe, otherwise
1231 * add an extra line to the dib section
1233 GetSystemInfo(&sysInfo);
1234 if( ((This->resource.size + 3) % sysInfo.dwPageSize) < 4) {
1235 extraline = 1;
1236 TRACE("Adding an extra line to the dib section\n");
1239 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1240 b_info->bmiHeader.biWidth = This->currentDesc.Width;
1241 b_info->bmiHeader.biHeight = -This->currentDesc.Height -extraline;
1242 b_info->bmiHeader.biSizeImage = ( This->currentDesc.Height + extraline) * IWineD3DSurface_GetPitch(iface);
1243 b_info->bmiHeader.biPlanes = 1;
1244 b_info->bmiHeader.biBitCount = This->bytesPerPixel * 8;
1246 b_info->bmiHeader.biXPelsPerMeter = 0;
1247 b_info->bmiHeader.biYPelsPerMeter = 0;
1248 b_info->bmiHeader.biClrUsed = 0;
1249 b_info->bmiHeader.biClrImportant = 0;
1251 /* Get the bit masks */
1252 masks = (DWORD *) &(b_info->bmiColors);
1253 switch (This->resource.format) {
1254 case WINED3DFMT_R8G8B8:
1255 usage = DIB_RGB_COLORS;
1256 b_info->bmiHeader.biCompression = BI_RGB;
1257 break;
1259 case WINED3DFMT_X1R5G5B5:
1260 case WINED3DFMT_A1R5G5B5:
1261 case WINED3DFMT_A4R4G4B4:
1262 case WINED3DFMT_X4R4G4B4:
1263 case WINED3DFMT_R3G3B2:
1264 case WINED3DFMT_A8R3G3B2:
1265 case WINED3DFMT_A2B10G10R10:
1266 case WINED3DFMT_A8B8G8R8:
1267 case WINED3DFMT_X8B8G8R8:
1268 case WINED3DFMT_A2R10G10B10:
1269 case WINED3DFMT_R5G6B5:
1270 case WINED3DFMT_A16B16G16R16:
1271 usage = 0;
1272 b_info->bmiHeader.biCompression = BI_BITFIELDS;
1273 masks[0] = formatEntry->redMask;
1274 masks[1] = formatEntry->greenMask;
1275 masks[2] = formatEntry->blueMask;
1276 break;
1278 default:
1279 /* Don't know palette */
1280 b_info->bmiHeader.biCompression = BI_RGB;
1281 usage = 0;
1282 break;
1285 ddc = GetDC(0);
1286 if (ddc == 0) {
1287 HeapFree(GetProcessHeap(), 0, b_info);
1288 return HRESULT_FROM_WIN32(GetLastError());
1291 TRACE("Creating a DIB section with size %dx%dx%d, size=%d\n", b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight, b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
1292 This->dib.DIBsection = CreateDIBSection(ddc, b_info, usage, &This->dib.bitmap_data, 0 /* Handle */, 0 /* Offset */);
1293 ReleaseDC(0, ddc);
1295 if (!This->dib.DIBsection) {
1296 ERR("CreateDIBSection failed!\n");
1297 HeapFree(GetProcessHeap(), 0, b_info);
1298 return HRESULT_FROM_WIN32(GetLastError());
1301 TRACE("DIBSection at : %p\n", This->dib.bitmap_data);
1303 /* copy the existing surface to the dib section */
1304 if(This->resource.allocatedMemory) {
1305 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, b_info->bmiHeader.biSizeImage);
1306 /* We won't need that any more */
1307 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
1308 } else {
1309 /* This is to make LockRect read the gl Texture although memory is allocated */
1310 This->Flags &= ~SFLAG_INSYSMEM;
1313 HeapFree(GetProcessHeap(), 0, b_info);
1315 /* Use the dib section from now on */
1316 This->resource.allocatedMemory = This->dib.bitmap_data;
1318 /* Now allocate a HDC */
1319 This->hDC = CreateCompatibleDC(0);
1320 This->dib.holdbitmap = SelectObject(This->hDC, This->dib.DIBsection);
1321 TRACE("using wined3d palette %p\n", This->palette);
1322 SelectPalette(This->hDC,
1323 This->palette ? This->palette->hpal : 0,
1324 FALSE);
1326 This->Flags |= SFLAG_DIBSECTION;
1329 /* Lock the surface */
1330 hr = IWineD3DSurface_LockRect(iface,
1331 &lock,
1332 NULL,
1334 if(FAILED(hr)) {
1335 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1336 /* keep the dib section */
1337 return hr;
1340 if(This->resource.format == WINED3DFMT_P8 ||
1341 This->resource.format == WINED3DFMT_A8P8) {
1342 unsigned int n;
1343 if(This->palette) {
1344 PALETTEENTRY ent[256];
1346 GetPaletteEntries(This->palette->hpal, 0, 256, ent);
1347 for (n=0; n<256; n++) {
1348 col[n].rgbRed = ent[n].peRed;
1349 col[n].rgbGreen = ent[n].peGreen;
1350 col[n].rgbBlue = ent[n].peBlue;
1351 col[n].rgbReserved = 0;
1353 } else {
1354 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1356 for (n=0; n<256; n++) {
1357 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
1358 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
1359 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
1360 col[n].rgbReserved = 0;
1364 SetDIBColorTable(This->hDC, 0, 256, col);
1367 *pHDC = This->hDC;
1368 TRACE("returning %p\n",*pHDC);
1369 This->Flags |= SFLAG_DCINUSE;
1371 return WINED3D_OK;
1374 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1375 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1377 TRACE("(%p)->(%p)\n",This,hDC);
1379 if (!(This->Flags & SFLAG_DCINUSE))
1380 return WINED3DERR_INVALIDCALL;
1382 /* we locked first, so unlock now */
1383 IWineD3DSurface_UnlockRect(iface);
1385 This->Flags &= ~SFLAG_DCINUSE;
1387 return WINED3D_OK;
1390 /* ******************************************************
1391 IWineD3DSurface Internal (No mapping to directx api) parts follow
1392 ****************************************************** */
1394 static HRESULT d3dfmt_get_conv(IWineD3DSurfaceImpl *This, BOOL need_alpha_ck, BOOL use_texturing, GLenum *format, GLenum *internal, GLenum *type, CONVERT_TYPES *convert, int *target_bpp) {
1395 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & DDSD_CKSRCBLT);
1396 const PixelFormatDesc *formatEntry = getFormatDescEntry(This->resource.format);
1398 /* Default values: From the surface */
1399 *format = formatEntry->glFormat;
1400 *internal = formatEntry->glInternal;
1401 *type = formatEntry->glType;
1402 *convert = NO_CONVERSION;
1403 *target_bpp = This->bytesPerPixel;
1405 /* Ok, now look if we have to do any conversion */
1406 switch(This->resource.format) {
1407 case WINED3DFMT_P8:
1408 /* ****************
1409 Paletted Texture
1410 **************** */
1411 /* Use conversion when the paletted texture extension is not available, or when it is available make sure it is used
1412 * for texturing as it won't work for calls like glDraw-/glReadPixels and further also use conversion in case of color keying.
1414 if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) || colorkey_active || (!use_texturing && GL_SUPPORT(EXT_PALETTED_TEXTURE)) ) {
1415 *format = GL_RGBA;
1416 *internal = GL_RGBA;
1417 *type = GL_UNSIGNED_BYTE;
1418 *target_bpp = 4;
1419 if(colorkey_active) {
1420 *convert = CONVERT_PALETTED_CK;
1421 } else {
1422 *convert = CONVERT_PALETTED;
1426 break;
1428 case WINED3DFMT_R3G3B2:
1429 /* **********************
1430 GL_UNSIGNED_BYTE_3_3_2
1431 ********************** */
1432 if (colorkey_active) {
1433 /* This texture format will never be used.. So do not care about color keying
1434 up until the point in time it will be needed :-) */
1435 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1437 break;
1439 case WINED3DFMT_R5G6B5:
1440 if (colorkey_active) {
1441 *convert = CONVERT_CK_565;
1442 *format = GL_RGBA;
1443 *internal = GL_RGBA;
1444 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1446 break;
1448 case WINED3DFMT_R8G8B8:
1449 if (colorkey_active) {
1450 *convert = CONVERT_CK_RGB24;
1451 *format = GL_RGBA;
1452 *internal = GL_RGBA;
1453 *type = GL_UNSIGNED_INT_8_8_8_8;
1454 *target_bpp = 4;
1456 break;
1458 case WINED3DFMT_X8R8G8B8:
1459 if (colorkey_active) {
1460 *convert = CONVERT_RGB32_888;
1461 *format = GL_RGBA;
1462 *internal = GL_RGBA;
1463 *type = GL_UNSIGNED_INT_8_8_8_8;
1465 break;
1467 case WINED3DFMT_V8U8:
1468 /* TODO: GL_ATI_envmap_bumpmap provides suitable formats.
1469 * use it instead of converting
1470 * Remember to adjust the texbem instruction in the shader
1472 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1473 *convert = CONVERT_V8U8;
1474 *format = GL_BGR;
1475 *internal = GL_RGB8;
1476 *type = GL_BYTE;
1477 *target_bpp = 3;
1478 break;
1480 case WINED3DFMT_X8L8V8U8:
1481 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1482 FIXME("Conversion for D3D_X8L8V8U8 not implemented\n");
1483 *format = GL_BGRA;
1484 *internal = GL_RGBA8;
1485 *type = GL_BYTE;
1486 *target_bpp = 4;
1487 break;
1489 case WINED3DFMT_Q8W8V8U8:
1490 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1491 FIXME("Conversion for D3D_Q8W8V8U8 not implemented\n");
1492 *format = GL_BGRA;
1493 *internal = GL_RGBA8;
1494 *type = GL_BYTE;
1495 *target_bpp = 4;
1496 break;
1498 case WINED3DFMT_V16U16:
1499 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1500 FIXME("Conversion for D3D_V16U16 not implemented\n");
1501 *format = GL_COLOR_INDEX;
1502 *internal = GL_COLOR_INDEX;
1503 *type = GL_SHORT;
1504 *target_bpp = 4;
1505 break;
1507 default:
1508 break;
1511 return WINED3D_OK;
1514 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf) {
1515 BYTE *source, *dest;
1516 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surf);
1518 switch (convert) {
1519 case NO_CONVERSION:
1521 memcpy(dst, src, pitch * height);
1522 break;
1524 case CONVERT_PALETTED:
1525 case CONVERT_PALETTED_CK:
1527 IWineD3DPaletteImpl* pal = surf->palette;
1528 BYTE table[256][4];
1529 unsigned int i;
1530 unsigned int x, y;
1532 if( pal == NULL) {
1533 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1536 if (pal == NULL) {
1537 /* Still no palette? Use the device's palette */
1538 /* Get the surface's palette */
1539 for (i = 0; i < 256; i++) {
1540 IWineD3DDeviceImpl *device = surf->resource.wineD3DDevice;
1542 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1543 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1544 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1545 if ((convert == CONVERT_PALETTED_CK) &&
1546 (i >= surf->SrcBltCKey.dwColorSpaceLowValue) &&
1547 (i <= surf->SrcBltCKey.dwColorSpaceHighValue)) {
1548 /* We should maybe here put a more 'neutral' color than the standard bright purple
1549 one often used by application to prevent the nice purple borders when bi-linear
1550 filtering is on */
1551 table[i][3] = 0x00;
1552 } else {
1553 table[i][3] = 0xFF;
1556 } else {
1557 TRACE("Using surface palette %p\n", pal);
1558 /* Get the surface's palette */
1559 for (i = 0; i < 256; i++) {
1560 table[i][0] = pal->palents[i].peRed;
1561 table[i][1] = pal->palents[i].peGreen;
1562 table[i][2] = pal->palents[i].peBlue;
1563 if ((convert == CONVERT_PALETTED_CK) &&
1564 (i >= surf->SrcBltCKey.dwColorSpaceLowValue) &&
1565 (i <= surf->SrcBltCKey.dwColorSpaceHighValue)) {
1566 /* We should maybe here put a more 'neutral' color than the standard bright purple
1567 one often used by application to prevent the nice purple borders when bi-linear
1568 filtering is on */
1569 table[i][3] = 0x00;
1570 } else {
1571 table[i][3] = 0xFF;
1576 for (y = 0; y < height; y++)
1578 source = src + pitch * y;
1579 dest = dst + outpitch * y;
1580 /* This is an 1 bpp format, using the width here is fine */
1581 for (x = 0; x < width; x++) {
1582 BYTE color = *source++;
1583 *dest++ = table[color][0];
1584 *dest++ = table[color][1];
1585 *dest++ = table[color][2];
1586 *dest++ = table[color][3];
1590 break;
1592 case CONVERT_CK_565:
1594 /* Converting the 565 format in 5551 packed to emulate color-keying.
1596 Note : in all these conversion, it would be best to average the averaging
1597 pixels to get the color of the pixel that will be color-keyed to
1598 prevent 'color bleeding'. This will be done later on if ever it is
1599 too visible.
1601 Note2: Nvidia documents say that their driver does not support alpha + color keying
1602 on the same surface and disables color keying in such a case
1604 unsigned int x, y;
1605 WORD *Source;
1606 WORD *Dest;
1608 TRACE("Color keyed 565\n");
1610 for (y = 0; y < height; y++) {
1611 Source = (WORD *) (src + y * pitch);
1612 Dest = (WORD *) (dst + y * outpitch);
1613 for (x = 0; x < width; x++ ) {
1614 WORD color = *Source++;
1615 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1616 if ((color < surf->SrcBltCKey.dwColorSpaceLowValue) ||
1617 (color > surf->SrcBltCKey.dwColorSpaceHighValue)) {
1618 *Dest |= 0x0001;
1620 Dest++;
1624 break;
1626 case CONVERT_V8U8:
1628 unsigned int x, y;
1629 short *Source;
1630 unsigned char *Dest;
1631 for(y = 0; y < height; y++) {
1632 Source = (short *) (src + y * pitch);
1633 Dest = (unsigned char *) (dst + y * outpitch);
1634 for (x = 0; x < width; x++ ) {
1635 long color = (*Source++);
1636 /* B */ Dest[0] = 0xff;
1637 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1638 /* R */ Dest[2] = (color) + 128; /* U */
1639 Dest += 3;
1642 break;
1645 default:
1646 ERR("Unsupported conversation type %d\n", convert);
1648 return WINED3D_OK;
1651 /* This function is used in case of 8bit paletted textures to upload the palette.
1652 For now it only supports GL_EXT_paletted_texture extension but support for other
1653 extensions like ARB_fragment_program and ATI_fragment_shaders will be added as well.
1655 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
1656 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1657 IWineD3DPaletteImpl* pal = This->palette;
1658 BYTE table[256][4];
1659 int i;
1661 if (pal == NULL) {
1662 /* Still no palette? Use the device's palette */
1663 /* Get the surface's palette */
1664 for (i = 0; i < 256; i++) {
1665 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1667 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1668 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1669 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1670 if ((convert == CONVERT_PALETTED_CK) &&
1671 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1672 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1673 /* We should maybe here put a more 'neutral' color than the standard bright purple
1674 one often used by application to prevent the nice purple borders when bi-linear
1675 filtering is on */
1676 table[i][3] = 0x00;
1677 } else {
1678 table[i][3] = 0xFF;
1681 } else {
1682 TRACE("Using surface palette %p\n", pal);
1683 /* Get the surface's palette */
1684 for (i = 0; i < 256; i++) {
1685 table[i][0] = pal->palents[i].peRed;
1686 table[i][1] = pal->palents[i].peGreen;
1687 table[i][2] = pal->palents[i].peBlue;
1688 if ((convert == CONVERT_PALETTED_CK) &&
1689 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1690 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1691 /* We should maybe here put a more 'neutral' color than the standard bright purple
1692 one often used by application to prevent the nice purple borders when bi-linear
1693 filtering is on */
1694 table[i][3] = 0x00;
1695 } else {
1696 table[i][3] = 0xFF;
1700 GL_EXTCALL(glColorTableEXT(GL_TEXTURE_2D,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
1703 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
1704 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1706 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
1707 /* If a ddraw-style palette is attached assume no d3d9 palette change.
1708 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
1710 return FALSE;
1713 if(This->palette9) {
1714 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
1715 return FALSE;
1717 } else {
1718 This->palette9 = (PALETTEENTRY *) HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
1720 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
1721 return TRUE;
1724 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface) {
1725 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1726 GLenum format, internal, type;
1727 CONVERT_TYPES convert;
1728 int bpp;
1729 int width, pitch, outpitch;
1730 BYTE *mem;
1732 if (!(This->Flags & SFLAG_INTEXTURE)) {
1733 TRACE("Reloading because surface is dirty\n");
1734 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
1735 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & DDSD_CKSRCBLT))) ||
1736 /* Reload: vice versa OR */
1737 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & DDSD_CKSRCBLT)) ||
1738 /* Also reload: Color key is active AND the color key has changed */
1739 ((This->CKeyFlags & DDSD_CKSRCBLT) && (
1740 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
1741 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
1742 TRACE("Reloading because of color keying\n");
1743 } else if(palette9_changed(This)) {
1744 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
1745 } else {
1746 TRACE("surface is already in texture\n");
1747 return WINED3D_OK;
1750 This->Flags |= SFLAG_INTEXTURE;
1752 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
1753 * These resources are not bound by device size or format restrictions. Because of this,
1754 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
1755 * However, these resources can always be created, locked, and copied.
1757 if (This->resource.pool == WINED3DPOOL_SCRATCH )
1759 FIXME("(%p) Operation not supported for scratch textures\n",This);
1760 return WINED3DERR_INVALIDCALL;
1763 if (This->Flags & SFLAG_INDRAWABLE) {
1764 if (This->glDescription.level != 0)
1765 FIXME("Surface in texture is only supported for level 0\n");
1766 else if (This->resource.format == WINED3DFMT_P8 || This->resource.format == WINED3DFMT_A8P8 ||
1767 This->resource.format == WINED3DFMT_DXT1 || This->resource.format == WINED3DFMT_DXT2 ||
1768 This->resource.format == WINED3DFMT_DXT3 || This->resource.format == WINED3DFMT_DXT4 ||
1769 This->resource.format == WINED3DFMT_DXT5)
1770 FIXME("Format %d not supported\n", This->resource.format);
1771 else {
1772 GLint prevRead;
1774 ENTER_GL();
1775 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1776 vcheckGLcall("glGetIntegerv");
1777 glReadBuffer(This->resource.wineD3DDevice->offscreenBuffer);
1778 vcheckGLcall("glReadBuffer");
1780 glCopyTexImage2D(This->glDescription.target,
1781 This->glDescription.level,
1782 This->glDescription.glFormatInternal,
1785 This->currentDesc.Width,
1786 This->currentDesc.Height,
1789 checkGLcall("glCopyTexImage2D");
1790 glReadBuffer(prevRead);
1791 vcheckGLcall("glReadBuffer");
1793 LEAVE_GL();
1795 TRACE("Updated target %d\n", This->glDescription.target);
1797 return WINED3D_OK;
1799 /* Otherwise: System memory copy must be most up to date */
1801 if(This->CKeyFlags & DDSD_CKSRCBLT) {
1802 This->Flags |= SFLAG_GLCKEY;
1803 This->glCKey = This->SrcBltCKey;
1805 else This->Flags &= ~SFLAG_GLCKEY;
1807 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp);
1809 /* The width is in 'length' not in bytes */
1810 width = This->currentDesc.Width;
1811 pitch = IWineD3DSurface_GetPitch(iface);
1813 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
1814 int height = This->currentDesc.Height;
1816 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
1817 outpitch = width * bpp;
1818 outpitch = (outpitch + SURFACE_ALIGNMENT - 1) & ~(SURFACE_ALIGNMENT - 1);
1820 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
1821 if(!mem) {
1822 ERR("Out of memory %d, %d!\n", outpitch, height);
1823 return WINED3DERR_OUTOFVIDEOMEMORY;
1825 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
1827 This->Flags |= SFLAG_CONVERTED;
1828 } else if (This->resource.format == WINED3DFMT_P8 && GL_SUPPORT(EXT_PALETTED_TEXTURE)) {
1829 d3dfmt_p8_upload_palette(iface, convert);
1830 This->Flags &= ~SFLAG_CONVERTED;
1831 mem = This->resource.allocatedMemory;
1832 } else {
1833 This->Flags &= ~SFLAG_CONVERTED;
1834 mem = This->resource.allocatedMemory;
1837 /* Make sure the correct pitch is used */
1838 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
1840 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
1841 TRACE("non power of two support\n");
1842 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
1843 if (mem) {
1844 surface_upload_data(This, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
1846 } else {
1847 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
1848 if (mem) {
1849 surface_upload_data(This, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
1853 /* Restore the default pitch */
1854 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1856 if (mem != This->resource.allocatedMemory)
1857 HeapFree(GetProcessHeap(), 0, mem);
1859 #if 0
1861 static unsigned int gen = 0;
1862 char buffer[4096];
1863 ++gen;
1864 if ((gen % 10) == 0) {
1865 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
1866 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
1869 * debugging crash code
1870 if (gen == 250) {
1871 void** test = NULL;
1872 *test = 0;
1876 #endif
1878 if (!(This->Flags & SFLAG_DONOTFREE)) {
1879 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
1880 This->resource.allocatedMemory = NULL;
1881 This->Flags &= ~SFLAG_INSYSMEM;
1884 return WINED3D_OK;
1887 #include <errno.h>
1888 #include <stdio.h>
1889 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
1890 FILE* f = NULL;
1891 UINT i, y;
1892 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1893 char *allocatedMemory;
1894 char *textureRow;
1895 IWineD3DSwapChain *swapChain = NULL;
1896 int width, height;
1897 GLuint tmpTexture = 0;
1898 DWORD color;
1899 /*FIXME:
1900 Textures my not be stored in ->allocatedgMemory and a GlTexture
1901 so we should lock the surface before saving a snapshot, or at least check that
1903 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
1904 by calling GetTexImage and in compressed form by calling
1905 GetCompressedTexImageARB. Queried compressed images can be saved and
1906 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
1907 texture images do not need to be processed by the GL and should
1908 significantly improve texture loading performance relative to uncompressed
1909 images. */
1911 /* Setup the width and height to be the internal texture width and height. */
1912 width = This->pow2Width;
1913 height = This->pow2Height;
1914 /* check to see if were a 'virtual' texture e.g. were not a pbuffer of texture were a back buffer*/
1915 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
1917 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
1918 /* if were not a real texture then read the back buffer into a real texture */
1919 /* we don't want to interfere with the back buffer so read the data into a temporary
1920 * texture and then save the data out of the temporary texture
1922 GLint prevRead;
1923 ENTER_GL();
1924 TRACE("(%p) Reading render target into texture\n", This);
1925 glEnable(GL_TEXTURE_2D);
1927 glGenTextures(1, &tmpTexture);
1928 glBindTexture(GL_TEXTURE_2D, tmpTexture);
1930 glTexImage2D(GL_TEXTURE_2D,
1932 GL_RGBA,
1933 width,
1934 height,
1935 0/*border*/,
1936 GL_RGBA,
1937 GL_UNSIGNED_INT_8_8_8_8_REV,
1938 NULL);
1940 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1941 vcheckGLcall("glGetIntegerv");
1942 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
1943 vcheckGLcall("glReadBuffer");
1944 glCopyTexImage2D(GL_TEXTURE_2D,
1946 GL_RGBA,
1949 width,
1950 height,
1953 checkGLcall("glCopyTexImage2D");
1954 glReadBuffer(prevRead);
1955 LEAVE_GL();
1957 } else { /* bind the real texture, and make sure it up to date */
1958 IWineD3DSurface_PreLoad(iface);
1960 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
1961 ENTER_GL();
1962 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
1963 glGetTexImage(GL_TEXTURE_2D,
1964 This->glDescription.level,
1965 GL_RGBA,
1966 GL_UNSIGNED_INT_8_8_8_8_REV,
1967 allocatedMemory);
1968 checkGLcall("glTexImage2D");
1969 if (tmpTexture) {
1970 glBindTexture(GL_TEXTURE_2D, 0);
1971 glDeleteTextures(1, &tmpTexture);
1973 LEAVE_GL();
1975 f = fopen(filename, "w+");
1976 if (NULL == f) {
1977 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
1978 return WINED3DERR_INVALIDCALL;
1980 /* Save the dat out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha chanel*/
1981 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
1982 /* TGA header */
1983 fputc(0,f);
1984 fputc(0,f);
1985 fputc(2,f);
1986 fputc(0,f);
1987 fputc(0,f);
1988 fputc(0,f);
1989 fputc(0,f);
1990 fputc(0,f);
1991 fputc(0,f);
1992 fputc(0,f);
1993 fputc(0,f);
1994 fputc(0,f);
1995 /* short width*/
1996 fwrite(&width,2,1,f);
1997 /* short height */
1998 fwrite(&height,2,1,f);
1999 /* format rgba */
2000 fputc(0x20,f);
2001 fputc(0x28,f);
2002 /* raw data */
2003 /* if the data is upside down if we've fetched it from a back buffer, so it needs flipping again to make it the correct way up*/
2004 if(swapChain)
2005 textureRow = allocatedMemory + (width * (height - 1) *4);
2006 else
2007 textureRow = allocatedMemory;
2008 for (y = 0 ; y < height; y++) {
2009 for (i = 0; i < width; i++) {
2010 color = *((DWORD*)textureRow);
2011 fputc((color >> 16) & 0xFF, f); /* B */
2012 fputc((color >> 8) & 0xFF, f); /* G */
2013 fputc((color >> 0) & 0xFF, f); /* R */
2014 fputc((color >> 24) & 0xFF, f); /* A */
2015 textureRow += 4;
2017 /* take two rows of the pointer to the texture memory */
2018 if(swapChain)
2019 (textureRow-= width << 3);
2022 TRACE("Closing file\n");
2023 fclose(f);
2025 if(swapChain) {
2026 IWineD3DSwapChain_Release(swapChain);
2028 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2029 return WINED3D_OK;
2033 * Slightly inefficient way to handle multiple dirty rects but it works :)
2035 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2036 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2037 IWineD3DBaseTexture *baseTexture = NULL;
2038 This->Flags &= ~(SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
2039 if (NULL != pDirtyRect) {
2040 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2041 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2042 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2043 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2044 } else {
2045 This->dirtyRect.left = 0;
2046 This->dirtyRect.top = 0;
2047 This->dirtyRect.right = This->currentDesc.Width;
2048 This->dirtyRect.bottom = This->currentDesc.Height;
2050 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2051 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2052 /* if the container is a basetexture then mark it dirty. */
2053 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2054 TRACE("Passing to conatiner\n");
2055 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2056 IWineD3DBaseTexture_Release(baseTexture);
2058 return WINED3D_OK;
2061 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
2062 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2064 TRACE("This %p, container %p\n", This, container);
2066 /* We can't keep a reference to the container, since the container already keeps a reference to us. */
2068 TRACE("Setting container to %p from %p\n", container, This->container);
2069 This->container = container;
2071 return WINED3D_OK;
2074 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2075 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2076 const PixelFormatDesc *formatEntry = getFormatDescEntry(format);
2078 if (This->resource.format != WINED3DFMT_UNKNOWN) {
2079 FIXME("(%p) : The foramt of the surface must be WINED3DFORMAT_UNKNOWN\n", This);
2080 return WINED3DERR_INVALIDCALL;
2083 TRACE("(%p) : Setting texture foramt to (%d,%s)\n", This, format, debug_d3dformat(format));
2084 if (format == WINED3DFMT_UNKNOWN) {
2085 This->resource.size = 0;
2086 } else if (format == WINED3DFMT_DXT1) {
2087 /* DXT1 is half byte per pixel */
2088 This->resource.size = ((max(This->pow2Width, 4) * formatEntry->bpp) * max(This->pow2Height, 4)) >> 1;
2090 } else if (format == WINED3DFMT_DXT2 || format == WINED3DFMT_DXT3 ||
2091 format == WINED3DFMT_DXT4 || format == WINED3DFMT_DXT5) {
2092 This->resource.size = ((max(This->pow2Width, 4) * formatEntry->bpp) * max(This->pow2Height, 4));
2093 } else {
2094 This->resource.size = ((This->pow2Width * formatEntry->bpp) + SURFACE_ALIGNMENT - 1) & ~(SURFACE_ALIGNMENT - 1);
2095 This->resource.size *= This->pow2Height;
2099 /* Setup some glformat defaults */
2100 This->glDescription.glFormat = formatEntry->glFormat;
2101 This->glDescription.glFormatInternal = formatEntry->glInternal;
2102 This->glDescription.glType = formatEntry->glType;
2104 if (format != WINED3DFMT_UNKNOWN) {
2105 This->bytesPerPixel = formatEntry->bpp;
2106 } else {
2107 This->bytesPerPixel = 0;
2110 This->Flags |= (WINED3DFMT_D16_LOCKABLE == format) ? SFLAG_LOCKABLE : 0;
2112 This->resource.format = format;
2114 TRACE("(%p) : Size %d, bytesPerPixel %d, glFormat %d, glFotmatInternal %d, glType %d\n", This, This->resource.size, This->bytesPerPixel, This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2116 return WINED3D_OK;
2119 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2120 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2122 /* Render targets depend on their hdc, and we can't create a hdc on a user pointer */
2123 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
2124 ERR("Not supported on render targets\n");
2125 return WINED3DERR_INVALIDCALL;
2128 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2129 WARN("Surface is locked or the HDC is in use\n");
2130 return WINED3DERR_INVALIDCALL;
2133 if(Mem && Mem != This->resource.allocatedMemory) {
2135 /* Do I have to copy the old surface content? */
2136 if(This->Flags & SFLAG_DIBSECTION) {
2137 /* Release the DC. No need to hold the critical section for the update
2138 * Thread because this thread runs only on front buffers, but this method
2139 * fails for render targets in the check above.
2141 SelectObject(This->hDC, This->dib.holdbitmap);
2142 DeleteDC(This->hDC);
2143 /* Release the DIB section */
2144 DeleteObject(This->dib.DIBsection);
2145 This->dib.bitmap_data = NULL;
2146 This->resource.allocatedMemory = NULL;
2147 This->hDC = NULL;
2148 This->Flags &= ~SFLAG_DIBSECTION;
2149 } else if(!(This->Flags & SFLAG_USERPTR)) {
2150 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
2152 This->resource.allocatedMemory = Mem;
2153 This->Flags |= SFLAG_USERPTR;
2154 } else if(This->Flags & SFLAG_USERPTR) {
2155 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2156 This->resource.allocatedMemory = NULL;
2157 This->Flags &= ~SFLAG_USERPTR;
2159 return WINED3D_OK;
2162 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2163 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2164 IWineD3DDevice *D3D = (IWineD3DDevice *) This->resource.wineD3DDevice;
2165 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2167 /* Flipping is only supported on RenderTargets */
2168 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return DDERR_NOTFLIPPABLE;
2170 if(override) {
2171 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2172 * FIXME("(%p) Target override is not supported by now\n", This);
2173 * Additionally, it isn't really possible to support triple-buffering
2174 * properly on opengl at all
2178 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2179 return IWineD3DDevice_Present(D3D, NULL, NULL, 0, NULL);
2182 /* Does a direct frame buffer -> texture copy. Stretching is done
2183 * with single pixel copy calls
2185 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown) {
2186 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2187 float xrel, yrel;
2188 UINT row;
2189 BOOL warned = FALSE; /* deliberately not static */
2190 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2192 ENTER_GL();
2194 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2196 /* Bind the target texture */
2197 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
2198 checkGLcall("glBindTexture");
2199 if(!swapchain) {
2200 glReadBuffer(myDevice->offscreenBuffer);
2201 } else if(swapchain->backBuffer && SrcSurface == swapchain->backBuffer[0]) {
2202 glReadBuffer(GL_BACK);
2203 } else {
2204 glReadBuffer(GL_FRONT);
2206 checkGLcall("glReadBuffer");
2208 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2209 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2211 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2212 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2215 if(upsidedown &&
2216 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2217 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2218 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2220 glCopyTexSubImage2D(This->glDescription.target,
2221 This->glDescription.level,
2222 drect->x1, drect->y1, /* xoffset, yoffset */
2223 srect->x1, Src->currentDesc.Height - srect->y2,
2224 drect->x2 - drect->x1, drect->y2 - drect->y1);
2225 } else {
2226 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2227 /* I have to process this row by row to swap the image,
2228 * otherwise it would be upside down, so streching in y direction
2229 * doesn't cost extra time
2231 * However, streching in x direction can be avoided if not necessary
2233 for(row = drect->y1; row < drect->y2; row++) {
2234 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2235 /* Well, that stuff works, but it's very slow.
2236 * find a better way instead
2238 UINT col;
2240 if(!warned) {
2241 warned = TRUE;
2242 FIXME("Doing a pixel by pixel render target -> texture copy, expect performance issues\n");
2245 for(col = drect->x1; col < drect->x2; col++) {
2246 glCopyTexSubImage2D(This->glDescription.target,
2247 This->glDescription.level,
2248 drect->x1 + col, row, /* xoffset, yoffset */
2249 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2250 1, 1);
2252 } else {
2253 glCopyTexSubImage2D(This->glDescription.target,
2254 This->glDescription.level,
2255 drect->x1, row, /* xoffset, yoffset */
2256 srect->x1, yoffset - (int) (row * yrel),
2257 drect->x2-drect->x1, 1);
2262 vcheckGLcall("glCopyTexSubImage2D");
2263 LEAVE_GL();
2266 /* Uses the hardware to stretch and flip the image */
2267 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown) {
2268 GLuint src, backup = 0;
2269 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2270 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2271 float left, right, top, bottom; /* Texture coordinates */
2272 UINT fbwidth = Src->currentDesc.Width;
2273 UINT fbheight = Src->currentDesc.Height;
2274 GLenum drawBuffer = GL_BACK;
2276 TRACE("Using hwstretch blit\n");
2277 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2278 ENTER_GL();
2279 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2281 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2282 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2284 if(GL_LIMITS(aux_buffers) >= 2) {
2285 /* Got more than one aux buffer? Use the 2nd aux buffer */
2286 drawBuffer = GL_AUX1;
2287 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2288 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2289 drawBuffer = GL_AUX0;
2292 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2293 glGenTextures(1, &backup);
2294 checkGLcall("glGenTextures\n");
2295 glBindTexture(GL_TEXTURE_2D, backup);
2296 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2297 } else {
2298 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2299 * we are reading from the back buffer, the backup can be used as source texture
2301 if(Src->glDescription.textureName == 0) {
2302 /* Get it a description */
2303 IWineD3DSurface_PreLoad(SrcSurface);
2305 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
2306 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2308 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2309 Src->Flags &= ~SFLAG_INTEXTURE;
2312 glReadBuffer(GL_BACK);
2313 checkGLcall("glReadBuffer(GL_BACK)");
2315 /* TODO: Only back up the part that will be overwritten */
2316 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2317 0, 0 /* read offsets */,
2318 0, 0,
2319 fbwidth,
2320 fbheight);
2322 checkGLcall("glCopyTexSubImage2D");
2324 /* No issue with overriding these - the sampler is dirty due to blit usage */
2325 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2326 checkGLcall("glTexParameteri");
2327 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2328 checkGLcall("glTexParameteri");
2330 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2331 src = backup ? backup : Src->glDescription.textureName;
2332 } else {
2333 glReadBuffer(GL_FRONT);
2334 checkGLcall("glReadBuffer(GL_FRONT)");
2336 glGenTextures(1, &src);
2337 checkGLcall("glGenTextures(1, &src)");
2338 glBindTexture(GL_TEXTURE_2D, src);
2339 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2341 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2342 * out for power of 2 sizes
2344 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2345 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2346 checkGLcall("glTexImage2D");
2347 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2348 0, 0 /* read offsets */,
2349 0, 0,
2350 fbwidth,
2351 fbheight);
2353 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2354 checkGLcall("glTexParameteri");
2355 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2356 checkGLcall("glTexParameteri");
2358 glReadBuffer(GL_BACK);
2359 checkGLcall("glReadBuffer(GL_BACK)");
2361 checkGLcall("glEnd and previous");
2363 left = (float) srect->x1 / (float) Src->pow2Width;
2364 right = (float) srect->x2 / (float) Src->pow2Width;
2366 if(upsidedown) {
2367 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2368 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2369 } else {
2370 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2371 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2374 /* draw the source texture stretched and upside down. The correct surface is bound already */
2375 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
2376 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
2378 glDrawBuffer(drawBuffer);
2379 glReadBuffer(drawBuffer);
2381 glBegin(GL_QUADS);
2382 /* bottom left */
2383 glTexCoord2f(left, bottom);
2384 glVertex2i(0, fbheight);
2386 /* top left */
2387 glTexCoord2f(left, top);
2388 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2390 /* top right */
2391 glTexCoord2f(right, top);
2392 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2394 /* bottom right */
2395 glTexCoord2f(right, bottom);
2396 glVertex2i(drect->x2 - drect->x1, fbheight);
2397 glEnd();
2398 checkGLcall("glEnd and previous");
2400 /* Now read the stretched and upside down image into the destination texture */
2401 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2402 checkGLcall("glBindTexture");
2403 glCopyTexSubImage2D(This->glDescription.target,
2405 drect->x1, drect->y1, /* xoffset, yoffset */
2406 0, 0, /* We blitted the image to the origin */
2407 drect->x2 - drect->x1, drect->y2 - drect->y1);
2408 checkGLcall("glCopyTexSubImage2D");
2410 /* Write the back buffer backup back */
2411 glBindTexture(GL_TEXTURE_2D, backup ? backup : Src->glDescription.textureName);
2412 checkGLcall("glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName)");
2414 if(drawBuffer == GL_BACK) {
2415 glBegin(GL_QUADS);
2416 /* top left */
2417 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2418 glVertex2i(0, 0);
2420 /* bottom left */
2421 glTexCoord2f(0.0, 0.0);
2422 glVertex2i(0, fbheight);
2424 /* bottom right */
2425 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2426 glVertex2i(fbwidth, Src->currentDesc.Height);
2428 /* top right */
2429 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2430 glVertex2i(fbwidth, 0);
2431 glEnd();
2432 } else {
2433 /* Restore the old draw buffer */
2434 glDrawBuffer(GL_BACK);
2437 /* Cleanup */
2438 if(src != Src->glDescription.textureName && src != backup) {
2439 glDeleteTextures(1, &src);
2440 checkGLcall("glDeleteTextures(1, &src)");
2442 if(backup) {
2443 glDeleteTextures(1, &backup);
2444 checkGLcall("glDeleteTextures(1, &backup)");
2446 LEAVE_GL();
2449 /* Not called from the VTable */
2450 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, DDBLTFX *DDBltFx) {
2451 WINED3DRECT rect;
2452 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2453 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2454 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2455 BOOL SrcOK = TRUE;
2457 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2459 /* Get the swapchain. One of the surfaces has to be a primary surface */
2460 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2461 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2462 if(Src) {
2463 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2464 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2467 /* Early sort out of cases where no render target is used */
2468 if(!dstSwapchain && !srcSwapchain &&
2469 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2470 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2471 return WINED3DERR_INVALIDCALL;
2474 /* No destination color keying supported */
2475 if(Flags & (DDBLT_KEYDEST | DDBLT_KEYDESTOVERRIDE)) {
2476 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2477 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2478 return WINED3DERR_INVALIDCALL;
2481 if (DestRect) {
2482 rect.x1 = DestRect->left;
2483 rect.y1 = DestRect->top;
2484 rect.x2 = DestRect->right;
2485 rect.y2 = DestRect->bottom;
2486 } else {
2487 rect.x1 = 0;
2488 rect.y1 = 0;
2489 rect.x2 = This->currentDesc.Width;
2490 rect.y2 = This->currentDesc.Height;
2493 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2494 if(dstSwapchain && dstSwapchain == srcSwapchain) {
2495 /* Half-life does a Blt from the back buffer to the front buffer,
2496 * Full surface size, no flags... Use present instead
2499 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2500 if( SrcRect ) {
2501 if( (SrcRect->left == 0) && (SrcRect->top == 0) &&
2502 (SrcRect->right == Src->currentDesc.Width) && (SrcRect->bottom == Src->currentDesc.Height) ) {
2503 SrcOK = TRUE;
2505 } else {
2506 SrcOK = TRUE;
2509 /* Check the Destination rect and the surface sizes */
2510 if(SrcOK &&
2511 (rect.x1 == 0) && (rect.y1 == 0) &&
2512 (rect.x2 == This->currentDesc.Width) && (rect.y2 == This->currentDesc.Height) &&
2513 (This->currentDesc.Width == Src->currentDesc.Width) &&
2514 (This->currentDesc.Height == Src->currentDesc.Height)) {
2515 /* These flags are unimportant for the flag check, remove them */
2517 if((Flags & ~(DDBLT_DONOTWAIT | DDBLT_WAIT)) == 0) {
2518 if( dstSwapchain->backBuffer && ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) &&
2519 SrcSurface == dstSwapchain->backBuffer[0] ) {
2521 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
2523 /* The idea behind this is that a glReadPixels and a glDrawPixels call
2524 * take very long, while a flip is fast.
2525 * This applies to Half-Life, which does such Blts every time it finished
2526 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
2527 * menu. This is also used by all apps when they do windowed rendering
2529 * The problem is that flipping is not really the same as copying. After a
2530 * Blt the front buffer is a copy of the back buffer, and the back buffer is
2531 * untouched. Therefore it's necessary to override the swap effect
2532 * and to set it back after the flip.
2535 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
2537 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
2538 IWineD3DDevice_Present((IWineD3DDevice *) This->resource.wineD3DDevice,
2539 NULL, NULL, 0, NULL);
2541 dstSwapchain->presentParms.SwapEffect = orig_swap;
2543 return WINED3D_OK;
2548 TRACE("Unsupported blit between buffers on the same swapchain\n");
2549 return WINED3DERR_INVALIDCALL;
2550 } else if((dstSwapchain || This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) &&
2551 (srcSwapchain || SrcSurface == myDevice->render_targets[0]) ) {
2552 ERR("Can't perform hardware blit between 2 different swapchains, falling back to software\n");
2553 return WINED3DERR_INVALIDCALL;
2556 if(srcSwapchain || SrcSurface == myDevice->render_targets[0]) {
2557 /* Blit from render target to texture */
2558 WINED3DRECT srect;
2559 BOOL upsideDown, stretchx;
2561 if(Flags & (DDBLT_KEYSRC | DDBLT_KEYSRCOVERRIDE)) {
2562 TRACE("Color keying not supported by frame buffer to texture blit\n");
2563 return WINED3DERR_INVALIDCALL;
2564 /* Destination color key is checked above */
2567 /* Call preload for the surface to make sure it isn't dirty */
2568 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2570 /* Make sure that the top pixel is always above the bottom pixel, and keep a seperate upside down flag
2571 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2573 if(SrcRect) {
2574 if(SrcRect->top < SrcRect->bottom) {
2575 srect.y1 = SrcRect->top;
2576 srect.y2 = SrcRect->bottom;
2577 upsideDown = FALSE;
2578 } else {
2579 srect.y1 = SrcRect->bottom;
2580 srect.y2 = SrcRect->top;
2581 upsideDown = TRUE;
2583 srect.x1 = SrcRect->left;
2584 srect.x2 = SrcRect->right;
2585 } else {
2586 srect.x1 = 0;
2587 srect.y1 = 0;
2588 srect.x2 = Src->currentDesc.Width;
2589 srect.y2 = Src->currentDesc.Height;
2590 upsideDown = FALSE;
2592 if(rect.x1 > rect.x2) {
2593 UINT tmp = rect.x2;
2594 rect.x2 = rect.x1;
2595 rect.x1 = tmp;
2596 upsideDown = !upsideDown;
2598 if(!srcSwapchain) {
2599 TRACE("Reading from an offscreen target\n");
2600 upsideDown = !upsideDown;
2603 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
2604 stretchx = TRUE;
2605 } else {
2606 stretchx = FALSE;
2609 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2610 * flip the image nor scale it. If GL_EXT_framebuffer_blit is available it can be used(hopefully,
2611 * not implemented by now). Otherwise:
2613 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2614 * -> If the app wants a image width an unscaled width, copy it line per line
2615 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
2616 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2617 * back buffer. This is slower than reading line per line, thus not used for flipping
2618 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2619 * pixel by pixel
2621 if(FALSE /* GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) */) {
2622 TRACE("Using GL_EXT_framebuffer_blit for copying\n");
2623 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
2624 rect.y2 - rect.y1 > Src->currentDesc.Height) {
2625 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
2626 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown);
2627 } else {
2628 TRACE("Using hardware stretching to flip / stretch the texture\n");
2629 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown);
2632 if(!(This->Flags & SFLAG_DONOTFREE)) {
2633 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
2634 This->resource.allocatedMemory = NULL;
2635 } else {
2636 This->Flags &= ~SFLAG_INSYSMEM;
2638 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
2639 * path is never entered
2641 This->Flags |= SFLAG_INTEXTURE;
2643 return WINED3D_OK;
2644 } else if(Src) {
2645 /* Blit from offscreen surface to render target */
2646 float glTexCoord[4];
2647 DWORD oldCKeyFlags = Src->CKeyFlags;
2648 DDCOLORKEY oldBltCKey = This->SrcBltCKey;
2649 RECT SourceRectangle;
2651 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
2653 if(SrcRect) {
2654 SourceRectangle.left = SrcRect->left;
2655 SourceRectangle.right = SrcRect->right;
2656 SourceRectangle.top = SrcRect->top;
2657 SourceRectangle.bottom = SrcRect->bottom;
2658 } else {
2659 SourceRectangle.left = 0;
2660 SourceRectangle.right = Src->currentDesc.Width;
2661 SourceRectangle.top = 0;
2662 SourceRectangle.bottom = Src->currentDesc.Height;
2665 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
2666 /* Fall back to software */
2667 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
2668 SourceRectangle.left, SourceRectangle.top,
2669 SourceRectangle.right, SourceRectangle.bottom);
2670 return WINED3DERR_INVALIDCALL;
2673 /* Color keying: Check if we have to do a color keyed blt,
2674 * and if not check if a color key is activated.
2676 * Just modify the color keying parameters in the surface and restore them afterwards
2677 * The surface keeps track of the color key last used to load the opengl surface.
2678 * PreLoad will catch the change to the flags and color key and reload if neccessary.
2680 if(Flags & DDBLT_KEYSRC) {
2681 /* Use color key from surface */
2682 } else if(Flags & DDBLT_KEYSRCOVERRIDE) {
2683 /* Use color key from DDBltFx */
2684 Src->CKeyFlags |= DDSD_CKSRCBLT;
2685 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
2686 } else {
2687 /* Do not use color key */
2688 Src->CKeyFlags &= ~DDSD_CKSRCBLT;
2691 /* Now load the surface */
2692 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
2694 ENTER_GL();
2696 /* Activate the destination context, set it up for blitting */
2697 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
2699 if(!dstSwapchain) {
2700 TRACE("Drawing to offscreen buffer\n");
2701 glDrawBuffer(myDevice->offscreenBuffer);
2702 } else if(This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer) {
2703 TRACE("Drawing to front buffer\n");
2704 glDrawBuffer(GL_FRONT);
2705 checkGLcall("glDrawBuffer GL_FRONT");
2706 } else {
2707 TRACE("Drawing to back buffer\n");
2708 glDrawBuffer(GL_BACK);
2709 checkGLcall("glDrawBuffer GL_BACK");
2712 /* Bind the texture */
2713 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
2714 checkGLcall("glBindTexture");
2716 /* No filtering for blts */
2717 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
2718 GL_NEAREST);
2719 checkGLcall("glTexParameteri");
2720 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
2721 GL_NEAREST);
2722 checkGLcall("glTexParameteri");
2723 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
2724 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
2725 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2726 checkGLcall("glTexEnvi");
2728 /* This is for color keying */
2729 if(Flags & (DDBLT_KEYSRC | DDBLT_KEYSRCOVERRIDE)) {
2730 glEnable(GL_ALPHA_TEST);
2731 checkGLcall("glEnable GL_ALPHA_TEST");
2732 glAlphaFunc(GL_NOTEQUAL, 0.0);
2733 checkGLcall("glAlphaFunc\n");
2734 } else {
2735 glDisable(GL_ALPHA_TEST);
2736 checkGLcall("glDisable GL_ALPHA_TEST");
2739 /* Draw a textured quad
2741 glBegin(GL_QUADS);
2743 glColor3d(1.0f, 1.0f, 1.0f);
2744 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
2745 glVertex3f(rect.x1,
2746 rect.y1,
2747 0.0);
2749 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
2750 glVertex3f(rect.x1, rect.y2, 0.0);
2752 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
2753 glVertex3f(rect.x2,
2754 rect.y2,
2755 0.0);
2757 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
2758 glVertex3f(rect.x2,
2759 rect.y1,
2760 0.0);
2761 glEnd();
2762 checkGLcall("glEnd");
2764 if(Flags & (DDBLT_KEYSRC | DDBLT_KEYSRCOVERRIDE)) {
2765 glDisable(GL_ALPHA_TEST);
2766 checkGLcall("glDisable(GL_ALPHA_TEST)");
2769 /* Unbind the texture */
2770 glBindTexture(GL_TEXTURE_2D, 0);
2771 checkGLcall("glEnable glBindTexture");
2773 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
2774 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
2776 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
2777 glDrawBuffer(GL_BACK);
2779 /* Restore the color key parameters */
2780 Src->CKeyFlags = oldCKeyFlags;
2781 This->SrcBltCKey = oldBltCKey;
2783 LEAVE_GL();
2785 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
2786 This->Flags &= ~SFLAG_INSYSMEM;
2787 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
2788 * is outdated now
2790 if(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO) {
2791 This->Flags |= SFLAG_INDRAWABLE;
2792 This->Flags &= ~SFLAG_INTEXTURE;
2793 } else {
2794 This->Flags |= SFLAG_INTEXTURE;
2797 return WINED3D_OK;
2798 } else {
2799 /* Source-Less Blit to render target */
2800 if (Flags & DDBLT_COLORFILL) {
2801 /* This is easy to handle for the D3D Device... */
2802 DWORD color;
2804 TRACE("Colorfill\n");
2806 /* The color as given in the Blt function is in the format of the frame-buffer...
2807 * 'clear' expect it in ARGB format => we need to do some conversion :-)
2809 if (This->resource.format == WINED3DFMT_P8) {
2810 if (This->palette) {
2811 color = ((0xFF000000) |
2812 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
2813 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
2814 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
2815 } else {
2816 color = 0xFF000000;
2819 else if (This->resource.format == WINED3DFMT_R5G6B5) {
2820 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
2821 color = 0xFFFFFFFF;
2822 } else {
2823 color = ((0xFF000000) |
2824 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
2825 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
2826 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
2829 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
2830 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
2831 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
2833 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
2834 color = DDBltFx->u5.dwFillColor;
2836 else {
2837 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
2838 return WINED3DERR_INVALIDCALL;
2841 TRACE("Calling GetSwapChain with mydevice = %p\n", myDevice);
2842 if(dstSwapchain && dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]) {
2843 glDrawBuffer(GL_BACK);
2844 checkGLcall("glDrawBuffer(GL_BACK)");
2845 } else if (dstSwapchain && This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer) {
2846 glDrawBuffer(GL_FRONT);
2847 checkGLcall("glDrawBuffer(GL_FRONT)");
2848 } else if(This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2849 glDrawBuffer(myDevice->offscreenBuffer);
2850 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer3)");
2851 } else {
2852 TRACE("Surface is higher back buffer, falling back to software\n");
2853 return WINED3DERR_INVALIDCALL;
2856 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
2858 IWineD3DDevice_Clear( (IWineD3DDevice *) myDevice,
2859 1 /* Number of rectangles */,
2860 &rect,
2861 WINED3DCLEAR_TARGET,
2862 color,
2863 0.0 /* Z */,
2864 0 /* Stencil */);
2866 /* Restore the original draw buffer */
2867 if(!dstSwapchain) {
2868 glDrawBuffer(myDevice->offscreenBuffer);
2869 } else if(dstSwapchain->backBuffer && dstSwapchain->backBuffer[0]) {
2870 glDrawBuffer(GL_BACK);
2872 vcheckGLcall("glDrawBuffer");
2874 return WINED3D_OK;
2878 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
2879 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
2880 return WINED3DERR_INVALIDCALL;
2883 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, DDBLTFX *DDBltFx) {
2884 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2885 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2886 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2887 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2888 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
2890 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair */
2891 if(myDevice->inScene &&
2892 (iface == myDevice->stencilBufferTarget ||
2893 (SrcSurface && SrcSurface == myDevice->stencilBufferTarget))) {
2894 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
2895 return WINED3DERR_INVALIDCALL;
2898 /* Special cases for RenderTargets */
2899 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
2900 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
2901 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) return WINED3D_OK;
2904 /* For the rest call the X11 surface implementation.
2905 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
2906 * other Blts are rather rare
2908 return IWineGDISurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2911 HRESULT WINAPI IWineD3DSurfaceImpl_GetBltStatus(IWineD3DSurface *iface, DWORD Flags) {
2912 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2913 TRACE("(%p)->(%x)\n", This, Flags);
2915 switch (Flags)
2917 case DDGBS_CANBLT:
2918 case DDGBS_ISBLTDONE:
2919 return DD_OK;
2921 default:
2922 return DDERR_INVALIDPARAMS;
2926 HRESULT WINAPI IWineD3DSurfaceImpl_GetFlipStatus(IWineD3DSurface *iface, DWORD Flags) {
2927 /* XXX: DDERR_INVALIDSURFACETYPE */
2929 TRACE("(%p)->(%08x)\n",iface,Flags);
2930 switch (Flags) {
2931 case DDGFS_CANFLIP:
2932 case DDGFS_ISFLIPDONE:
2933 return DD_OK;
2935 default:
2936 return DDERR_INVALIDPARAMS;
2940 HRESULT WINAPI IWineD3DSurfaceImpl_IsLost(IWineD3DSurface *iface) {
2941 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2942 TRACE("(%p)\n", This);
2944 return This->Flags & SFLAG_LOST ? DDERR_SURFACELOST : WINED3D_OK;
2947 HRESULT WINAPI IWineD3DSurfaceImpl_Restore(IWineD3DSurface *iface) {
2948 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2949 TRACE("(%p)\n", This);
2951 /* So far we don't lose anything :) */
2952 This->Flags &= ~SFLAG_LOST;
2953 return WINED3D_OK;
2956 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
2957 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2958 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
2959 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2960 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
2962 if(myDevice->inScene &&
2963 (iface == myDevice->stencilBufferTarget ||
2964 (Source && Source == myDevice->stencilBufferTarget))) {
2965 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
2966 return WINED3DERR_INVALIDCALL;
2969 /* Special cases for RenderTargets */
2970 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
2971 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
2973 RECT SrcRect, DstRect;
2974 DWORD Flags=0;
2976 if(rsrc) {
2977 SrcRect.left = rsrc->left;
2978 SrcRect.top= rsrc->top;
2979 SrcRect.bottom = rsrc->bottom;
2980 SrcRect.right = rsrc->right;
2981 } else {
2982 SrcRect.left = 0;
2983 SrcRect.top = 0;
2984 SrcRect.right = srcImpl->currentDesc.Width;
2985 SrcRect.bottom = srcImpl->currentDesc.Height;
2988 DstRect.left = dstx;
2989 DstRect.top=dsty;
2990 DstRect.right = dstx + SrcRect.right - SrcRect.left;
2991 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
2993 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
2994 if(trans & DDBLTFAST_SRCCOLORKEY)
2995 Flags |= DDBLT_KEYSRC;
2996 if(trans & DDBLTFAST_DESTCOLORKEY)
2997 Flags |= DDBLT_KEYDEST;
2998 if(trans & DDBLTFAST_WAIT)
2999 Flags |= DDBLT_WAIT;
3000 if(trans & DDBLTFAST_DONOTWAIT)
3001 Flags |= DDBLT_DONOTWAIT;
3003 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL) == WINED3D_OK) return WINED3D_OK;
3007 return IWineGDISurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3010 HRESULT WINAPI IWineD3DSurfaceImpl_GetPalette(IWineD3DSurface *iface, IWineD3DPalette **Pal) {
3011 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3012 TRACE("(%p)->(%p)\n", This, Pal);
3014 *Pal = (IWineD3DPalette *) This->palette;
3015 return DD_OK;
3018 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
3019 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3020 RGBQUAD col[256];
3021 IWineD3DPaletteImpl *pal = This->palette;
3022 unsigned int n;
3023 TRACE("(%p)\n", This);
3025 if(This->resource.format == WINED3DFMT_P8 ||
3026 This->resource.format == WINED3DFMT_A8P8)
3028 if(!This->Flags & SFLAG_INSYSMEM) {
3029 FIXME("Palette changed with surface that does not have an up to date system memory copy\n");
3031 TRACE("Dirtifying surface\n");
3032 This->Flags &= ~(SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3035 if(This->Flags & SFLAG_DIBSECTION) {
3036 TRACE("(%p): Updating the hdc's palette\n", This);
3037 for (n=0; n<256; n++) {
3038 if(pal) {
3039 col[n].rgbRed = pal->palents[n].peRed;
3040 col[n].rgbGreen = pal->palents[n].peGreen;
3041 col[n].rgbBlue = pal->palents[n].peBlue;
3042 } else {
3043 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3044 /* Use the default device palette */
3045 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
3046 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
3047 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
3049 col[n].rgbReserved = 0;
3051 SetDIBColorTable(This->hDC, 0, 256, col);
3054 return WINED3D_OK;
3057 HRESULT WINAPI IWineD3DSurfaceImpl_SetPalette(IWineD3DSurface *iface, IWineD3DPalette *Pal) {
3058 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3059 IWineD3DPaletteImpl *PalImpl = (IWineD3DPaletteImpl *) Pal;
3060 TRACE("(%p)->(%p)\n", This, Pal);
3062 if(This->palette != NULL)
3063 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3064 This->palette->Flags &= ~DDPCAPS_PRIMARYSURFACE;
3066 if(PalImpl != NULL) {
3067 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3068 /* Set the device's main palette if the palette
3069 * wasn't a primary palette before
3071 if(!(PalImpl->Flags & DDPCAPS_PRIMARYSURFACE)) {
3072 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3073 unsigned int i;
3075 for(i=0; i < 256; i++) {
3076 device->palettes[device->currentPalette][i] = PalImpl->palents[i];
3080 (PalImpl)->Flags |= DDPCAPS_PRIMARYSURFACE;
3083 This->palette = PalImpl;
3085 return IWineD3DSurface_RealizePalette(iface);
3088 HRESULT WINAPI IWineD3DSurfaceImpl_SetColorKey(IWineD3DSurface *iface, DWORD Flags, DDCOLORKEY *CKey) {
3089 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3090 TRACE("(%p)->(%08x,%p)\n", This, Flags, CKey);
3092 if ((Flags & DDCKEY_COLORSPACE) != 0) {
3093 FIXME(" colorkey value not supported (%08x) !\n", Flags);
3094 return DDERR_INVALIDPARAMS;
3097 /* Dirtify the surface, but only if a key was changed */
3098 if(CKey) {
3099 switch (Flags & ~DDCKEY_COLORSPACE) {
3100 case DDCKEY_DESTBLT:
3101 This->DestBltCKey = *CKey;
3102 This->CKeyFlags |= DDSD_CKDESTBLT;
3103 break;
3105 case DDCKEY_DESTOVERLAY:
3106 This->DestOverlayCKey = *CKey;
3107 This->CKeyFlags |= DDSD_CKDESTOVERLAY;
3108 break;
3110 case DDCKEY_SRCOVERLAY:
3111 This->SrcOverlayCKey = *CKey;
3112 This->CKeyFlags |= DDSD_CKSRCOVERLAY;
3113 break;
3115 case DDCKEY_SRCBLT:
3116 This->SrcBltCKey = *CKey;
3117 This->CKeyFlags |= DDSD_CKSRCBLT;
3118 break;
3121 else {
3122 switch (Flags & ~DDCKEY_COLORSPACE) {
3123 case DDCKEY_DESTBLT:
3124 This->CKeyFlags &= ~DDSD_CKDESTBLT;
3125 break;
3127 case DDCKEY_DESTOVERLAY:
3128 This->CKeyFlags &= ~DDSD_CKDESTOVERLAY;
3129 break;
3131 case DDCKEY_SRCOVERLAY:
3132 This->CKeyFlags &= ~DDSD_CKSRCOVERLAY;
3133 break;
3135 case DDCKEY_SRCBLT:
3136 This->CKeyFlags &= ~DDSD_CKSRCBLT;
3137 break;
3141 return WINED3D_OK;
3144 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3145 /** Check against the maximum texture sizes supported by the video card **/
3146 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3148 TRACE("%p\n", This);
3149 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3150 /* one of three options
3151 1: Do the same as we do with nonpow 2 and scale the texture, (any texture ops would require the texture to be scaled which is potentially slow)
3152 2: Set the texture to the maxium size (bad idea)
3153 3: WARN and return WINED3DERR_NOTAVAILABLE;
3154 4: Create the surface, but allow it to be used only for DirectDraw Blts. Some apps(e.g. Swat 3) create textures with a Height of 16 and a Width > 3000 and blt 16x16 letter areas from them to the render target.
3156 WARN("(%p) Creating an oversized surface\n", This);
3157 This->Flags |= SFLAG_OVERSIZE;
3159 /* This will be initialized on the first blt */
3160 This->glRect.left = 0;
3161 This->glRect.top = 0;
3162 This->glRect.right = 0;
3163 This->glRect.bottom = 0;
3164 } else {
3165 /* No oversize, gl rect is the full texture size */
3166 This->Flags &= ~SFLAG_OVERSIZE;
3167 This->glRect.left = 0;
3168 This->glRect.top = 0;
3169 This->glRect.right = This->pow2Width;
3170 This->glRect.bottom = This->pow2Height;
3173 return WINED3D_OK;
3176 DWORD WINAPI IWineD3DSurfaceImpl_GetPitch(IWineD3DSurface *iface) {
3177 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3178 DWORD ret;
3179 TRACE("(%p)\n", This);
3181 /* DXTn formats don't have exact pitches as they are to the new row of blocks,
3182 where each block is 4x4 pixels, 8 bytes (dxt1) and 16 bytes (dxt2/3/4/5)
3183 ie pitch = (width/4) * bytes per block */
3184 if (This->resource.format == WINED3DFMT_DXT1) /* DXT1 is 8 bytes per block */
3185 ret = ((This->currentDesc.Width + 3) >> 2) << 3;
3186 else if (This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
3187 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) /* DXT2/3/4/5 is 16 bytes per block */
3188 ret = ((This->currentDesc.Width + 3) >> 2) << 4;
3189 else {
3190 ret = This->bytesPerPixel * This->currentDesc.Width; /* Bytes / row */
3191 /* Surfaces are 32 bit aligned */
3192 ret = (ret + SURFACE_ALIGNMENT - 1) & ~(SURFACE_ALIGNMENT - 1);
3194 TRACE("(%p) Returning %d\n", This, ret);
3195 return ret;
3198 HRESULT WINAPI IWineD3DSurfaceImpl_SetOverlayPosition(IWineD3DSurface *iface, LONG X, LONG Y) {
3199 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3201 FIXME("(%p)->(%d,%d) Stub!\n", This, X, Y);
3203 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3205 TRACE("(%p): Not an overlay surface\n", This);
3206 return DDERR_NOTAOVERLAYSURFACE;
3209 return WINED3D_OK;
3212 HRESULT WINAPI IWineD3DSurfaceImpl_GetOverlayPosition(IWineD3DSurface *iface, LONG *X, LONG *Y) {
3213 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3215 FIXME("(%p)->(%p,%p) Stub!\n", This, X, Y);
3217 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3219 TRACE("(%p): Not an overlay surface\n", This);
3220 return DDERR_NOTAOVERLAYSURFACE;
3223 return WINED3D_OK;
3226 HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlayZOrder(IWineD3DSurface *iface, DWORD Flags, IWineD3DSurface *Ref) {
3227 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3228 IWineD3DSurfaceImpl *RefImpl = (IWineD3DSurfaceImpl *) Ref;
3230 FIXME("(%p)->(%08x,%p) Stub!\n", This, Flags, RefImpl);
3232 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3234 TRACE("(%p): Not an overlay surface\n", This);
3235 return DDERR_NOTAOVERLAYSURFACE;
3238 return WINED3D_OK;
3241 HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlay(IWineD3DSurface *iface, RECT *SrcRect, IWineD3DSurface *DstSurface, RECT *DstRect, DWORD Flags, WINEDDOVERLAYFX *FX) {
3242 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3243 IWineD3DSurfaceImpl *Dst = (IWineD3DSurfaceImpl *) DstSurface;
3244 FIXME("(%p)->(%p, %p, %p, %08x, %p)\n", This, SrcRect, Dst, DstRect, Flags, FX);
3246 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3248 TRACE("(%p): Not an overlay surface\n", This);
3249 return DDERR_NOTAOVERLAYSURFACE;
3252 return WINED3D_OK;
3255 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
3257 /* IUnknown */
3258 IWineD3DSurfaceImpl_QueryInterface,
3259 IWineD3DSurfaceImpl_AddRef,
3260 IWineD3DSurfaceImpl_Release,
3261 /* IWineD3DResource */
3262 IWineD3DSurfaceImpl_GetParent,
3263 IWineD3DSurfaceImpl_GetDevice,
3264 IWineD3DSurfaceImpl_SetPrivateData,
3265 IWineD3DSurfaceImpl_GetPrivateData,
3266 IWineD3DSurfaceImpl_FreePrivateData,
3267 IWineD3DSurfaceImpl_SetPriority,
3268 IWineD3DSurfaceImpl_GetPriority,
3269 IWineD3DSurfaceImpl_PreLoad,
3270 IWineD3DSurfaceImpl_GetType,
3271 /* IWineD3DSurface */
3272 IWineD3DSurfaceImpl_GetContainer,
3273 IWineD3DSurfaceImpl_GetDesc,
3274 IWineD3DSurfaceImpl_LockRect,
3275 IWineD3DSurfaceImpl_UnlockRect,
3276 IWineD3DSurfaceImpl_GetDC,
3277 IWineD3DSurfaceImpl_ReleaseDC,
3278 IWineD3DSurfaceImpl_Flip,
3279 IWineD3DSurfaceImpl_Blt,
3280 IWineD3DSurfaceImpl_GetBltStatus,
3281 IWineD3DSurfaceImpl_GetFlipStatus,
3282 IWineD3DSurfaceImpl_IsLost,
3283 IWineD3DSurfaceImpl_Restore,
3284 IWineD3DSurfaceImpl_BltFast,
3285 IWineD3DSurfaceImpl_GetPalette,
3286 IWineD3DSurfaceImpl_SetPalette,
3287 IWineD3DSurfaceImpl_RealizePalette,
3288 IWineD3DSurfaceImpl_SetColorKey,
3289 IWineD3DSurfaceImpl_GetPitch,
3290 IWineD3DSurfaceImpl_SetMem,
3291 IWineD3DSurfaceImpl_SetOverlayPosition,
3292 IWineD3DSurfaceImpl_GetOverlayPosition,
3293 IWineD3DSurfaceImpl_UpdateOverlayZOrder,
3294 IWineD3DSurfaceImpl_UpdateOverlay,
3295 /* Internal use: */
3296 IWineD3DSurfaceImpl_AddDirtyRect,
3297 IWineD3DSurfaceImpl_LoadTexture,
3298 IWineD3DSurfaceImpl_SaveSnapshot,
3299 IWineD3DSurfaceImpl_SetContainer,
3300 IWineD3DSurfaceImpl_SetGlTextureDesc,
3301 IWineD3DSurfaceImpl_GetGlDesc,
3302 IWineD3DSurfaceImpl_GetData,
3303 IWineD3DSurfaceImpl_SetFormat,
3304 IWineD3DSurfaceImpl_PrivateSetup