push 599a9d3db769aad8dabfd10a59120f719b00f4ee
[wine/hacks.git] / dlls / wined3d / surface.c
blob2abb277db330d79edd50b6b39a03b88dff2c5ae0
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
11 * Copyright 2007 Henri Verbeet
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 #include "config.h"
29 #include "wine/port.h"
30 #include "wined3d_private.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
33 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
35 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf);
37 static void surface_download_data(IWineD3DSurfaceImpl *This) {
38 if (!This->resource.allocatedMemory) This->resource.allocatedMemory = HeapAlloc(GetProcessHeap(), 0, This->resource.size + 4);
39 if (This->resource.format == WINED3DFMT_DXT1 ||
40 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
41 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
42 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
43 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
44 } else {
45 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
46 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
48 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
49 checkGLcall("glGetCompressedTexImageARB()");
51 } else {
52 void *mem;
53 int src_pitch = 0;
54 int dst_pitch = 0;
56 if(This->Flags & SFLAG_CONVERTED) {
57 FIXME("Read back converted textures unsupported\n");
58 return;
61 if (This->Flags & SFLAG_NONPOW2) {
62 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
63 src_pitch = This->bytesPerPixel * This->pow2Width;
64 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
65 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
66 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
67 } else {
68 mem = This->resource.allocatedMemory;
71 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
72 This->glDescription.glFormat, This->glDescription.glType, mem);
74 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
75 This->glDescription.glType, mem);
76 checkGLcall("glGetTexImage()");
78 if (This->Flags & SFLAG_NONPOW2) {
79 LPBYTE src_data, dst_data;
80 int y;
82 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
83 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
84 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
86 * We're doing this...
88 * instead of boxing the texture :
89 * |<-texture width ->| -->pow2width| /\
90 * |111111111111111111| | |
91 * |222 Texture 222222| boxed empty | texture height
92 * |3333 Data 33333333| | |
93 * |444444444444444444| | \/
94 * ----------------------------------- |
95 * | boxed empty | boxed empty | pow2height
96 * | | | \/
97 * -----------------------------------
100 * we're repacking the data to the expected texture width
102 * |<-texture width ->| -->pow2width| /\
103 * |111111111111111111222222222222222| |
104 * |222333333333333333333444444444444| texture height
105 * |444444 | |
106 * | | \/
107 * | | |
108 * | empty | pow2height
109 * | | \/
110 * -----------------------------------
112 * == is the same as
114 * |<-texture width ->| /\
115 * |111111111111111111|
116 * |222222222222222222|texture height
117 * |333333333333333333|
118 * |444444444444444444| \/
119 * --------------------
121 * this also means that any references to allocatedMemory should work with the data as if were a
122 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
124 * internally the texture is still stored in a boxed format so any references to textureName will
125 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
127 * Performance should not be an issue, because applications normally do not lock the surfaces when
128 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
129 * and doesn't have to be re-read.
131 src_data = mem;
132 dst_data = This->resource.allocatedMemory;
133 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
134 for (y = 1 ; y < This->currentDesc.Height; y++) {
135 /* skip the first row */
136 src_data += src_pitch;
137 dst_data += dst_pitch;
138 memcpy(dst_data, src_data, dst_pitch);
141 HeapFree(GetProcessHeap(), 0, mem);
144 /* Surface has now been downloaded */
145 This->Flags |= SFLAG_INSYSMEM;
148 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
149 if (This->resource.format == WINED3DFMT_DXT1 ||
150 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
151 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
152 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
153 FIXME("Using DXT1/3/5 without advertized support\n");
154 } else {
155 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
156 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
157 This->Flags |= SFLAG_CLIENT;
160 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
161 ENTER_GL();
162 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
163 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
164 * function uses glCompressedTexImage2D instead of the SubImage call
166 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
167 width, height, 0 /* border */, This->resource.size, data));
168 checkGLcall("glCompressedTexSubImage2D");
169 LEAVE_GL();
171 } else {
172 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
173 ENTER_GL();
174 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
175 checkGLcall("glTexSubImage2D");
176 LEAVE_GL();
180 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
181 BOOL enable_client_storage = FALSE;
183 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,
184 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
186 if (This->resource.format == WINED3DFMT_DXT1 ||
187 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
188 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
189 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
190 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
191 return;
194 ENTER_GL();
196 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
197 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
198 /* In some cases we want to disable client storage.
199 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
200 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
201 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
202 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
203 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
205 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
206 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
207 This->Flags &= SFLAG_CLIENT;
208 enable_client_storage = TRUE;
209 } else {
210 This->Flags |= SFLAG_CLIENT;
211 /* Below point opengl to our allocated texture memory */
214 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type,
215 This->Flags & SFLAG_CLIENT ? This->resource.allocatedMemory : NULL);
216 checkGLcall("glTexImage2D");
218 if(enable_client_storage) {
219 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
220 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
222 LEAVE_GL();
224 This->Flags |= SFLAG_ALLOCATED;
227 /* In D3D the depth stencil dimensions have to be greater than or equal to the
228 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
229 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
230 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
231 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
232 renderbuffer_entry_t *entry;
233 GLuint renderbuffer = 0;
234 unsigned int src_width, src_height;
236 src_width = This->pow2Width;
237 src_height = This->pow2Height;
239 /* A depth stencil smaller than the render target is not valid */
240 if (width > src_width || height > src_height) return;
242 /* Remove any renderbuffer set if the sizes match */
243 if (width == src_width && height == src_height) {
244 This->current_renderbuffer = NULL;
245 return;
248 /* Look if we've already got a renderbuffer of the correct dimensions */
249 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
250 if (entry->width == width && entry->height == height) {
251 renderbuffer = entry->id;
252 This->current_renderbuffer = entry;
253 break;
257 if (!renderbuffer) {
258 const GlPixelFormatDesc *glDesc;
259 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
261 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
262 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
263 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, glDesc->glFormat, width, height));
265 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
266 entry->width = width;
267 entry->height = height;
268 entry->id = renderbuffer;
269 list_add_head(&This->renderbuffers, &entry->entry);
271 This->current_renderbuffer = entry;
274 checkGLcall("set_compatible_renderbuffer");
277 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
278 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
279 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
281 TRACE("(%p) : swapchain %p\n", This, swapchain);
283 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
284 TRACE("Returning GL_BACK\n");
285 return GL_BACK;
286 } else if (swapchain_impl->frontBuffer == iface) {
287 TRACE("Returning GL_FRONT\n");
288 return GL_FRONT;
291 FIXME("Higher back buffer, returning GL_BACK\n");
292 return GL_BACK;
295 /* *******************************************
296 IWineD3DSurface IUnknown parts follow
297 ******************************************* */
298 HRESULT WINAPI IWineD3DSurfaceImpl_QueryInterface(IWineD3DSurface *iface, REFIID riid, LPVOID *ppobj)
300 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
301 /* Warn ,but be nice about things */
302 TRACE("(%p)->(%s,%p)\n", This,debugstr_guid(riid),ppobj);
304 if (IsEqualGUID(riid, &IID_IUnknown)
305 || IsEqualGUID(riid, &IID_IWineD3DBase)
306 || IsEqualGUID(riid, &IID_IWineD3DResource)
307 || IsEqualGUID(riid, &IID_IWineD3DSurface)) {
308 IUnknown_AddRef((IUnknown*)iface);
309 *ppobj = This;
310 return S_OK;
312 *ppobj = NULL;
313 return E_NOINTERFACE;
316 ULONG WINAPI IWineD3DSurfaceImpl_AddRef(IWineD3DSurface *iface) {
317 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
318 ULONG ref = InterlockedIncrement(&This->resource.ref);
319 TRACE("(%p) : AddRef increasing from %d\n", This,ref - 1);
320 return ref;
323 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
324 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
325 ULONG ref = InterlockedDecrement(&This->resource.ref);
326 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
327 if (ref == 0) {
328 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) This->resource.wineD3DDevice;
329 renderbuffer_entry_t *entry, *entry2;
330 TRACE("(%p) : cleaning up\n", This);
332 if(iface == device->lastActiveRenderTarget) {
333 IWineD3DSwapChainImpl *swapchain = device->swapchains ? (IWineD3DSwapChainImpl *) device->swapchains[0] : NULL;
335 TRACE("Last active render target destroyed\n");
336 /* Find a replacement surface for the currently active back buffer. The context manager does not do NULL
337 * checks, so switch to a valid target as long as the currently set surface is still valid. Use the
338 * surface of the implicit swpchain. If that is the same as the destroyed surface the device is destroyed
339 * and the lastActiveRenderTarget member shouldn't matter
341 if(swapchain) {
342 if(swapchain->backBuffer && swapchain->backBuffer[0] != iface) {
343 TRACE("Activating primary back buffer\n");
344 ActivateContext(device, swapchain->backBuffer[0], CTXUSAGE_RESOURCELOAD);
345 } else if(!swapchain->backBuffer && swapchain->frontBuffer != iface) {
346 /* Single buffering environment */
347 TRACE("Activating primary front buffer\n");
348 ActivateContext(device, swapchain->frontBuffer, CTXUSAGE_RESOURCELOAD);
349 } else {
350 TRACE("Device is being destroyed, setting lastActiveRenderTarget = 0xdeadbabe\n");
351 /* Implicit render target destroyed, that means the device is being destroyed
352 * whatever we set here, it shouldn't matter
354 device->lastActiveRenderTarget = (IWineD3DSurface *) 0xdeadbabe;
356 } else {
357 /* May happen during ddraw uninitialization */
358 TRACE("Render target set, but swapchain does not exist!\n");
359 device->lastActiveRenderTarget = (IWineD3DSurface *) 0xdeadcafe;
363 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
365 /* Need a context to destroy the texture. Use the currently active render target, but only if
366 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
367 * When destroying the primary rt, Uninit3D will activate a context before doing anything
369 if(device->render_targets[0]) {
370 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
373 TRACE("Deleting texture %d\n", This->glDescription.textureName);
374 ENTER_GL();
375 glDeleteTextures(1, &This->glDescription.textureName);
376 LEAVE_GL();
379 if(This->Flags & SFLAG_DIBSECTION) {
380 /* Release the DC */
381 SelectObject(This->hDC, This->dib.holdbitmap);
382 DeleteDC(This->hDC);
383 /* Release the DIB section */
384 DeleteObject(This->dib.DIBsection);
385 This->dib.bitmap_data = NULL;
386 This->resource.allocatedMemory = NULL;
388 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
390 HeapFree(GetProcessHeap(), 0, This->palette9);
392 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
393 if(iface == device->ddraw_primary)
394 device->ddraw_primary = NULL;
396 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
397 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
398 HeapFree(GetProcessHeap(), 0, entry);
401 TRACE("(%p) Released\n", This);
402 HeapFree(GetProcessHeap(), 0, This);
405 return ref;
408 /* ****************************************************
409 IWineD3DSurface IWineD3DResource parts follow
410 **************************************************** */
411 HRESULT WINAPI IWineD3DSurfaceImpl_GetDevice(IWineD3DSurface *iface, IWineD3DDevice** ppDevice) {
412 return IWineD3DResourceImpl_GetDevice((IWineD3DResource *)iface, ppDevice);
415 HRESULT WINAPI IWineD3DSurfaceImpl_SetPrivateData(IWineD3DSurface *iface, REFGUID refguid, CONST void* pData, DWORD SizeOfData, DWORD Flags) {
416 return IWineD3DResourceImpl_SetPrivateData((IWineD3DResource *)iface, refguid, pData, SizeOfData, Flags);
419 HRESULT WINAPI IWineD3DSurfaceImpl_GetPrivateData(IWineD3DSurface *iface, REFGUID refguid, void* pData, DWORD* pSizeOfData) {
420 return IWineD3DResourceImpl_GetPrivateData((IWineD3DResource *)iface, refguid, pData, pSizeOfData);
423 HRESULT WINAPI IWineD3DSurfaceImpl_FreePrivateData(IWineD3DSurface *iface, REFGUID refguid) {
424 return IWineD3DResourceImpl_FreePrivateData((IWineD3DResource *)iface, refguid);
427 DWORD WINAPI IWineD3DSurfaceImpl_SetPriority(IWineD3DSurface *iface, DWORD PriorityNew) {
428 return IWineD3DResourceImpl_SetPriority((IWineD3DResource *)iface, PriorityNew);
431 DWORD WINAPI IWineD3DSurfaceImpl_GetPriority(IWineD3DSurface *iface) {
432 return IWineD3DResourceImpl_GetPriority((IWineD3DResource *)iface);
435 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
436 /* TODO: check for locks */
437 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
438 IWineD3DBaseTexture *baseTexture = NULL;
439 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
441 TRACE("(%p)Checking to see if the container is a base texture\n", This);
442 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
443 TRACE("Passing to container\n");
444 IWineD3DBaseTexture_PreLoad(baseTexture);
445 IWineD3DBaseTexture_Release(baseTexture);
446 } else {
447 TRACE("(%p) : About to load surface\n", This);
449 if(!device->isInDraw) {
450 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
453 ENTER_GL();
454 glEnable(This->glDescription.target);/* make sure texture support is enabled in this context */
455 if (!This->glDescription.level) {
456 if (!This->glDescription.textureName) {
457 glGenTextures(1, &This->glDescription.textureName);
458 checkGLcall("glGenTextures");
459 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
461 glBindTexture(This->glDescription.target, This->glDescription.textureName);
462 checkGLcall("glBindTexture");
463 IWineD3DSurface_LoadTexture(iface, FALSE);
464 /* This is where we should be reducing the amount of GLMemoryUsed */
465 } else if (This->glDescription.textureName) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
466 /* assume this is a coding error not a real error for now */
467 FIXME("Mipmap surface has a glTexture bound to it!\n");
469 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
470 /* Tell opengl to try and keep this texture in video ram (well mostly) */
471 GLclampf tmp;
472 tmp = 0.9f;
473 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
475 LEAVE_GL();
477 return;
480 WINED3DRESOURCETYPE WINAPI IWineD3DSurfaceImpl_GetType(IWineD3DSurface *iface) {
481 TRACE("(%p) : calling resourceimpl_GetType\n", iface);
482 return IWineD3DResourceImpl_GetType((IWineD3DResource *)iface);
485 HRESULT WINAPI IWineD3DSurfaceImpl_GetParent(IWineD3DSurface *iface, IUnknown **pParent) {
486 TRACE("(%p) : calling resourceimpl_GetParent\n", iface);
487 return IWineD3DResourceImpl_GetParent((IWineD3DResource *)iface, pParent);
490 /* ******************************************************
491 IWineD3DSurface IWineD3DSurface parts follow
492 ****************************************************** */
494 HRESULT WINAPI IWineD3DSurfaceImpl_GetContainer(IWineD3DSurface* iface, REFIID riid, void** ppContainer) {
495 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
496 IWineD3DBase *container = 0;
498 TRACE("(This %p, riid %s, ppContainer %p)\n", This, debugstr_guid(riid), ppContainer);
500 if (!ppContainer) {
501 ERR("Called without a valid ppContainer.\n");
504 /** From MSDN:
505 * If the surface is created using CreateImageSurface/CreateOffscreenPlainSurface, CreateRenderTarget,
506 * or CreateDepthStencilSurface, the surface is considered stand alone. In this case,
507 * GetContainer will return the Direct3D device used to create the surface.
509 if (This->container) {
510 container = This->container;
511 } else {
512 container = (IWineD3DBase *)This->resource.wineD3DDevice;
515 TRACE("Relaying to QueryInterface\n");
516 return IUnknown_QueryInterface(container, riid, ppContainer);
519 HRESULT WINAPI IWineD3DSurfaceImpl_GetDesc(IWineD3DSurface *iface, WINED3DSURFACE_DESC *pDesc) {
520 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
522 TRACE("(%p) : copying into %p\n", This, pDesc);
523 if(pDesc->Format != NULL) *(pDesc->Format) = This->resource.format;
524 if(pDesc->Type != NULL) *(pDesc->Type) = This->resource.resourceType;
525 if(pDesc->Usage != NULL) *(pDesc->Usage) = This->resource.usage;
526 if(pDesc->Pool != NULL) *(pDesc->Pool) = This->resource.pool;
527 if(pDesc->Size != NULL) *(pDesc->Size) = This->resource.size; /* dx8 only */
528 if(pDesc->MultiSampleType != NULL) *(pDesc->MultiSampleType) = This->currentDesc.MultiSampleType;
529 if(pDesc->MultiSampleQuality != NULL) *(pDesc->MultiSampleQuality) = This->currentDesc.MultiSampleQuality;
530 if(pDesc->Width != NULL) *(pDesc->Width) = This->currentDesc.Width;
531 if(pDesc->Height != NULL) *(pDesc->Height) = This->currentDesc.Height;
532 return WINED3D_OK;
535 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
536 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
537 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
538 if (This->glDescription.textureName == 0 && textureName != 0) {
539 This->Flags &= ~SFLAG_INTEXTURE;
540 IWineD3DSurface_AddDirtyRect(iface, NULL);
542 This->glDescription.textureName = textureName;
543 This->glDescription.target = target;
544 This->Flags &= ~SFLAG_ALLOCATED;
547 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
548 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
549 TRACE("(%p) : returning %p\n", This, &This->glDescription);
550 *glDescription = &This->glDescription;
553 /* TODO: think about moving this down to resource? */
554 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
555 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
556 /* 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 future */
557 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
558 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
560 return (CONST void*)(This->resource.allocatedMemory);
563 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch, BOOL srcUpsideDown) {
564 BYTE *mem;
565 GLint fmt;
566 GLint type;
567 BYTE *row, *top, *bottom;
568 int i;
569 BOOL bpp;
571 switch(This->resource.format)
573 case WINED3DFMT_P8:
575 /* GL can't return palettized data, so read ARGB pixels into a
576 * separate block of memory and convert them into palettized format
577 * in software. Slow, but if the app means to use palettized render
578 * targets and locks it...
580 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
581 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
582 * for the color channels when palettizing the colors.
584 fmt = GL_RGB;
585 type = GL_UNSIGNED_BYTE;
586 pitch *= 3;
587 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
588 if(!mem) {
589 ERR("Out of memory\n");
590 return;
592 bpp = This->bytesPerPixel * 3;
594 break;
596 default:
597 mem = dest;
598 fmt = This->glDescription.glFormat;
599 type = This->glDescription.glType;
600 bpp = This->bytesPerPixel;
603 glReadPixels(rect->left, rect->top,
604 rect->right - rect->left,
605 rect->bottom - rect->top,
606 fmt, type, mem);
607 vcheckGLcall("glReadPixels");
609 /* TODO: Merge this with the palettization loop below for P8 targets */
611 if(!srcUpsideDown) {
612 UINT len, off;
613 /* glReadPixels returns the image upside down, and there is no way to prevent this.
614 Flip the lines in software */
615 len = (rect->right - rect->left) * bpp;
616 off = rect->left * bpp;
618 row = HeapAlloc(GetProcessHeap(), 0, len);
619 if(!row) {
620 ERR("Out of memory\n");
621 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
622 return;
625 top = mem + pitch * rect->top;
626 bottom = ((BYTE *) mem) + pitch * ( rect->bottom - rect->top - 1);
627 for(i = 0; i < (rect->bottom - rect->top) / 2; i++) {
628 memcpy(row, top + off, len);
629 memcpy(top + off, bottom + off, len);
630 memcpy(bottom + off, row, len);
631 top += pitch;
632 bottom -= pitch;
634 HeapFree(GetProcessHeap(), 0, row);
637 if(This->resource.format == WINED3DFMT_P8) {
638 PALETTEENTRY *pal;
639 DWORD width = pitch / 3;
640 int x, y, c;
641 if(This->palette) {
642 pal = This->palette->palents;
643 } else {
644 pal = This->resource.wineD3DDevice->palettes[This->resource.wineD3DDevice->currentPalette];
647 for(y = rect->top; y < rect->bottom; y++) {
648 for(x = rect->left; x < rect->right; x++) {
649 /* start lines pixels */
650 BYTE *blue = (BYTE *) ((BYTE *) mem) + y * pitch + x * (sizeof(BYTE) * 3);
651 BYTE *green = blue + 1;
652 BYTE *red = green + 1;
654 for(c = 0; c < 256; c++) {
655 if(*red == pal[c].peRed &&
656 *green == pal[c].peGreen &&
657 *blue == pal[c].peBlue)
659 *((BYTE *) dest + y * width + x) = c;
660 break;
665 HeapFree(GetProcessHeap(), 0, mem);
669 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
670 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
671 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
672 IWineD3DSwapChainImpl *swapchain = NULL;
674 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
676 if (This->Flags & SFLAG_LOCKED) {
677 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
678 return WINED3DERR_INVALIDCALL;
680 if (!(This->Flags & SFLAG_LOCKABLE)) {
681 /* Note: UpdateTextures calls CopyRects which calls this routine to populate the
682 texture regions, and since the destination is an unlockable region we need
683 to tolerate this */
684 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
685 /*return WINED3DERR_INVALIDCALL; */
688 pLockedRect->Pitch = IWineD3DSurface_GetPitch(iface);
690 /* Mark the surface locked */
691 This->Flags |= SFLAG_LOCKED;
693 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy */
694 if(!This->resource.allocatedMemory) {
695 This->resource.allocatedMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + 4);
696 This->Flags &= ~SFLAG_INSYSMEM; /* This is the marker that surface data has to be downloaded */
699 /* Calculate the correct start address to report */
700 if (NULL == pRect) {
701 pLockedRect->pBits = This->resource.allocatedMemory;
702 This->lockedRect.left = 0;
703 This->lockedRect.top = 0;
704 This->lockedRect.right = This->currentDesc.Width;
705 This->lockedRect.bottom = This->currentDesc.Height;
706 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);
707 } else {
708 TRACE("Lock Rect (%p) = l %d, t %d, r %d, b %d\n", pRect, pRect->left, pRect->top, pRect->right, pRect->bottom);
710 /* DXTn textures are based on compressed blocks of 4x4 pixels, each
711 * 16 bytes large (8 bytes in case of DXT1). Because of that Pitch has
712 * slightly different meaning compared to regular textures. For DXTn
713 * textures Pitch is the size of a row of blocks, 4 high and "width"
714 * long. The x offset is calculated differently as well, since moving 4
715 * pixels to the right actually moves an entire 4x4 block to right, ie
716 * 16 bytes (8 in case of DXT1). */
717 if (This->resource.format == WINED3DFMT_DXT1) {
718 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top / 4) + (pRect->left * 2);
719 } else if (This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3
720 || This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
721 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top / 4) + (pRect->left * 4);
722 } else {
723 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top) + (pRect->left * This->bytesPerPixel);
725 This->lockedRect.left = pRect->left;
726 This->lockedRect.top = pRect->top;
727 This->lockedRect.right = pRect->right;
728 This->lockedRect.bottom = pRect->bottom;
731 if (This->Flags & SFLAG_NONPOW2) {
732 TRACE("Locking non-power 2 texture\n");
735 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
736 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
737 * changed
739 if(!(This->Flags & SFLAG_DYNLOCK)) {
740 This->lockCount++;
741 /* MAXLOCKCOUNT is defined in wined3d_private.h */
742 if(This->lockCount > MAXLOCKCOUNT) {
743 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
744 This->Flags |= SFLAG_DYNLOCK;
748 if (Flags & WINED3DLOCK_DISCARD) {
749 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
750 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
751 This->Flags |= SFLAG_INSYSMEM;
754 if (This->Flags & SFLAG_INSYSMEM) {
755 TRACE("Local copy is up to date, not downloading data\n");
756 goto lock_end;
759 /* Now download the surface content from opengl
760 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
761 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
763 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
764 if(swapchain || iface == myDevice->render_targets[0]) {
765 BOOL srcIsUpsideDown;
767 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
768 static BOOL warned = FALSE;
769 if(!warned) {
770 ERR("The application tries to lock the render target, but render target locking is disabled\n");
771 warned = TRUE;
773 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
774 return WINED3D_OK;
777 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
778 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
779 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
780 * context->last_was_blit set on the unlock.
782 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
783 ENTER_GL();
785 /* Select the correct read buffer, and give some debug output.
786 * There is no need to keep track of the current read buffer or reset it, every part of the code
787 * that reads sets the read buffer as desired.
789 if(!swapchain) {
790 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
791 * Read from the back buffer
793 TRACE("Locking offscreen render target\n");
794 glReadBuffer(myDevice->offscreenBuffer);
795 srcIsUpsideDown = TRUE;
796 } else {
797 GLenum buffer = surface_get_gl_buffer(iface, (IWineD3DSwapChain *)swapchain);
798 TRACE("Locking %#x buffer\n", buffer);
799 glReadBuffer(buffer);
800 checkGLcall("glReadBuffer");
802 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
803 srcIsUpsideDown = FALSE;
806 switch(wined3d_settings.rendertargetlock_mode) {
807 case RTL_AUTO:
808 case RTL_READDRAW:
809 case RTL_READTEX:
810 read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
811 break;
813 case RTL_TEXDRAW:
814 case RTL_TEXTEX:
815 read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
816 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
817 break;
819 LEAVE_GL();
821 /* Mark the local copy up to date if a full download was done */
822 if(This->lockedRect.left == 0 &&
823 This->lockedRect.top == 0 &&
824 This->lockedRect.right == This->currentDesc.Width &&
825 This->lockedRect.bottom == This->currentDesc.Height) {
826 This->Flags |= SFLAG_INSYSMEM;
828 } else if(iface == myDevice->stencilBufferTarget) {
829 /** the depth stencil in openGL has a format of GL_FLOAT
830 * which should be good for WINED3DFMT_D16_LOCKABLE
831 * and WINED3DFMT_D16
832 * it is unclear what format the stencil buffer is in except.
833 * 'Each index is converted to fixed point...
834 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
835 * mappings in the table GL_PIXEL_MAP_S_TO_S.
836 * glReadPixels(This->lockedRect.left,
837 * This->lockedRect.bottom - j - 1,
838 * This->lockedRect.right - This->lockedRect.left,
839 * 1,
840 * GL_DEPTH_COMPONENT,
841 * type,
842 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
844 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
845 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
846 * none of that is the case the problem is not in this function :-)
847 ********************************************/
848 FIXME("Depth stencil locking not supported yet\n");
849 } else {
850 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
851 TRACE("locking an ordinary surface\n");
853 if (0 != This->glDescription.textureName) {
854 /* Now I have to copy thing bits back */
856 if(myDevice->createParms.BehaviorFlags & WINED3DCREATE_MULTITHREADED) {
857 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
860 ENTER_GL();
861 /* Make sure that a proper texture unit is selected, bind the texture and dirtify the sampler to restore the texture on the next draw */
862 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
863 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
864 checkGLcall("glActiveTextureARB");
866 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(0));
867 IWineD3DSurface_PreLoad(iface);
869 surface_download_data(This);
870 LEAVE_GL();
874 lock_end:
875 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
876 /* Don't dirtify */
877 } else {
878 IWineD3DBaseTexture *pBaseTexture;
880 * Dirtify on lock
881 * as seen in msdn docs
883 IWineD3DSurface_AddDirtyRect(iface, &This->lockedRect);
885 /** Dirtify Container if needed */
886 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
887 TRACE("Making container dirty\n");
888 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
889 IWineD3DBaseTexture_Release(pBaseTexture);
890 } else {
891 TRACE("Surface is standalone, no need to dirty the container\n");
895 TRACE("returning memory@%p, pitch(%d) dirtyfied(%d)\n", pLockedRect->pBits, pLockedRect->Pitch,
896 This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
897 return WINED3D_OK;
900 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This) {
901 GLint prev_store;
902 GLint prev_rasterpos[4];
903 GLint skipBytes = 0;
904 BOOL storechanged = FALSE, memory_allocated = FALSE;
905 GLint fmt, type;
906 BYTE *mem;
907 UINT bpp;
908 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
910 glDisable(GL_TEXTURE_2D);
911 vcheckGLcall("glDisable(GL_TEXTURE_2D)");
913 glFlush();
914 vcheckGLcall("glFlush");
915 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
916 vcheckGLcall("glIntegerv");
917 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
918 vcheckGLcall("glIntegerv");
919 glPixelZoom(1.0, -1.0);
920 vcheckGLcall("glPixelZoom");
922 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
923 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
924 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
926 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
927 vcheckGLcall("glRasterPos2f");
929 /* Some drivers(radeon dri, others?) don't like exceptions during
930 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
931 * after ReleaseDC. Reading it will cause an exception, which x11drv will
932 * catch to put the dib section in InSync mode, which leads to a crash
933 * and a blocked x server on my radeon card.
935 * The following lines read the dib section so it is put in inSync mode
936 * before glDrawPixels is called and the crash is prevented. There won't
937 * be any interfering gdi accesses, because UnlockRect is called from
938 * ReleaseDC, and the app won't use the dc any more afterwards.
940 if(This->Flags & SFLAG_DIBSECTION) {
941 volatile BYTE read;
942 read = This->resource.allocatedMemory[0];
945 switch (This->resource.format) {
946 /* No special care needed */
947 case WINED3DFMT_A4R4G4B4:
948 case WINED3DFMT_R5G6B5:
949 case WINED3DFMT_A1R5G5B5:
950 case WINED3DFMT_R8G8B8:
951 type = This->glDescription.glType;
952 fmt = This->glDescription.glFormat;
953 mem = This->resource.allocatedMemory;
954 bpp = This->bytesPerPixel;
955 break;
957 case WINED3DFMT_X4R4G4B4:
959 int size;
960 unsigned short *data;
961 data = (unsigned short *)This->resource.allocatedMemory;
962 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
963 while(size > 0) {
964 *data |= 0xF000;
965 data++;
966 size--;
968 type = This->glDescription.glType;
969 fmt = This->glDescription.glFormat;
970 mem = This->resource.allocatedMemory;
971 bpp = This->bytesPerPixel;
973 break;
975 case WINED3DFMT_X1R5G5B5:
977 int size;
978 unsigned short *data;
979 data = (unsigned short *)This->resource.allocatedMemory;
980 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
981 while(size > 0) {
982 *data |= 0x8000;
983 data++;
984 size--;
986 type = This->glDescription.glType;
987 fmt = This->glDescription.glFormat;
988 mem = This->resource.allocatedMemory;
989 bpp = This->bytesPerPixel;
991 break;
993 case WINED3DFMT_X8R8G8B8:
995 /* make sure the X byte is set to alpha on, since it
996 could be any random value. This fixes the intro movie in Pirates! */
997 int size;
998 unsigned int *data;
999 data = (unsigned int *)This->resource.allocatedMemory;
1000 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
1001 while(size > 0) {
1002 *data |= 0xFF000000;
1003 data++;
1004 size--;
1007 /* Fall through */
1009 case WINED3DFMT_A8R8G8B8:
1011 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
1012 vcheckGLcall("glPixelStorei");
1013 storechanged = TRUE;
1014 type = This->glDescription.glType;
1015 fmt = This->glDescription.glFormat;
1016 mem = This->resource.allocatedMemory;
1017 bpp = This->bytesPerPixel;
1019 break;
1021 case WINED3DFMT_A2R10G10B10:
1023 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
1024 vcheckGLcall("glPixelStorei");
1025 storechanged = TRUE;
1026 type = This->glDescription.glType;
1027 fmt = This->glDescription.glFormat;
1028 mem = This->resource.allocatedMemory;
1029 bpp = This->bytesPerPixel;
1031 break;
1033 case WINED3DFMT_P8:
1035 int height = This->glRect.bottom - This->glRect.top;
1036 type = GL_UNSIGNED_BYTE;
1037 fmt = GL_RGBA;
1039 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * sizeof(DWORD));
1040 if(!mem) {
1041 ERR("Out of memory\n");
1042 return;
1044 memory_allocated = TRUE;
1045 d3dfmt_convert_surface(This->resource.allocatedMemory,
1046 mem,
1047 pitch,
1048 pitch,
1049 height,
1050 pitch * 4,
1051 CONVERT_PALETTED,
1052 This);
1053 bpp = This->bytesPerPixel * 4;
1054 pitch *= 4;
1056 break;
1058 default:
1059 FIXME("Unsupported Format %u in locking func\n", This->resource.format);
1061 /* Give it a try */
1062 type = This->glDescription.glType;
1063 fmt = This->glDescription.glFormat;
1064 mem = This->resource.allocatedMemory;
1065 bpp = This->bytesPerPixel;
1068 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1069 (This->lockedRect.bottom - This->lockedRect.top)-1,
1070 fmt, type,
1071 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1072 checkGLcall("glDrawPixels");
1073 glPixelZoom(1.0,1.0);
1074 vcheckGLcall("glPixelZoom");
1076 glRasterPos3iv(&prev_rasterpos[0]);
1077 vcheckGLcall("glRasterPos3iv");
1079 /* Reset to previous pack row length */
1080 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1081 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1082 if(storechanged) {
1083 glPixelStorei(GL_PACK_SWAP_BYTES, prev_store);
1084 vcheckGLcall("glPixelStorei GL_PACK_SWAP_BYTES");
1087 /* Blitting environment requires that 2D texturing is enabled. It was turned off before,
1088 * turn it on again
1090 glEnable(GL_TEXTURE_2D);
1091 checkGLcall("glEnable(GL_TEXTURE_2D)");
1093 if(memory_allocated) HeapFree(GetProcessHeap(), 0, mem);
1094 return;
1097 static void flush_to_framebuffer_texture(IWineD3DSurfaceImpl *This) {
1098 float glTexCoord[4];
1100 glTexCoord[0] = (float) This->lockedRect.left / (float) This->pow2Width; /* left */
1101 glTexCoord[1] = (float) This->lockedRect.right / (float) This->pow2Width; /* right */
1102 glTexCoord[2] = (float) This->lockedRect.top / (float) This->pow2Height; /* top */
1103 glTexCoord[3] = (float) This->lockedRect.bottom / (float) This->pow2Height; /* bottom */
1105 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
1107 ENTER_GL();
1109 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
1110 checkGLcall("glEnable glBindTexture");
1112 /* No filtering for blts */
1113 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1114 checkGLcall("glTexParameteri");
1115 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1116 checkGLcall("glTexParameteri");
1118 /* Start drawing a quad */
1119 glBegin(GL_QUADS);
1121 glColor3d(1.0f, 1.0f, 1.0f);
1122 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
1123 glVertex3f(This->lockedRect.left, This->lockedRect.top, 0.0);
1125 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
1126 glVertex3f(This->lockedRect.left, This->lockedRect.bottom, 0.0);
1128 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
1129 glVertex3d(This->lockedRect.right, This->lockedRect.bottom, 0.0);
1131 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
1132 glVertex3f(This->lockedRect.right, This->lockedRect.top, 0.0);
1134 glEnd();
1135 checkGLcall("glEnd");
1137 /* Unbind the texture */
1138 glBindTexture(GL_TEXTURE_2D, 0);
1139 checkGLcall("glEnable glBindTexture");
1141 LEAVE_GL();
1144 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1145 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1146 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1147 IWineD3DSwapChainImpl *swapchain = NULL;
1149 if (!(This->Flags & SFLAG_LOCKED)) {
1150 WARN("trying to Unlock an unlocked surf@%p\n", This);
1151 return WINED3DERR_INVALIDCALL;
1154 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1156 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1157 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1158 goto unlock_end;
1161 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1162 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1163 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1164 static BOOL warned = FALSE;
1165 if(!warned) {
1166 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1167 warned = TRUE;
1169 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1170 goto unlock_end;
1173 /* Activate the correct context for the render target */
1174 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1175 ENTER_GL();
1177 if(!swapchain) {
1178 /* Primary offscreen render target */
1179 TRACE("Offscreen render target\n");
1180 glDrawBuffer(myDevice->offscreenBuffer);
1181 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1182 } else {
1183 GLenum buffer = surface_get_gl_buffer(iface, (IWineD3DSwapChain *)swapchain);
1184 TRACE("Unlocking %#x buffer\n", buffer);
1185 glDrawBuffer(buffer);
1186 checkGLcall("glDrawBuffer");
1188 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1191 switch(wined3d_settings.rendertargetlock_mode) {
1192 case RTL_AUTO:
1193 case RTL_READDRAW:
1194 case RTL_TEXDRAW:
1195 flush_to_framebuffer_drawpixels(This);
1196 break;
1198 case RTL_READTEX:
1199 case RTL_TEXTEX:
1200 flush_to_framebuffer_texture(This);
1201 break;
1203 if(!swapchain) {
1204 glDrawBuffer(myDevice->offscreenBuffer);
1205 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1206 } else if(swapchain->backBuffer) {
1207 glDrawBuffer(GL_BACK);
1208 checkGLcall("glDrawBuffer(GL_BACK)");
1209 } else {
1210 glDrawBuffer(GL_FRONT);
1211 checkGLcall("glDrawBuffer(GL_FRONT)");
1213 LEAVE_GL();
1215 This->dirtyRect.left = This->currentDesc.Width;
1216 This->dirtyRect.top = This->currentDesc.Height;
1217 This->dirtyRect.right = 0;
1218 This->dirtyRect.bottom = 0;
1219 This->Flags |= SFLAG_INDRAWABLE;
1220 } else if(iface == myDevice->stencilBufferTarget) {
1221 FIXME("Depth Stencil buffer locking is not implemented\n");
1222 } else {
1223 /* The rest should be a normal texture */
1224 IWineD3DBaseTextureImpl *impl;
1225 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1226 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1227 * states need resetting
1229 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1230 if(impl->baseTexture.bindCount) {
1231 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1233 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1237 unlock_end:
1238 This->Flags &= ~SFLAG_LOCKED;
1239 memset(&This->lockedRect, 0, sizeof(RECT));
1240 return WINED3D_OK;
1243 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1244 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1245 WINED3DLOCKED_RECT lock;
1246 UINT usage;
1247 BITMAPINFO* b_info;
1248 HDC ddc;
1249 DWORD *masks;
1250 HRESULT hr;
1251 RGBQUAD col[256];
1252 const StaticPixelFormatDesc *formatEntry = getFormatDescEntry(This->resource.format, NULL, NULL);
1254 TRACE("(%p)->(%p)\n",This,pHDC);
1256 if(This->Flags & SFLAG_USERPTR) {
1257 ERR("Not supported on surfaces with an application-provided surfaces\n");
1258 return WINEDDERR_NODC;
1261 /* Give more detailed info for ddraw */
1262 if (This->Flags & SFLAG_DCINUSE)
1263 return WINEDDERR_DCALREADYCREATED;
1265 /* Can't GetDC if the surface is locked */
1266 if (This->Flags & SFLAG_LOCKED)
1267 return WINED3DERR_INVALIDCALL;
1269 memset(&lock, 0, sizeof(lock)); /* To be sure */
1271 /* Create a DIB section if there isn't a hdc yet */
1272 if(!This->hDC) {
1273 int extraline = 0;
1274 SYSTEM_INFO sysInfo;
1275 void *oldmem = This->resource.allocatedMemory;
1277 switch (This->bytesPerPixel) {
1278 case 2:
1279 case 4:
1280 /* Allocate extra space to store the RGB bit masks. */
1281 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
1282 break;
1284 case 3:
1285 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
1286 break;
1288 default:
1289 /* Allocate extra space for a palette. */
1290 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1291 sizeof(BITMAPINFOHEADER)
1292 + sizeof(RGBQUAD)
1293 * (1 << (This->bytesPerPixel * 8)));
1294 break;
1297 if (!b_info)
1298 return E_OUTOFMEMORY;
1300 /* Some apps access the surface in via DWORDs, and do not take the necessary care at the end of the
1301 * surface. So we need at least extra 4 bytes at the end of the surface. Check against the page size,
1302 * if the last page used for the surface has at least 4 spare bytes we're safe, otherwise
1303 * add an extra line to the dib section
1305 GetSystemInfo(&sysInfo);
1306 if( ((This->resource.size + 3) % sysInfo.dwPageSize) < 4) {
1307 extraline = 1;
1308 TRACE("Adding an extra line to the dib section\n");
1311 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1312 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
1313 b_info->bmiHeader.biWidth = IWineD3DSurface_GetPitch(iface) / This->bytesPerPixel;
1314 b_info->bmiHeader.biHeight = -This->currentDesc.Height -extraline;
1315 b_info->bmiHeader.biSizeImage = ( This->currentDesc.Height + extraline) * IWineD3DSurface_GetPitch(iface);
1316 b_info->bmiHeader.biPlanes = 1;
1317 b_info->bmiHeader.biBitCount = This->bytesPerPixel * 8;
1319 b_info->bmiHeader.biXPelsPerMeter = 0;
1320 b_info->bmiHeader.biYPelsPerMeter = 0;
1321 b_info->bmiHeader.biClrUsed = 0;
1322 b_info->bmiHeader.biClrImportant = 0;
1324 /* Get the bit masks */
1325 masks = (DWORD *) &(b_info->bmiColors);
1326 switch (This->resource.format) {
1327 case WINED3DFMT_R8G8B8:
1328 usage = DIB_RGB_COLORS;
1329 b_info->bmiHeader.biCompression = BI_RGB;
1330 break;
1332 case WINED3DFMT_X1R5G5B5:
1333 case WINED3DFMT_A1R5G5B5:
1334 case WINED3DFMT_A4R4G4B4:
1335 case WINED3DFMT_X4R4G4B4:
1336 case WINED3DFMT_R3G3B2:
1337 case WINED3DFMT_A8R3G3B2:
1338 case WINED3DFMT_A2B10G10R10:
1339 case WINED3DFMT_A8B8G8R8:
1340 case WINED3DFMT_X8B8G8R8:
1341 case WINED3DFMT_A2R10G10B10:
1342 case WINED3DFMT_R5G6B5:
1343 case WINED3DFMT_A16B16G16R16:
1344 usage = 0;
1345 b_info->bmiHeader.biCompression = BI_BITFIELDS;
1346 masks[0] = formatEntry->redMask;
1347 masks[1] = formatEntry->greenMask;
1348 masks[2] = formatEntry->blueMask;
1349 break;
1351 default:
1352 /* Don't know palette */
1353 b_info->bmiHeader.biCompression = BI_RGB;
1354 usage = 0;
1355 break;
1358 ddc = GetDC(0);
1359 if (ddc == 0) {
1360 HeapFree(GetProcessHeap(), 0, b_info);
1361 return HRESULT_FROM_WIN32(GetLastError());
1364 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);
1365 This->dib.DIBsection = CreateDIBSection(ddc, b_info, usage, &This->dib.bitmap_data, 0 /* Handle */, 0 /* Offset */);
1366 ReleaseDC(0, ddc);
1368 if (!This->dib.DIBsection) {
1369 ERR("CreateDIBSection failed!\n");
1370 HeapFree(GetProcessHeap(), 0, b_info);
1371 return HRESULT_FROM_WIN32(GetLastError());
1374 TRACE("DIBSection at : %p\n", This->dib.bitmap_data);
1376 /* copy the existing surface to the dib section */
1377 if(This->resource.allocatedMemory) {
1378 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, b_info->bmiHeader.biSizeImage);
1379 /* We won't need that any more */
1380 } else {
1381 /* This is to make LockRect read the gl Texture although memory is allocated */
1382 This->Flags &= ~SFLAG_INSYSMEM;
1385 HeapFree(GetProcessHeap(), 0, b_info);
1387 /* Use the dib section from now on */
1388 This->resource.allocatedMemory = This->dib.bitmap_data;
1390 /* Now allocate a HDC */
1391 This->hDC = CreateCompatibleDC(0);
1392 This->dib.holdbitmap = SelectObject(This->hDC, This->dib.DIBsection);
1393 TRACE("using wined3d palette %p\n", This->palette);
1394 SelectPalette(This->hDC,
1395 This->palette ? This->palette->hpal : 0,
1396 FALSE);
1398 This->Flags |= SFLAG_DIBSECTION;
1400 if(This->Flags & SFLAG_CLIENT) {
1401 IWineD3DSurface_PreLoad(iface);
1403 HeapFree(GetProcessHeap(), 0, oldmem);
1406 /* Lock the surface */
1407 hr = IWineD3DSurface_LockRect(iface,
1408 &lock,
1409 NULL,
1411 if(FAILED(hr)) {
1412 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1413 /* keep the dib section */
1414 return hr;
1417 if(This->resource.format == WINED3DFMT_P8 ||
1418 This->resource.format == WINED3DFMT_A8P8) {
1419 unsigned int n;
1420 if(This->palette) {
1421 PALETTEENTRY ent[256];
1423 GetPaletteEntries(This->palette->hpal, 0, 256, ent);
1424 for (n=0; n<256; n++) {
1425 col[n].rgbRed = ent[n].peRed;
1426 col[n].rgbGreen = ent[n].peGreen;
1427 col[n].rgbBlue = ent[n].peBlue;
1428 col[n].rgbReserved = 0;
1430 } else {
1431 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1433 for (n=0; n<256; n++) {
1434 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
1435 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
1436 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
1437 col[n].rgbReserved = 0;
1441 SetDIBColorTable(This->hDC, 0, 256, col);
1444 *pHDC = This->hDC;
1445 TRACE("returning %p\n",*pHDC);
1446 This->Flags |= SFLAG_DCINUSE;
1448 return WINED3D_OK;
1451 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1452 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1454 TRACE("(%p)->(%p)\n",This,hDC);
1456 if (!(This->Flags & SFLAG_DCINUSE))
1457 return WINED3DERR_INVALIDCALL;
1459 if (This->hDC !=hDC) {
1460 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1461 return WINED3DERR_INVALIDCALL;
1464 /* we locked first, so unlock now */
1465 IWineD3DSurface_UnlockRect(iface);
1467 This->Flags &= ~SFLAG_DCINUSE;
1469 return WINED3D_OK;
1472 /* ******************************************************
1473 IWineD3DSurface Internal (No mapping to directx api) parts follow
1474 ****************************************************** */
1476 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, BOOL srgb_mode) {
1477 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1478 const GlPixelFormatDesc *glDesc;
1479 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1481 /* Default values: From the surface */
1482 *format = glDesc->glFormat;
1483 *internal = srgb_mode?glDesc->glGammaInternal:glDesc->glInternal;
1484 *type = glDesc->glType;
1485 *convert = NO_CONVERSION;
1486 *target_bpp = This->bytesPerPixel;
1488 /* Ok, now look if we have to do any conversion */
1489 switch(This->resource.format) {
1490 case WINED3DFMT_P8:
1491 /* ****************
1492 Paletted Texture
1493 **************** */
1494 /* Use conversion when the paletted texture extension is not available, or when it is available make sure it is used
1495 * for texturing as it won't work for calls like glDraw-/glReadPixels and further also use conversion in case of color keying.
1497 if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) || colorkey_active || (!use_texturing && GL_SUPPORT(EXT_PALETTED_TEXTURE)) ) {
1498 *format = GL_RGBA;
1499 *internal = GL_RGBA;
1500 *type = GL_UNSIGNED_BYTE;
1501 *target_bpp = 4;
1502 if(colorkey_active) {
1503 *convert = CONVERT_PALETTED_CK;
1504 } else {
1505 *convert = CONVERT_PALETTED;
1509 break;
1511 case WINED3DFMT_R3G3B2:
1512 /* **********************
1513 GL_UNSIGNED_BYTE_3_3_2
1514 ********************** */
1515 if (colorkey_active) {
1516 /* This texture format will never be used.. So do not care about color keying
1517 up until the point in time it will be needed :-) */
1518 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1520 break;
1522 case WINED3DFMT_R5G6B5:
1523 if (colorkey_active) {
1524 *convert = CONVERT_CK_565;
1525 *format = GL_RGBA;
1526 *internal = GL_RGBA;
1527 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1529 break;
1531 case WINED3DFMT_X1R5G5B5:
1532 if (colorkey_active) {
1533 *convert = CONVERT_CK_5551;
1534 *format = GL_BGRA;
1535 *internal = GL_RGBA;
1536 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1538 break;
1540 case WINED3DFMT_R8G8B8:
1541 if (colorkey_active) {
1542 *convert = CONVERT_CK_RGB24;
1543 *format = GL_RGBA;
1544 *internal = GL_RGBA;
1545 *type = GL_UNSIGNED_INT_8_8_8_8;
1546 *target_bpp = 4;
1548 break;
1550 case WINED3DFMT_X8R8G8B8:
1551 if (colorkey_active) {
1552 *convert = CONVERT_RGB32_888;
1553 *format = GL_RGBA;
1554 *internal = GL_RGBA;
1555 *type = GL_UNSIGNED_INT_8_8_8_8;
1557 break;
1559 case WINED3DFMT_V8U8:
1560 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1561 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1562 *format = GL_DUDV_ATI;
1563 *internal = GL_DU8DV8_ATI;
1564 *type = GL_BYTE;
1565 /* No conversion - Just change the gl type */
1566 break;
1568 *convert = CONVERT_V8U8;
1569 *format = GL_BGR;
1570 *internal = GL_RGB8;
1571 *type = GL_UNSIGNED_BYTE;
1572 *target_bpp = 3;
1573 break;
1575 case WINED3DFMT_X8L8V8U8:
1576 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1577 *convert = CONVERT_X8L8V8U8;
1578 *format = GL_BGRA;
1579 *internal = GL_RGBA8;
1580 *type = GL_UNSIGNED_BYTE;
1581 *target_bpp = 4;
1582 /* Not supported by GL_ATI_envmap_bumpmap */
1583 break;
1585 case WINED3DFMT_Q8W8V8U8:
1586 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1587 *convert = CONVERT_Q8W8V8U8;
1588 *format = GL_BGRA;
1589 *internal = GL_RGBA8;
1590 *type = GL_UNSIGNED_BYTE;
1591 *target_bpp = 4;
1592 /* Not supported by GL_ATI_envmap_bumpmap */
1593 break;
1595 case WINED3DFMT_V16U16:
1596 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1597 *convert = CONVERT_V16U16;
1598 *format = GL_BGR;
1599 *internal = GL_RGB16;
1600 *type = GL_SHORT;
1601 *target_bpp = 6;
1602 /* What should I do here about GL_ATI_envmap_bumpmap?
1603 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1605 break;
1607 case WINED3DFMT_A4L4:
1608 /* A4L4 exists as an internal gl format, but for some reason there is not
1609 * format+type combination to load it. Thus convert it to A8L8, then load it
1610 * with A4L4 internal, but A8L8 format+type
1612 *convert = CONVERT_A4L4;
1613 *format = GL_LUMINANCE_ALPHA;
1614 *internal = GL_LUMINANCE4_ALPHA4;
1615 *type = GL_UNSIGNED_BYTE;
1616 *target_bpp = 2;
1617 break;
1619 case WINED3DFMT_R32F:
1620 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1621 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1622 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1623 * 1.0 instead.
1625 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1627 *convert = CONVERT_R32F;
1628 *format = GL_RGB;
1629 *internal = GL_RGB32F_ARB;
1630 *type = GL_FLOAT;
1631 *target_bpp = 12;
1632 break;
1634 case WINED3DFMT_R16F:
1635 /* Simmilar to R32F */
1636 *convert = CONVERT_R16F;
1637 *format = GL_RGB;
1638 *internal = GL_RGB16F_ARB;
1639 *type = GL_HALF_FLOAT_ARB;
1640 *target_bpp = 6;
1641 break;
1643 default:
1644 break;
1647 return WINED3D_OK;
1650 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf) {
1651 BYTE *source, *dest;
1652 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surf);
1654 switch (convert) {
1655 case NO_CONVERSION:
1657 memcpy(dst, src, pitch * height);
1658 break;
1660 case CONVERT_PALETTED:
1661 case CONVERT_PALETTED_CK:
1663 IWineD3DPaletteImpl* pal = surf->palette;
1664 BYTE table[256][4];
1665 unsigned int i;
1666 unsigned int x, y;
1668 if( pal == NULL) {
1669 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1672 if (pal == NULL) {
1673 /* Still no palette? Use the device's palette */
1674 /* Get the surface's palette */
1675 for (i = 0; i < 256; i++) {
1676 IWineD3DDeviceImpl *device = surf->resource.wineD3DDevice;
1678 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1679 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1680 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1681 if ((convert == CONVERT_PALETTED_CK) &&
1682 (i >= surf->SrcBltCKey.dwColorSpaceLowValue) &&
1683 (i <= surf->SrcBltCKey.dwColorSpaceHighValue)) {
1684 /* We should maybe here put a more 'neutral' color than the standard bright purple
1685 one often used by application to prevent the nice purple borders when bi-linear
1686 filtering is on */
1687 table[i][3] = 0x00;
1688 } else {
1689 table[i][3] = 0xFF;
1692 } else {
1693 TRACE("Using surface palette %p\n", pal);
1694 /* Get the surface's palette */
1695 for (i = 0; i < 256; i++) {
1696 table[i][0] = pal->palents[i].peRed;
1697 table[i][1] = pal->palents[i].peGreen;
1698 table[i][2] = pal->palents[i].peBlue;
1699 if ((convert == CONVERT_PALETTED_CK) &&
1700 (i >= surf->SrcBltCKey.dwColorSpaceLowValue) &&
1701 (i <= surf->SrcBltCKey.dwColorSpaceHighValue)) {
1702 /* We should maybe here put a more 'neutral' color than the standard bright purple
1703 one often used by application to prevent the nice purple borders when bi-linear
1704 filtering is on */
1705 table[i][3] = 0x00;
1706 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
1707 table[i][3] = pal->palents[i].peFlags;
1708 } else {
1709 table[i][3] = 0xFF;
1714 for (y = 0; y < height; y++)
1716 source = src + pitch * y;
1717 dest = dst + outpitch * y;
1718 /* This is an 1 bpp format, using the width here is fine */
1719 for (x = 0; x < width; x++) {
1720 BYTE color = *source++;
1721 *dest++ = table[color][0];
1722 *dest++ = table[color][1];
1723 *dest++ = table[color][2];
1724 *dest++ = table[color][3];
1728 break;
1730 case CONVERT_CK_565:
1732 /* Converting the 565 format in 5551 packed to emulate color-keying.
1734 Note : in all these conversion, it would be best to average the averaging
1735 pixels to get the color of the pixel that will be color-keyed to
1736 prevent 'color bleeding'. This will be done later on if ever it is
1737 too visible.
1739 Note2: Nvidia documents say that their driver does not support alpha + color keying
1740 on the same surface and disables color keying in such a case
1742 unsigned int x, y;
1743 WORD *Source;
1744 WORD *Dest;
1746 TRACE("Color keyed 565\n");
1748 for (y = 0; y < height; y++) {
1749 Source = (WORD *) (src + y * pitch);
1750 Dest = (WORD *) (dst + y * outpitch);
1751 for (x = 0; x < width; x++ ) {
1752 WORD color = *Source++;
1753 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1754 if ((color < surf->SrcBltCKey.dwColorSpaceLowValue) ||
1755 (color > surf->SrcBltCKey.dwColorSpaceHighValue)) {
1756 *Dest |= 0x0001;
1758 Dest++;
1762 break;
1764 case CONVERT_CK_5551:
1766 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1767 unsigned int x, y;
1768 WORD *Source;
1769 WORD *Dest;
1770 TRACE("Color keyed 5551\n");
1771 for (y = 0; y < height; y++) {
1772 Source = (WORD *) (src + y * pitch);
1773 Dest = (WORD *) (dst + y * outpitch);
1774 for (x = 0; x < width; x++ ) {
1775 WORD color = *Source++;
1776 *Dest = color;
1777 if ((color < surf->SrcBltCKey.dwColorSpaceLowValue) ||
1778 (color > surf->SrcBltCKey.dwColorSpaceHighValue)) {
1779 *Dest |= (1 << 15);
1781 else {
1782 *Dest &= ~(1 << 15);
1784 Dest++;
1788 break;
1790 case CONVERT_V8U8:
1792 unsigned int x, y;
1793 short *Source;
1794 unsigned char *Dest;
1795 for(y = 0; y < height; y++) {
1796 Source = (short *) (src + y * pitch);
1797 Dest = (unsigned char *) (dst + y * outpitch);
1798 for (x = 0; x < width; x++ ) {
1799 long color = (*Source++);
1800 /* B */ Dest[0] = 0xff;
1801 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1802 /* R */ Dest[2] = (color) + 128; /* U */
1803 Dest += 3;
1806 break;
1809 case CONVERT_Q8W8V8U8:
1811 unsigned int x, y;
1812 DWORD *Source;
1813 unsigned char *Dest;
1814 for(y = 0; y < height; y++) {
1815 Source = (DWORD *) (src + y * pitch);
1816 Dest = (unsigned char *) (dst + y * outpitch);
1817 for (x = 0; x < width; x++ ) {
1818 long color = (*Source++);
1819 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1820 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1821 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1822 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1823 Dest += 4;
1826 break;
1829 case CONVERT_A4L4:
1831 unsigned int x, y;
1832 unsigned char *Source;
1833 unsigned char *Dest;
1834 for(y = 0; y < height; y++) {
1835 Source = (unsigned char *) (src + y * pitch);
1836 Dest = (unsigned char *) (dst + y * outpitch);
1837 for (x = 0; x < width; x++ ) {
1838 unsigned char color = (*Source++);
1839 /* A */ Dest[1] = (color & 0xf0) << 0;
1840 /* L */ Dest[0] = (color & 0x0f) << 4;
1841 Dest += 2;
1844 break;
1847 case CONVERT_R32F:
1849 unsigned int x, y;
1850 float *Source;
1851 float *Dest;
1852 for(y = 0; y < height; y++) {
1853 Source = (float *) (src + y * pitch);
1854 Dest = (float *) (dst + y * outpitch);
1855 for (x = 0; x < width; x++ ) {
1856 float color = (*Source++);
1857 Dest[0] = color;
1858 Dest[1] = 1.0;
1859 Dest[2] = 1.0;
1860 Dest += 3;
1863 break;
1866 case CONVERT_R16F:
1868 unsigned int x, y;
1869 WORD *Source;
1870 WORD *Dest;
1871 WORD one = 0x3c00;
1872 for(y = 0; y < height; y++) {
1873 Source = (WORD *) (src + y * pitch);
1874 Dest = (WORD *) (dst + y * outpitch);
1875 for (x = 0; x < width; x++ ) {
1876 WORD color = (*Source++);
1877 Dest[0] = color;
1878 Dest[1] = one;
1879 Dest[2] = one;
1880 Dest += 3;
1883 break;
1885 default:
1886 ERR("Unsupported conversation type %d\n", convert);
1888 return WINED3D_OK;
1891 /* This function is used in case of 8bit paletted textures to upload the palette.
1892 For now it only supports GL_EXT_paletted_texture extension but support for other
1893 extensions like ARB_fragment_program and ATI_fragment_shaders will be added as well.
1895 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
1896 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1897 IWineD3DPaletteImpl* pal = This->palette;
1898 BYTE table[256][4];
1899 int i;
1901 if (pal == NULL) {
1902 /* Still no palette? Use the device's palette */
1903 /* Get the surface's palette */
1904 for (i = 0; i < 256; i++) {
1905 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1907 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1908 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1909 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1910 if ((convert == CONVERT_PALETTED_CK) &&
1911 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1912 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1913 /* We should maybe here put a more 'neutral' color than the standard bright purple
1914 one often used by application to prevent the nice purple borders when bi-linear
1915 filtering is on */
1916 table[i][3] = 0x00;
1917 } else {
1918 table[i][3] = 0xFF;
1921 } else {
1922 TRACE("Using surface palette %p\n", pal);
1923 /* Get the surface's palette */
1924 for (i = 0; i < 256; i++) {
1925 table[i][0] = pal->palents[i].peRed;
1926 table[i][1] = pal->palents[i].peGreen;
1927 table[i][2] = pal->palents[i].peBlue;
1928 if ((convert == CONVERT_PALETTED_CK) &&
1929 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1930 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1931 /* We should maybe here put a more 'neutral' color than the standard bright purple
1932 one often used by application to prevent the nice purple borders when bi-linear
1933 filtering is on */
1934 table[i][3] = 0x00;
1935 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
1936 table[i][3] = pal->palents[i].peFlags;
1937 } else {
1938 table[i][3] = 0xFF;
1942 GL_EXTCALL(glColorTableEXT(GL_TEXTURE_2D,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
1945 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
1946 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1948 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
1949 /* If a ddraw-style palette is attached assume no d3d9 palette change.
1950 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
1952 return FALSE;
1955 if(This->palette9) {
1956 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
1957 return FALSE;
1959 } else {
1960 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
1962 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
1963 return TRUE;
1966 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
1967 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1968 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1969 GLenum format, internal, type;
1970 CONVERT_TYPES convert;
1971 int bpp;
1972 int width, pitch, outpitch;
1973 BYTE *mem;
1975 if (!(This->Flags & SFLAG_INTEXTURE)) {
1976 TRACE("Reloading because surface is dirty\n");
1977 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
1978 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
1979 /* Reload: vice versa OR */
1980 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
1981 /* Also reload: Color key is active AND the color key has changed */
1982 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
1983 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
1984 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
1985 TRACE("Reloading because of color keying\n");
1986 } else if(palette9_changed(This)) {
1987 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
1988 } else {
1989 TRACE("surface is already in texture\n");
1990 return WINED3D_OK;
1993 This->Flags |= SFLAG_INTEXTURE;
1995 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
1996 * These resources are not bound by device size or format restrictions. Because of this,
1997 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
1998 * However, these resources can always be created, locked, and copied.
2000 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2002 FIXME("(%p) Operation not supported for scratch textures\n",This);
2003 return WINED3DERR_INVALIDCALL;
2006 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb_mode);
2008 if (This->Flags & SFLAG_INDRAWABLE) {
2009 if (This->glDescription.level != 0)
2010 FIXME("Surface in texture is only supported for level 0\n");
2011 else if (This->resource.format == WINED3DFMT_P8 || This->resource.format == WINED3DFMT_A8P8 ||
2012 This->resource.format == WINED3DFMT_DXT1 || This->resource.format == WINED3DFMT_DXT2 ||
2013 This->resource.format == WINED3DFMT_DXT3 || This->resource.format == WINED3DFMT_DXT4 ||
2014 This->resource.format == WINED3DFMT_DXT5)
2015 FIXME("Format %d not supported\n", This->resource.format);
2016 else {
2017 GLint prevRead;
2019 ENTER_GL();
2020 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2021 vcheckGLcall("glGetIntegerv");
2022 glReadBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2023 vcheckGLcall("glReadBuffer");
2025 if(!(This->Flags & SFLAG_ALLOCATED)) {
2026 surface_allocate_surface(This, internal, This->pow2Width,
2027 This->pow2Height, format, type);
2030 glCopyTexSubImage2D(This->glDescription.target,
2031 This->glDescription.level,
2032 0, 0, 0, 0,
2033 This->currentDesc.Width,
2034 This->currentDesc.Height);
2035 checkGLcall("glCopyTexSubImage2D");
2037 glReadBuffer(prevRead);
2038 vcheckGLcall("glReadBuffer");
2040 LEAVE_GL();
2042 TRACE("Updated target %d\n", This->glDescription.target);
2044 return WINED3D_OK;
2045 } else
2046 /* The only place where LoadTexture() might get called when isInDraw=1
2047 * is ActivateContext where lastActiveRenderTarget is preloaded.
2049 if(iface == device->lastActiveRenderTarget && device->isInDraw)
2050 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
2052 /* Otherwise: System memory copy must be most up to date */
2054 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
2055 This->Flags |= SFLAG_GLCKEY;
2056 This->glCKey = This->SrcBltCKey;
2058 else This->Flags &= ~SFLAG_GLCKEY;
2060 /* The width is in 'length' not in bytes */
2061 width = This->currentDesc.Width;
2062 pitch = IWineD3DSurface_GetPitch(iface);
2064 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
2065 int height = This->currentDesc.Height;
2067 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
2068 outpitch = width * bpp;
2069 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
2071 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
2072 if(!mem) {
2073 ERR("Out of memory %d, %d!\n", outpitch, height);
2074 return WINED3DERR_OUTOFVIDEOMEMORY;
2076 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
2078 This->Flags |= SFLAG_CONVERTED;
2079 } else if (This->resource.format == WINED3DFMT_P8 && GL_SUPPORT(EXT_PALETTED_TEXTURE)) {
2080 d3dfmt_p8_upload_palette(iface, convert);
2081 This->Flags &= ~SFLAG_CONVERTED;
2082 mem = This->resource.allocatedMemory;
2083 } else {
2084 This->Flags &= ~SFLAG_CONVERTED;
2085 mem = This->resource.allocatedMemory;
2088 /* Make sure the correct pitch is used */
2089 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
2091 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
2092 TRACE("non power of two support\n");
2093 if(!(This->Flags & SFLAG_ALLOCATED)) {
2094 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
2096 if (mem) {
2097 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
2099 } else {
2100 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
2101 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
2103 if(!(This->Flags & SFLAG_ALLOCATED)) {
2104 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
2106 if (mem) {
2107 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
2111 /* Restore the default pitch */
2112 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2114 if (mem != This->resource.allocatedMemory)
2115 HeapFree(GetProcessHeap(), 0, mem);
2117 #if 0
2119 static unsigned int gen = 0;
2120 char buffer[4096];
2121 ++gen;
2122 if ((gen % 10) == 0) {
2123 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2124 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2127 * debugging crash code
2128 if (gen == 250) {
2129 void** test = NULL;
2130 *test = 0;
2134 #endif
2136 if (!(This->Flags & SFLAG_DONOTFREE)) {
2137 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
2138 This->resource.allocatedMemory = NULL;
2139 This->Flags &= ~SFLAG_INSYSMEM;
2142 return WINED3D_OK;
2145 #include <errno.h>
2146 #include <stdio.h>
2147 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2148 FILE* f = NULL;
2149 UINT i, y;
2150 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2151 char *allocatedMemory;
2152 char *textureRow;
2153 IWineD3DSwapChain *swapChain = NULL;
2154 int width, height;
2155 GLuint tmpTexture = 0;
2156 DWORD color;
2157 /*FIXME:
2158 Textures may not be stored in ->allocatedgMemory and a GlTexture
2159 so we should lock the surface before saving a snapshot, or at least check that
2161 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2162 by calling GetTexImage and in compressed form by calling
2163 GetCompressedTexImageARB. Queried compressed images can be saved and
2164 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2165 texture images do not need to be processed by the GL and should
2166 significantly improve texture loading performance relative to uncompressed
2167 images. */
2169 /* Setup the width and height to be the internal texture width and height. */
2170 width = This->pow2Width;
2171 height = This->pow2Height;
2172 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2173 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2175 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2176 /* if were not a real texture then read the back buffer into a real texture */
2177 /* we don't want to interfere with the back buffer so read the data into a temporary
2178 * texture and then save the data out of the temporary texture
2180 GLint prevRead;
2181 ENTER_GL();
2182 TRACE("(%p) Reading render target into texture\n", This);
2183 glEnable(GL_TEXTURE_2D);
2185 glGenTextures(1, &tmpTexture);
2186 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2188 glTexImage2D(GL_TEXTURE_2D,
2190 GL_RGBA,
2191 width,
2192 height,
2193 0/*border*/,
2194 GL_RGBA,
2195 GL_UNSIGNED_INT_8_8_8_8_REV,
2196 NULL);
2198 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2199 vcheckGLcall("glGetIntegerv");
2200 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2201 vcheckGLcall("glReadBuffer");
2202 glCopyTexImage2D(GL_TEXTURE_2D,
2204 GL_RGBA,
2207 width,
2208 height,
2211 checkGLcall("glCopyTexImage2D");
2212 glReadBuffer(prevRead);
2213 LEAVE_GL();
2215 } else { /* bind the real texture, and make sure it up to date */
2216 IWineD3DSurface_PreLoad(iface);
2218 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2219 ENTER_GL();
2220 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2221 glGetTexImage(GL_TEXTURE_2D,
2222 This->glDescription.level,
2223 GL_RGBA,
2224 GL_UNSIGNED_INT_8_8_8_8_REV,
2225 allocatedMemory);
2226 checkGLcall("glTexImage2D");
2227 if (tmpTexture) {
2228 glBindTexture(GL_TEXTURE_2D, 0);
2229 glDeleteTextures(1, &tmpTexture);
2231 LEAVE_GL();
2233 f = fopen(filename, "w+");
2234 if (NULL == f) {
2235 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2236 return WINED3DERR_INVALIDCALL;
2238 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2239 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2240 /* TGA header */
2241 fputc(0,f);
2242 fputc(0,f);
2243 fputc(2,f);
2244 fputc(0,f);
2245 fputc(0,f);
2246 fputc(0,f);
2247 fputc(0,f);
2248 fputc(0,f);
2249 fputc(0,f);
2250 fputc(0,f);
2251 fputc(0,f);
2252 fputc(0,f);
2253 /* short width*/
2254 fwrite(&width,2,1,f);
2255 /* short height */
2256 fwrite(&height,2,1,f);
2257 /* format rgba */
2258 fputc(0x20,f);
2259 fputc(0x28,f);
2260 /* raw data */
2261 /* 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 */
2262 if(swapChain)
2263 textureRow = allocatedMemory + (width * (height - 1) *4);
2264 else
2265 textureRow = allocatedMemory;
2266 for (y = 0 ; y < height; y++) {
2267 for (i = 0; i < width; i++) {
2268 color = *((DWORD*)textureRow);
2269 fputc((color >> 16) & 0xFF, f); /* B */
2270 fputc((color >> 8) & 0xFF, f); /* G */
2271 fputc((color >> 0) & 0xFF, f); /* R */
2272 fputc((color >> 24) & 0xFF, f); /* A */
2273 textureRow += 4;
2275 /* take two rows of the pointer to the texture memory */
2276 if(swapChain)
2277 (textureRow-= width << 3);
2280 TRACE("Closing file\n");
2281 fclose(f);
2283 if(swapChain) {
2284 IWineD3DSwapChain_Release(swapChain);
2286 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2287 return WINED3D_OK;
2291 * Slightly inefficient way to handle multiple dirty rects but it works :)
2293 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2294 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2295 IWineD3DBaseTexture *baseTexture = NULL;
2296 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2297 surface_download_data(This);
2299 This->Flags &= ~(SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
2300 if (NULL != pDirtyRect) {
2301 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2302 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2303 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2304 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2305 } else {
2306 This->dirtyRect.left = 0;
2307 This->dirtyRect.top = 0;
2308 This->dirtyRect.right = This->currentDesc.Width;
2309 This->dirtyRect.bottom = This->currentDesc.Height;
2311 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2312 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2313 /* if the container is a basetexture then mark it dirty. */
2314 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2315 TRACE("Passing to container\n");
2316 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2317 IWineD3DBaseTexture_Release(baseTexture);
2319 return WINED3D_OK;
2322 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
2323 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2325 TRACE("This %p, container %p\n", This, container);
2327 /* We can't keep a reference to the container, since the container already keeps a reference to us. */
2329 TRACE("Setting container to %p from %p\n", container, This->container);
2330 This->container = container;
2332 return WINED3D_OK;
2335 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2336 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2337 const GlPixelFormatDesc *glDesc;
2338 const StaticPixelFormatDesc *formatEntry = getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2340 if (This->resource.format != WINED3DFMT_UNKNOWN) {
2341 FIXME("(%p) : The format of the surface must be WINED3DFORMAT_UNKNOWN\n", This);
2342 return WINED3DERR_INVALIDCALL;
2345 TRACE("(%p) : Setting texture format to (%d,%s)\n", This, format, debug_d3dformat(format));
2346 if (format == WINED3DFMT_UNKNOWN) {
2347 This->resource.size = 0;
2348 } else if (format == WINED3DFMT_DXT1) {
2349 /* DXT1 is half byte per pixel */
2350 This->resource.size = ((max(This->pow2Width, 4) * formatEntry->bpp) * max(This->pow2Height, 4)) >> 1;
2352 } else if (format == WINED3DFMT_DXT2 || format == WINED3DFMT_DXT3 ||
2353 format == WINED3DFMT_DXT4 || format == WINED3DFMT_DXT5) {
2354 This->resource.size = ((max(This->pow2Width, 4) * formatEntry->bpp) * max(This->pow2Height, 4));
2355 } else {
2356 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
2357 This->resource.size = ((This->pow2Width * formatEntry->bpp) + alignment - 1) & ~(alignment - 1);
2358 This->resource.size *= This->pow2Height;
2362 /* Setup some glformat defaults */
2363 This->glDescription.glFormat = glDesc->glFormat;
2364 This->glDescription.glFormatInternal = glDesc->glInternal;
2365 This->glDescription.glType = glDesc->glType;
2367 if (format != WINED3DFMT_UNKNOWN) {
2368 This->bytesPerPixel = formatEntry->bpp;
2369 } else {
2370 This->bytesPerPixel = 0;
2373 This->Flags |= (WINED3DFMT_D16_LOCKABLE == format) ? SFLAG_LOCKABLE : 0;
2374 This->Flags &= ~SFLAG_ALLOCATED;
2376 This->resource.format = format;
2378 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);
2380 return WINED3D_OK;
2383 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2384 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2386 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer */
2387 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
2388 ERR("Not supported on render targets\n");
2389 return WINED3DERR_INVALIDCALL;
2392 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2393 WARN("Surface is locked or the HDC is in use\n");
2394 return WINED3DERR_INVALIDCALL;
2397 if(Mem && Mem != This->resource.allocatedMemory) {
2398 void *release = NULL;
2400 /* Do I have to copy the old surface content? */
2401 if(This->Flags & SFLAG_DIBSECTION) {
2402 /* Release the DC. No need to hold the critical section for the update
2403 * Thread because this thread runs only on front buffers, but this method
2404 * fails for render targets in the check above.
2406 SelectObject(This->hDC, This->dib.holdbitmap);
2407 DeleteDC(This->hDC);
2408 /* Release the DIB section */
2409 DeleteObject(This->dib.DIBsection);
2410 This->dib.bitmap_data = NULL;
2411 This->resource.allocatedMemory = NULL;
2412 This->hDC = NULL;
2413 This->Flags &= ~SFLAG_DIBSECTION;
2414 } else if(!(This->Flags & SFLAG_USERPTR)) {
2415 release = This->resource.allocatedMemory;
2417 This->resource.allocatedMemory = Mem;
2418 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2420 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2421 This->Flags &= ~(SFLAG_INDRAWABLE | SFLAG_INTEXTURE);
2423 /* For client textures opengl has to be notified */
2424 if(This->Flags & SFLAG_CLIENT) {
2425 This->Flags &= ~SFLAG_ALLOCATED;
2426 IWineD3DSurface_PreLoad(iface);
2427 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2430 /* Now free the old memory if any */
2431 HeapFree(GetProcessHeap(), 0, release);
2432 } else if(This->Flags & SFLAG_USERPTR) {
2433 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2434 This->resource.allocatedMemory = NULL;
2435 This->Flags &= ~SFLAG_USERPTR;
2437 if(This->Flags & SFLAG_CLIENT) {
2438 This->Flags &= ~SFLAG_ALLOCATED;
2439 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2440 IWineD3DSurface_PreLoad(iface);
2443 return WINED3D_OK;
2446 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2447 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2448 IWineD3DSwapChainImpl *swapchain = NULL;
2449 HRESULT hr;
2450 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2452 /* Flipping is only supported on RenderTargets */
2453 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2455 if(override) {
2456 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2457 * FIXME("(%p) Target override is not supported by now\n", This);
2458 * Additionally, it isn't really possible to support triple-buffering
2459 * properly on opengl at all
2463 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2464 if(!swapchain) {
2465 ERR("Flipped surface is not on a swapchain\n");
2466 return WINEDDERR_NOTFLIPPABLE;
2469 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2470 * and only d3d8 and d3d9 apps specify the presentation interval
2472 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2473 /* Most common case first to avoid wasting time on all the other cases */
2474 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2475 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2476 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2477 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2478 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2479 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2480 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2481 } else {
2482 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2485 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2486 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2487 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2488 return hr;
2491 /* Does a direct frame buffer -> texture copy. Stretching is done
2492 * with single pixel copy calls
2494 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2495 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2496 float xrel, yrel;
2497 UINT row;
2498 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2501 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2502 ENTER_GL();
2503 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2505 /* Bind the target texture */
2506 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
2507 checkGLcall("glBindTexture");
2508 if(!swapchain) {
2509 glReadBuffer(myDevice->offscreenBuffer);
2510 } else {
2511 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2512 glReadBuffer(buffer);
2514 checkGLcall("glReadBuffer");
2516 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2517 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2519 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2520 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2522 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2523 ERR("Texture filtering not supported in direct blit\n");
2525 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2526 ERR("Texture filtering not supported in direct blit\n");
2529 if(upsidedown &&
2530 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2531 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2532 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2534 glCopyTexSubImage2D(This->glDescription.target,
2535 This->glDescription.level,
2536 drect->x1, drect->y1, /* xoffset, yoffset */
2537 srect->x1, Src->currentDesc.Height - srect->y2,
2538 drect->x2 - drect->x1, drect->y2 - drect->y1);
2539 } else {
2540 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2541 /* I have to process this row by row to swap the image,
2542 * otherwise it would be upside down, so stretching in y direction
2543 * doesn't cost extra time
2545 * However, stretching in x direction can be avoided if not necessary
2547 for(row = drect->y1; row < drect->y2; row++) {
2548 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2549 /* Well, that stuff works, but it's very slow.
2550 * find a better way instead
2552 UINT col;
2554 for(col = drect->x1; col < drect->x2; col++) {
2555 glCopyTexSubImage2D(This->glDescription.target,
2556 This->glDescription.level,
2557 drect->x1 + col, row, /* xoffset, yoffset */
2558 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2559 1, 1);
2561 } else {
2562 glCopyTexSubImage2D(This->glDescription.target,
2563 This->glDescription.level,
2564 drect->x1, row, /* xoffset, yoffset */
2565 srect->x1, yoffset - (int) (row * yrel),
2566 drect->x2-drect->x1, 1);
2571 vcheckGLcall("glCopyTexSubImage2D");
2572 LEAVE_GL();
2575 /* Uses the hardware to stretch and flip the image */
2576 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2577 GLuint src, backup = 0;
2578 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2579 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2580 float left, right, top, bottom; /* Texture coordinates */
2581 UINT fbwidth = Src->currentDesc.Width;
2582 UINT fbheight = Src->currentDesc.Height;
2583 GLenum drawBuffer = GL_BACK;
2585 TRACE("Using hwstretch blit\n");
2586 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2587 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2588 ENTER_GL();
2589 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2591 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2592 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2594 if(GL_LIMITS(aux_buffers) >= 2) {
2595 /* Got more than one aux buffer? Use the 2nd aux buffer */
2596 drawBuffer = GL_AUX1;
2597 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2598 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2599 drawBuffer = GL_AUX0;
2602 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2603 glGenTextures(1, &backup);
2604 checkGLcall("glGenTextures\n");
2605 glBindTexture(GL_TEXTURE_2D, backup);
2606 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2607 } else {
2608 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2609 * we are reading from the back buffer, the backup can be used as source texture
2611 if(Src->glDescription.textureName == 0) {
2612 /* Get it a description */
2613 IWineD3DSurface_PreLoad(SrcSurface);
2615 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
2616 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2618 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2619 Src->Flags &= ~SFLAG_INTEXTURE;
2622 glReadBuffer(GL_BACK);
2623 checkGLcall("glReadBuffer(GL_BACK)");
2625 /* TODO: Only back up the part that will be overwritten */
2626 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2627 0, 0 /* read offsets */,
2628 0, 0,
2629 fbwidth,
2630 fbheight);
2632 checkGLcall("glCopyTexSubImage2D");
2634 /* No issue with overriding these - the sampler is dirty due to blit usage */
2635 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
2636 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2637 checkGLcall("glTexParameteri");
2638 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
2639 minMipLookup[Filter][WINED3DTEXF_NONE]);
2640 checkGLcall("glTexParameteri");
2642 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2643 src = backup ? backup : Src->glDescription.textureName;
2644 } else {
2645 glReadBuffer(GL_FRONT);
2646 checkGLcall("glReadBuffer(GL_FRONT)");
2648 glGenTextures(1, &src);
2649 checkGLcall("glGenTextures(1, &src)");
2650 glBindTexture(GL_TEXTURE_2D, src);
2651 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2653 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2654 * out for power of 2 sizes
2656 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2657 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2658 checkGLcall("glTexImage2D");
2659 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2660 0, 0 /* read offsets */,
2661 0, 0,
2662 fbwidth,
2663 fbheight);
2665 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2666 checkGLcall("glTexParameteri");
2667 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2668 checkGLcall("glTexParameteri");
2670 glReadBuffer(GL_BACK);
2671 checkGLcall("glReadBuffer(GL_BACK)");
2673 checkGLcall("glEnd and previous");
2675 left = (float) srect->x1 / (float) Src->pow2Width;
2676 right = (float) srect->x2 / (float) Src->pow2Width;
2678 if(upsidedown) {
2679 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2680 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2681 } else {
2682 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2683 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2686 /* draw the source texture stretched and upside down. The correct surface is bound already */
2687 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
2688 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
2690 glDrawBuffer(drawBuffer);
2691 glReadBuffer(drawBuffer);
2693 glBegin(GL_QUADS);
2694 /* bottom left */
2695 glTexCoord2f(left, bottom);
2696 glVertex2i(0, fbheight);
2698 /* top left */
2699 glTexCoord2f(left, top);
2700 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2702 /* top right */
2703 glTexCoord2f(right, top);
2704 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2706 /* bottom right */
2707 glTexCoord2f(right, bottom);
2708 glVertex2i(drect->x2 - drect->x1, fbheight);
2709 glEnd();
2710 checkGLcall("glEnd and previous");
2712 /* Now read the stretched and upside down image into the destination texture */
2713 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2714 checkGLcall("glBindTexture");
2715 glCopyTexSubImage2D(This->glDescription.target,
2717 drect->x1, drect->y1, /* xoffset, yoffset */
2718 0, 0, /* We blitted the image to the origin */
2719 drect->x2 - drect->x1, drect->y2 - drect->y1);
2720 checkGLcall("glCopyTexSubImage2D");
2722 /* Write the back buffer backup back */
2723 glBindTexture(GL_TEXTURE_2D, backup ? backup : Src->glDescription.textureName);
2724 checkGLcall("glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName)");
2726 if(drawBuffer == GL_BACK) {
2727 glBegin(GL_QUADS);
2728 /* top left */
2729 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2730 glVertex2i(0, 0);
2732 /* bottom left */
2733 glTexCoord2f(0.0, 0.0);
2734 glVertex2i(0, fbheight);
2736 /* bottom right */
2737 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2738 glVertex2i(fbwidth, Src->currentDesc.Height);
2740 /* top right */
2741 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2742 glVertex2i(fbwidth, 0);
2743 glEnd();
2744 } else {
2745 /* Restore the old draw buffer */
2746 glDrawBuffer(GL_BACK);
2749 /* Cleanup */
2750 if(src != Src->glDescription.textureName && src != backup) {
2751 glDeleteTextures(1, &src);
2752 checkGLcall("glDeleteTextures(1, &src)");
2754 if(backup) {
2755 glDeleteTextures(1, &backup);
2756 checkGLcall("glDeleteTextures(1, &backup)");
2758 LEAVE_GL();
2761 /* Not called from the VTable */
2762 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2763 WINED3DRECT rect;
2764 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2765 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2766 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2768 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2770 /* Get the swapchain. One of the surfaces has to be a primary surface */
2771 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2772 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2773 if(Src) {
2774 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2775 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2778 /* Early sort out of cases where no render target is used */
2779 if(!dstSwapchain && !srcSwapchain &&
2780 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2781 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2782 return WINED3DERR_INVALIDCALL;
2785 /* No destination color keying supported */
2786 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
2787 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2788 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2789 return WINED3DERR_INVALIDCALL;
2792 if (DestRect) {
2793 rect.x1 = DestRect->left;
2794 rect.y1 = DestRect->top;
2795 rect.x2 = DestRect->right;
2796 rect.y2 = DestRect->bottom;
2797 } else {
2798 rect.x1 = 0;
2799 rect.y1 = 0;
2800 rect.x2 = This->currentDesc.Width;
2801 rect.y2 = This->currentDesc.Height;
2804 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2805 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
2806 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
2807 /* Half-life does a Blt from the back buffer to the front buffer,
2808 * Full surface size, no flags... Use present instead
2810 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
2813 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2814 while(1)
2816 RECT mySrcRect;
2817 TRACE("Looking if a Present can be done...\n");
2818 /* Source Rectangle must be full surface */
2819 if( SrcRect ) {
2820 if(SrcRect->left != 0 || SrcRect->top != 0 ||
2821 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
2822 TRACE("No, Source rectangle doesn't match\n");
2823 break;
2826 mySrcRect.left = 0;
2827 mySrcRect.top = 0;
2828 mySrcRect.right = Src->currentDesc.Width;
2829 mySrcRect.bottom = Src->currentDesc.Height;
2831 /* No stretching may occur */
2832 if(mySrcRect.right != rect.x2 - rect.x1 ||
2833 mySrcRect.bottom != rect.y2 - rect.y1) {
2834 TRACE("No, stretching is done\n");
2835 break;
2838 /* Destination must be full surface or match the clipping rectangle */
2839 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
2841 RECT cliprect;
2842 POINT pos[2];
2843 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
2844 pos[0].x = rect.x1;
2845 pos[0].y = rect.y1;
2846 pos[1].x = rect.x2;
2847 pos[1].y = rect.y2;
2848 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
2849 pos, 2);
2851 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
2852 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
2854 TRACE("No, dest rectangle doesn't match(clipper)\n");
2855 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
2856 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
2857 break;
2860 else
2862 if(rect.x1 != 0 || rect.y1 != 0 ||
2863 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
2864 TRACE("No, dest rectangle doesn't match(surface size)\n");
2865 break;
2869 TRACE("Yes\n");
2871 /* These flags are unimportant for the flag check, remove them */
2872 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
2873 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
2875 /* The idea behind this is that a glReadPixels and a glDrawPixels call
2876 * take very long, while a flip is fast.
2877 * This applies to Half-Life, which does such Blts every time it finished
2878 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
2879 * menu. This is also used by all apps when they do windowed rendering
2881 * The problem is that flipping is not really the same as copying. After a
2882 * Blt the front buffer is a copy of the back buffer, and the back buffer is
2883 * untouched. Therefore it's necessary to override the swap effect
2884 * and to set it back after the flip.
2886 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
2887 * testcases.
2890 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
2891 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2893 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
2894 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
2896 dstSwapchain->presentParms.SwapEffect = orig_swap;
2898 return WINED3D_OK;
2900 break;
2903 TRACE("Unsupported blit between buffers on the same swapchain\n");
2904 return WINED3DERR_INVALIDCALL;
2905 } else if((dstSwapchain || This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) &&
2906 (srcSwapchain || SrcSurface == myDevice->render_targets[0]) ) {
2907 ERR("Can't perform hardware blit between 2 different swapchains, falling back to software\n");
2908 return WINED3DERR_INVALIDCALL;
2911 if(srcSwapchain || SrcSurface == myDevice->render_targets[0]) {
2912 /* Blit from render target to texture */
2913 WINED3DRECT srect;
2914 BOOL upsideDown, stretchx;
2916 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
2917 TRACE("Color keying not supported by frame buffer to texture blit\n");
2918 return WINED3DERR_INVALIDCALL;
2919 /* Destination color key is checked above */
2922 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2923 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2925 if(SrcRect) {
2926 if(SrcRect->top < SrcRect->bottom) {
2927 srect.y1 = SrcRect->top;
2928 srect.y2 = SrcRect->bottom;
2929 upsideDown = FALSE;
2930 } else {
2931 srect.y1 = SrcRect->bottom;
2932 srect.y2 = SrcRect->top;
2933 upsideDown = TRUE;
2935 srect.x1 = SrcRect->left;
2936 srect.x2 = SrcRect->right;
2937 } else {
2938 srect.x1 = 0;
2939 srect.y1 = 0;
2940 srect.x2 = Src->currentDesc.Width;
2941 srect.y2 = Src->currentDesc.Height;
2942 upsideDown = FALSE;
2944 if(rect.x1 > rect.x2) {
2945 UINT tmp = rect.x2;
2946 rect.x2 = rect.x1;
2947 rect.x1 = tmp;
2948 upsideDown = !upsideDown;
2950 if(!srcSwapchain) {
2951 TRACE("Reading from an offscreen target\n");
2952 upsideDown = !upsideDown;
2955 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
2956 stretchx = TRUE;
2957 } else {
2958 stretchx = FALSE;
2961 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2962 * flip the image nor scale it.
2964 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2965 * -> If the app wants a image width an unscaled width, copy it line per line
2966 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
2967 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2968 * back buffer. This is slower than reading line per line, thus not used for flipping
2969 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2970 * pixel by pixel
2972 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
2973 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
2974 * backends.
2976 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
2977 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
2978 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
2979 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
2980 rect.y2 - rect.y1 > Src->currentDesc.Height) {
2981 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
2982 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
2983 } else {
2984 TRACE("Using hardware stretching to flip / stretch the texture\n");
2985 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
2988 if(!(This->Flags & SFLAG_DONOTFREE)) {
2989 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
2990 This->resource.allocatedMemory = NULL;
2991 } else {
2992 This->Flags &= ~SFLAG_INSYSMEM;
2994 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
2995 * path is never entered
2997 This->Flags |= SFLAG_INTEXTURE;
2999 return WINED3D_OK;
3000 } else if(Src) {
3001 /* Blit from offscreen surface to render target */
3002 float glTexCoord[4];
3003 DWORD oldCKeyFlags = Src->CKeyFlags;
3004 WINEDDCOLORKEY oldBltCKey = This->SrcBltCKey;
3005 RECT SourceRectangle;
3007 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3009 if(SrcRect) {
3010 SourceRectangle.left = SrcRect->left;
3011 SourceRectangle.right = SrcRect->right;
3012 SourceRectangle.top = SrcRect->top;
3013 SourceRectangle.bottom = SrcRect->bottom;
3014 } else {
3015 SourceRectangle.left = 0;
3016 SourceRectangle.right = Src->currentDesc.Width;
3017 SourceRectangle.top = 0;
3018 SourceRectangle.bottom = Src->currentDesc.Height;
3021 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3022 /* Fall back to software */
3023 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3024 SourceRectangle.left, SourceRectangle.top,
3025 SourceRectangle.right, SourceRectangle.bottom);
3026 return WINED3DERR_INVALIDCALL;
3029 /* Color keying: Check if we have to do a color keyed blt,
3030 * and if not check if a color key is activated.
3032 * Just modify the color keying parameters in the surface and restore them afterwards
3033 * The surface keeps track of the color key last used to load the opengl surface.
3034 * PreLoad will catch the change to the flags and color key and reload if necessary.
3036 if(Flags & WINEDDBLT_KEYSRC) {
3037 /* Use color key from surface */
3038 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3039 /* Use color key from DDBltFx */
3040 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3041 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3042 } else {
3043 /* Do not use color key */
3044 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3047 /* Now load the surface */
3048 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3051 /* Activate the destination context, set it up for blitting */
3052 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3053 ENTER_GL();
3055 if(!dstSwapchain) {
3056 TRACE("Drawing to offscreen buffer\n");
3057 glDrawBuffer(myDevice->offscreenBuffer);
3058 checkGLcall("glDrawBuffer");
3059 } else {
3060 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
3061 TRACE("Drawing to %#x buffer\n", buffer);
3062 glDrawBuffer(buffer);
3063 checkGLcall("glDrawBuffer");
3066 /* Bind the texture */
3067 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
3068 checkGLcall("glBindTexture");
3070 /* Filtering for StretchRect */
3071 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
3072 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
3073 checkGLcall("glTexParameteri");
3074 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
3075 minMipLookup[Filter][WINED3DTEXF_NONE]);
3076 checkGLcall("glTexParameteri");
3077 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
3078 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
3079 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3080 checkGLcall("glTexEnvi");
3082 /* This is for color keying */
3083 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3084 glEnable(GL_ALPHA_TEST);
3085 checkGLcall("glEnable GL_ALPHA_TEST");
3086 glAlphaFunc(GL_NOTEQUAL, 0.0);
3087 checkGLcall("glAlphaFunc\n");
3088 } else {
3089 glDisable(GL_ALPHA_TEST);
3090 checkGLcall("glDisable GL_ALPHA_TEST");
3093 /* Draw a textured quad
3095 glBegin(GL_QUADS);
3097 glColor3d(1.0f, 1.0f, 1.0f);
3098 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3099 glVertex3f(rect.x1,
3100 rect.y1,
3101 0.0);
3103 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3104 glVertex3f(rect.x1, rect.y2, 0.0);
3106 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3107 glVertex3f(rect.x2,
3108 rect.y2,
3109 0.0);
3111 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3112 glVertex3f(rect.x2,
3113 rect.y1,
3114 0.0);
3115 glEnd();
3116 checkGLcall("glEnd");
3118 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3119 glDisable(GL_ALPHA_TEST);
3120 checkGLcall("glDisable(GL_ALPHA_TEST)");
3123 /* Unbind the texture */
3124 glBindTexture(GL_TEXTURE_2D, 0);
3125 checkGLcall("glEnable glBindTexture");
3127 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3128 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3130 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3131 glDrawBuffer(GL_BACK);
3132 checkGLcall("glDrawBuffer");
3134 /* Restore the color key parameters */
3135 Src->CKeyFlags = oldCKeyFlags;
3136 This->SrcBltCKey = oldBltCKey;
3138 LEAVE_GL();
3140 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3141 This->Flags &= ~SFLAG_INSYSMEM;
3142 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3143 * is outdated now
3145 if(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO) {
3146 This->Flags |= SFLAG_INDRAWABLE;
3147 This->Flags &= ~SFLAG_INTEXTURE;
3148 } else {
3149 This->Flags |= SFLAG_INTEXTURE;
3152 return WINED3D_OK;
3153 } else {
3154 /* Source-Less Blit to render target */
3155 if (Flags & WINEDDBLT_COLORFILL) {
3156 /* This is easy to handle for the D3D Device... */
3157 DWORD color;
3159 TRACE("Colorfill\n");
3161 /* The color as given in the Blt function is in the format of the frame-buffer...
3162 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3164 if (This->resource.format == WINED3DFMT_P8) {
3165 if (This->palette) {
3166 color = ((0xFF000000) |
3167 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3168 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3169 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3170 } else {
3171 color = 0xFF000000;
3174 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3175 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3176 color = 0xFFFFFFFF;
3177 } else {
3178 color = ((0xFF000000) |
3179 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3180 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3181 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3184 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3185 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3186 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3188 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3189 color = DDBltFx->u5.dwFillColor;
3191 else {
3192 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3193 return WINED3DERR_INVALIDCALL;
3196 TRACE("Calling GetSwapChain with mydevice = %p\n", myDevice);
3197 if(dstSwapchain && dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]) {
3198 glDrawBuffer(GL_BACK);
3199 checkGLcall("glDrawBuffer(GL_BACK)");
3200 } else if (dstSwapchain && This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer) {
3201 glDrawBuffer(GL_FRONT);
3202 checkGLcall("glDrawBuffer(GL_FRONT)");
3203 } else if(This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3204 glDrawBuffer(myDevice->offscreenBuffer);
3205 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer3)");
3206 } else {
3207 TRACE("Surface is higher back buffer, falling back to software\n");
3208 return WINED3DERR_INVALIDCALL;
3211 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3213 IWineD3DDevice_Clear( (IWineD3DDevice *) myDevice,
3214 1 /* Number of rectangles */,
3215 &rect,
3216 WINED3DCLEAR_TARGET,
3217 color,
3218 0.0 /* Z */,
3219 0 /* Stencil */);
3221 /* Restore the original draw buffer */
3222 if(!dstSwapchain) {
3223 glDrawBuffer(myDevice->offscreenBuffer);
3224 } else if(dstSwapchain->backBuffer && dstSwapchain->backBuffer[0]) {
3225 glDrawBuffer(GL_BACK);
3227 vcheckGLcall("glDrawBuffer");
3229 return WINED3D_OK;
3233 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3234 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3235 return WINED3DERR_INVALIDCALL;
3238 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3240 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3242 if (Flags & WINEDDBLT_DEPTHFILL)
3243 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3244 DestRect == NULL ? 0 : 1,
3245 (WINED3DRECT *) DestRect,
3246 WINED3DCLEAR_ZBUFFER,
3247 0x00000000,
3248 (float) DDBltFx->u5.dwFillDepth / (float) MAXDWORD,
3249 0x00000000);
3251 FIXME("(%p): Unsupp depthstencil blit\n", This);
3252 return WINED3DERR_INVALIDCALL;
3255 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3256 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3257 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3258 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3259 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3260 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3262 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3263 * except depth blits, which seem to work
3265 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3266 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3267 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3268 return WINED3DERR_INVALIDCALL;
3269 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3270 TRACE("Z Blit override handled the blit\n");
3271 return WINED3D_OK;
3275 /* Special cases for RenderTargets */
3276 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3277 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3278 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3281 /* For the rest call the X11 surface implementation.
3282 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3283 * other Blts are rather rare
3285 return IWineGDISurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3288 HRESULT WINAPI IWineD3DSurfaceImpl_GetBltStatus(IWineD3DSurface *iface, DWORD Flags) {
3289 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3290 TRACE("(%p)->(%x)\n", This, Flags);
3292 switch (Flags)
3294 case WINEDDGBS_CANBLT:
3295 case WINEDDGBS_ISBLTDONE:
3296 return WINED3D_OK;
3298 default:
3299 return WINED3DERR_INVALIDCALL;
3303 HRESULT WINAPI IWineD3DSurfaceImpl_GetFlipStatus(IWineD3DSurface *iface, DWORD Flags) {
3304 /* XXX: DDERR_INVALIDSURFACETYPE */
3306 TRACE("(%p)->(%08x)\n",iface,Flags);
3307 switch (Flags) {
3308 case WINEDDGFS_CANFLIP:
3309 case WINEDDGFS_ISFLIPDONE:
3310 return WINED3D_OK;
3312 default:
3313 return WINED3DERR_INVALIDCALL;
3317 HRESULT WINAPI IWineD3DSurfaceImpl_IsLost(IWineD3DSurface *iface) {
3318 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3319 TRACE("(%p)\n", This);
3321 /* D3D8 and 9 loose full devices, ddraw only surfaces */
3322 return This->Flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
3325 HRESULT WINAPI IWineD3DSurfaceImpl_Restore(IWineD3DSurface *iface) {
3326 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3327 TRACE("(%p)\n", This);
3329 /* So far we don't lose anything :) */
3330 This->Flags &= ~SFLAG_LOST;
3331 return WINED3D_OK;
3334 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3335 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3336 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3337 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3338 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3340 if(myDevice->inScene &&
3341 (iface == myDevice->stencilBufferTarget ||
3342 (Source && Source == myDevice->stencilBufferTarget))) {
3343 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3344 return WINED3DERR_INVALIDCALL;
3347 /* Special cases for RenderTargets */
3348 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3349 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3351 RECT SrcRect, DstRect;
3352 DWORD Flags=0;
3354 if(rsrc) {
3355 SrcRect.left = rsrc->left;
3356 SrcRect.top= rsrc->top;
3357 SrcRect.bottom = rsrc->bottom;
3358 SrcRect.right = rsrc->right;
3359 } else {
3360 SrcRect.left = 0;
3361 SrcRect.top = 0;
3362 SrcRect.right = srcImpl->currentDesc.Width;
3363 SrcRect.bottom = srcImpl->currentDesc.Height;
3366 DstRect.left = dstx;
3367 DstRect.top=dsty;
3368 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3369 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3371 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3372 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3373 Flags |= WINEDDBLT_KEYSRC;
3374 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3375 Flags |= WINEDDBLT_KEYDEST;
3376 if(trans & WINEDDBLTFAST_WAIT)
3377 Flags |= WINEDDBLT_WAIT;
3378 if(trans & WINEDDBLTFAST_DONOTWAIT)
3379 Flags |= WINEDDBLT_DONOTWAIT;
3381 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3385 return IWineGDISurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3388 HRESULT WINAPI IWineD3DSurfaceImpl_GetPalette(IWineD3DSurface *iface, IWineD3DPalette **Pal) {
3389 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3390 TRACE("(%p)->(%p)\n", This, Pal);
3392 *Pal = (IWineD3DPalette *) This->palette;
3393 return WINED3D_OK;
3396 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
3397 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3398 RGBQUAD col[256];
3399 IWineD3DPaletteImpl *pal = This->palette;
3400 unsigned int n;
3401 TRACE("(%p)\n", This);
3403 if(This->resource.format == WINED3DFMT_P8 ||
3404 This->resource.format == WINED3DFMT_A8P8)
3406 if(!This->Flags & SFLAG_INSYSMEM) {
3407 FIXME("Palette changed with surface that does not have an up to date system memory copy\n");
3409 TRACE("Dirtifying surface\n");
3410 This->Flags &= ~(SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3413 if(This->Flags & SFLAG_DIBSECTION) {
3414 TRACE("(%p): Updating the hdc's palette\n", This);
3415 for (n=0; n<256; n++) {
3416 if(pal) {
3417 col[n].rgbRed = pal->palents[n].peRed;
3418 col[n].rgbGreen = pal->palents[n].peGreen;
3419 col[n].rgbBlue = pal->palents[n].peBlue;
3420 } else {
3421 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3422 /* Use the default device palette */
3423 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
3424 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
3425 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
3427 col[n].rgbReserved = 0;
3429 SetDIBColorTable(This->hDC, 0, 256, col);
3432 return WINED3D_OK;
3435 HRESULT WINAPI IWineD3DSurfaceImpl_SetPalette(IWineD3DSurface *iface, IWineD3DPalette *Pal) {
3436 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3437 IWineD3DPaletteImpl *PalImpl = (IWineD3DPaletteImpl *) Pal;
3438 TRACE("(%p)->(%p)\n", This, Pal);
3440 if(This->palette != NULL)
3441 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3442 This->palette->Flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
3444 if(PalImpl != NULL) {
3445 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3446 /* Set the device's main palette if the palette
3447 * wasn't a primary palette before
3449 if(!(PalImpl->Flags & WINEDDPCAPS_PRIMARYSURFACE)) {
3450 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3451 unsigned int i;
3453 for(i=0; i < 256; i++) {
3454 device->palettes[device->currentPalette][i] = PalImpl->palents[i];
3458 (PalImpl)->Flags |= WINEDDPCAPS_PRIMARYSURFACE;
3461 This->palette = PalImpl;
3463 return IWineD3DSurface_RealizePalette(iface);
3466 HRESULT WINAPI IWineD3DSurfaceImpl_SetColorKey(IWineD3DSurface *iface, DWORD Flags, WINEDDCOLORKEY *CKey) {
3467 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3468 TRACE("(%p)->(%08x,%p)\n", This, Flags, CKey);
3470 if ((Flags & WINEDDCKEY_COLORSPACE) != 0) {
3471 FIXME(" colorkey value not supported (%08x) !\n", Flags);
3472 return WINED3DERR_INVALIDCALL;
3475 /* Dirtify the surface, but only if a key was changed */
3476 if(CKey) {
3477 switch (Flags & ~WINEDDCKEY_COLORSPACE) {
3478 case WINEDDCKEY_DESTBLT:
3479 This->DestBltCKey = *CKey;
3480 This->CKeyFlags |= WINEDDSD_CKDESTBLT;
3481 break;
3483 case WINEDDCKEY_DESTOVERLAY:
3484 This->DestOverlayCKey = *CKey;
3485 This->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3486 break;
3488 case WINEDDCKEY_SRCOVERLAY:
3489 This->SrcOverlayCKey = *CKey;
3490 This->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3491 break;
3493 case WINEDDCKEY_SRCBLT:
3494 This->SrcBltCKey = *CKey;
3495 This->CKeyFlags |= WINEDDSD_CKSRCBLT;
3496 break;
3499 else {
3500 switch (Flags & ~WINEDDCKEY_COLORSPACE) {
3501 case WINEDDCKEY_DESTBLT:
3502 This->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3503 break;
3505 case WINEDDCKEY_DESTOVERLAY:
3506 This->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3507 break;
3509 case WINEDDCKEY_SRCOVERLAY:
3510 This->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3511 break;
3513 case WINEDDCKEY_SRCBLT:
3514 This->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3515 break;
3519 return WINED3D_OK;
3522 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3523 /** Check against the maximum texture sizes supported by the video card **/
3524 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3525 unsigned int pow2Width, pow2Height;
3526 const GlPixelFormatDesc *glDesc;
3528 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3529 /* Setup some glformat defaults */
3530 This->glDescription.glFormat = glDesc->glFormat;
3531 This->glDescription.glFormatInternal = glDesc->glInternal;
3532 This->glDescription.glType = glDesc->glType;
3534 This->glDescription.textureName = 0;
3535 This->glDescription.target = GL_TEXTURE_2D;
3537 /* Non-power2 support */
3538 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO)) {
3539 pow2Width = This->currentDesc.Width;
3540 pow2Height = This->currentDesc.Height;
3541 } else {
3542 /* Find the nearest pow2 match */
3543 pow2Width = pow2Height = 1;
3544 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3545 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3547 This->pow2Width = pow2Width;
3548 This->pow2Height = pow2Height;
3550 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3551 WINED3DFORMAT Format = This->resource.format;
3552 /** TODO: add support for non power two compressed textures **/
3553 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3554 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5) {
3555 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3556 This, This->currentDesc.Width, This->currentDesc.Height);
3557 return WINED3DERR_NOTAVAILABLE;
3561 if(pow2Width != This->currentDesc.Width ||
3562 pow2Height != This->currentDesc.Height) {
3563 This->Flags |= SFLAG_NONPOW2;
3566 TRACE("%p\n", This);
3567 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3568 /* one of three options
3569 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)
3570 2: Set the texture to the maximum size (bad idea)
3571 3: WARN and return WINED3DERR_NOTAVAILABLE;
3572 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.
3574 WARN("(%p) Creating an oversized surface\n", This);
3575 This->Flags |= SFLAG_OVERSIZE;
3577 /* This will be initialized on the first blt */
3578 This->glRect.left = 0;
3579 This->glRect.top = 0;
3580 This->glRect.right = 0;
3581 This->glRect.bottom = 0;
3582 } else {
3583 /* No oversize, gl rect is the full texture size */
3584 This->Flags &= ~SFLAG_OVERSIZE;
3585 This->glRect.left = 0;
3586 This->glRect.top = 0;
3587 This->glRect.right = This->pow2Width;
3588 This->glRect.bottom = This->pow2Height;
3591 if(This->resource.allocatedMemory == NULL) {
3592 /* Make sure memory exists from the start, and it is initialized properly. D3D initializes surfaces,
3593 * gl does not, so we need to upload zeroes to init the gl texture.
3595 This->resource.allocatedMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->resource.size + 4);
3597 This->Flags |= SFLAG_INSYSMEM;
3599 return WINED3D_OK;
3602 DWORD WINAPI IWineD3DSurfaceImpl_GetPitch(IWineD3DSurface *iface) {
3603 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3604 DWORD ret;
3605 TRACE("(%p)\n", This);
3607 /* DXTn formats don't have exact pitches as they are to the new row of blocks,
3608 where each block is 4x4 pixels, 8 bytes (dxt1) and 16 bytes (dxt2/3/4/5)
3609 ie pitch = (width/4) * bytes per block */
3610 if (This->resource.format == WINED3DFMT_DXT1) /* DXT1 is 8 bytes per block */
3611 ret = ((This->currentDesc.Width + 3) >> 2) << 3;
3612 else if (This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
3613 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) /* DXT2/3/4/5 is 16 bytes per block */
3614 ret = ((This->currentDesc.Width + 3) >> 2) << 4;
3615 else {
3616 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
3617 ret = This->bytesPerPixel * This->currentDesc.Width; /* Bytes / row */
3618 ret = (ret + alignment - 1) & ~(alignment - 1);
3620 TRACE("(%p) Returning %d\n", This, ret);
3621 return ret;
3624 HRESULT WINAPI IWineD3DSurfaceImpl_SetOverlayPosition(IWineD3DSurface *iface, LONG X, LONG Y) {
3625 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3627 FIXME("(%p)->(%d,%d) Stub!\n", This, X, Y);
3629 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3631 TRACE("(%p): Not an overlay surface\n", This);
3632 return WINEDDERR_NOTAOVERLAYSURFACE;
3635 return WINED3D_OK;
3638 HRESULT WINAPI IWineD3DSurfaceImpl_GetOverlayPosition(IWineD3DSurface *iface, LONG *X, LONG *Y) {
3639 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3641 FIXME("(%p)->(%p,%p) Stub!\n", This, X, Y);
3643 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3645 TRACE("(%p): Not an overlay surface\n", This);
3646 return WINEDDERR_NOTAOVERLAYSURFACE;
3649 return WINED3D_OK;
3652 HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlayZOrder(IWineD3DSurface *iface, DWORD Flags, IWineD3DSurface *Ref) {
3653 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3654 IWineD3DSurfaceImpl *RefImpl = (IWineD3DSurfaceImpl *) Ref;
3656 FIXME("(%p)->(%08x,%p) Stub!\n", This, Flags, RefImpl);
3658 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3660 TRACE("(%p): Not an overlay surface\n", This);
3661 return WINEDDERR_NOTAOVERLAYSURFACE;
3664 return WINED3D_OK;
3667 HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlay(IWineD3DSurface *iface, RECT *SrcRect, IWineD3DSurface *DstSurface, RECT *DstRect, DWORD Flags, WINEDDOVERLAYFX *FX) {
3668 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3669 IWineD3DSurfaceImpl *Dst = (IWineD3DSurfaceImpl *) DstSurface;
3670 FIXME("(%p)->(%p, %p, %p, %08x, %p)\n", This, SrcRect, Dst, DstRect, Flags, FX);
3672 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3674 TRACE("(%p): Not an overlay surface\n", This);
3675 return WINEDDERR_NOTAOVERLAYSURFACE;
3678 return WINED3D_OK;
3681 HRESULT WINAPI IWineD3DSurfaceImpl_SetClipper(IWineD3DSurface *iface, IWineD3DClipper *clipper)
3683 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3684 TRACE("(%p)->(%p)\n", This, clipper);
3686 This->clipper = clipper;
3687 return WINED3D_OK;
3690 HRESULT WINAPI IWineD3DSurfaceImpl_GetClipper(IWineD3DSurface *iface, IWineD3DClipper **clipper)
3692 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3693 TRACE("(%p)->(%p)\n", This, clipper);
3695 *clipper = This->clipper;
3696 if(*clipper) {
3697 IWineD3DClipper_AddRef(*clipper);
3699 return WINED3D_OK;
3702 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
3704 /* IUnknown */
3705 IWineD3DSurfaceImpl_QueryInterface,
3706 IWineD3DSurfaceImpl_AddRef,
3707 IWineD3DSurfaceImpl_Release,
3708 /* IWineD3DResource */
3709 IWineD3DSurfaceImpl_GetParent,
3710 IWineD3DSurfaceImpl_GetDevice,
3711 IWineD3DSurfaceImpl_SetPrivateData,
3712 IWineD3DSurfaceImpl_GetPrivateData,
3713 IWineD3DSurfaceImpl_FreePrivateData,
3714 IWineD3DSurfaceImpl_SetPriority,
3715 IWineD3DSurfaceImpl_GetPriority,
3716 IWineD3DSurfaceImpl_PreLoad,
3717 IWineD3DSurfaceImpl_GetType,
3718 /* IWineD3DSurface */
3719 IWineD3DSurfaceImpl_GetContainer,
3720 IWineD3DSurfaceImpl_GetDesc,
3721 IWineD3DSurfaceImpl_LockRect,
3722 IWineD3DSurfaceImpl_UnlockRect,
3723 IWineD3DSurfaceImpl_GetDC,
3724 IWineD3DSurfaceImpl_ReleaseDC,
3725 IWineD3DSurfaceImpl_Flip,
3726 IWineD3DSurfaceImpl_Blt,
3727 IWineD3DSurfaceImpl_GetBltStatus,
3728 IWineD3DSurfaceImpl_GetFlipStatus,
3729 IWineD3DSurfaceImpl_IsLost,
3730 IWineD3DSurfaceImpl_Restore,
3731 IWineD3DSurfaceImpl_BltFast,
3732 IWineD3DSurfaceImpl_GetPalette,
3733 IWineD3DSurfaceImpl_SetPalette,
3734 IWineD3DSurfaceImpl_RealizePalette,
3735 IWineD3DSurfaceImpl_SetColorKey,
3736 IWineD3DSurfaceImpl_GetPitch,
3737 IWineD3DSurfaceImpl_SetMem,
3738 IWineD3DSurfaceImpl_SetOverlayPosition,
3739 IWineD3DSurfaceImpl_GetOverlayPosition,
3740 IWineD3DSurfaceImpl_UpdateOverlayZOrder,
3741 IWineD3DSurfaceImpl_UpdateOverlay,
3742 IWineD3DSurfaceImpl_SetClipper,
3743 IWineD3DSurfaceImpl_GetClipper,
3744 /* Internal use: */
3745 IWineD3DSurfaceImpl_AddDirtyRect,
3746 IWineD3DSurfaceImpl_LoadTexture,
3747 IWineD3DSurfaceImpl_SaveSnapshot,
3748 IWineD3DSurfaceImpl_SetContainer,
3749 IWineD3DSurfaceImpl_SetGlTextureDesc,
3750 IWineD3DSurfaceImpl_GetGlDesc,
3751 IWineD3DSurfaceImpl_GetData,
3752 IWineD3DSurfaceImpl_SetFormat,
3753 IWineD3DSurfaceImpl_PrivateSetup