push 83f6eeab4f78cf34cba36fe6c2150f9c23ec0aba
[wine/hacks.git] / dlls / wined3d / surface.c
blobb2d3aa1187cced3a50a08abdb84136de4d5f6f7a
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 /* Similar 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 inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
1967 GLboolean oldwrite[4];
1969 /* Some formats have only some color channels, and the others are 1.0.
1970 * since our rendering renders to all channels, and those pixel formats
1971 * are emulated by using a full texture with the other channels set to 1.0
1972 * manually, clear the unused channels.
1974 * This could be done with hacking colorwriteenable to mask the colors,
1975 * but before drawing the buffer would have to be cleared too, so there's
1976 * no gain in that
1978 switch(This->resource.format) {
1979 case WINED3DFMT_R16F:
1980 case WINED3DFMT_R32F:
1981 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
1982 /* Do not activate a context, the correct drawable is active already
1983 * though just the read buffer is set, make sure to have the correct draw
1984 * buffer too
1986 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
1987 glDisable(GL_SCISSOR_TEST);
1988 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
1989 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
1990 glClearColor(0.0, 1.0, 1.0, 1.0);
1991 glClear(GL_COLOR_BUFFER_BIT);
1992 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
1993 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
1994 checkGLcall("Unused channel clear\n");
1995 break;
1997 default: break;
2001 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2002 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2003 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2004 GLenum format, internal, type;
2005 CONVERT_TYPES convert;
2006 int bpp;
2007 int width, pitch, outpitch;
2008 BYTE *mem;
2010 if (!(This->Flags & SFLAG_INTEXTURE)) {
2011 TRACE("Reloading because surface is dirty\n");
2012 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2013 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2014 /* Reload: vice versa OR */
2015 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2016 /* Also reload: Color key is active AND the color key has changed */
2017 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2018 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2019 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2020 TRACE("Reloading because of color keying\n");
2021 } else if(palette9_changed(This)) {
2022 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
2023 } else {
2024 TRACE("surface is already in texture\n");
2025 return WINED3D_OK;
2028 This->Flags |= SFLAG_INTEXTURE;
2030 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2031 * These resources are not bound by device size or format restrictions. Because of this,
2032 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2033 * However, these resources can always be created, locked, and copied.
2035 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2037 FIXME("(%p) Operation not supported for scratch textures\n",This);
2038 return WINED3DERR_INVALIDCALL;
2041 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb_mode);
2043 if (This->Flags & SFLAG_INDRAWABLE) {
2044 if (This->resource.format == WINED3DFMT_P8 || This->resource.format == WINED3DFMT_A8P8 ||
2045 This->resource.format == WINED3DFMT_DXT1 || This->resource.format == WINED3DFMT_DXT2 ||
2046 This->resource.format == WINED3DFMT_DXT3 || This->resource.format == WINED3DFMT_DXT4 ||
2047 This->resource.format == WINED3DFMT_DXT5)
2048 FIXME("Format %d not supported\n", This->resource.format);
2049 else {
2050 GLint prevRead;
2052 ENTER_GL();
2053 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2054 vcheckGLcall("glGetIntegerv");
2055 glReadBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2056 vcheckGLcall("glReadBuffer");
2058 if(!(This->Flags & SFLAG_ALLOCATED)) {
2059 surface_allocate_surface(This, internal, This->pow2Width,
2060 This->pow2Height, format, type);
2063 clear_unused_channels(This);
2065 glCopyTexSubImage2D(This->glDescription.target,
2066 This->glDescription.level,
2067 0, 0, 0, 0,
2068 This->currentDesc.Width,
2069 This->currentDesc.Height);
2070 checkGLcall("glCopyTexSubImage2D");
2072 glReadBuffer(prevRead);
2073 vcheckGLcall("glReadBuffer");
2075 LEAVE_GL();
2077 TRACE("Updated target %d\n", This->glDescription.target);
2079 return WINED3D_OK;
2080 } else
2081 /* The only place where LoadTexture() might get called when isInDraw=1
2082 * is ActivateContext where lastActiveRenderTarget is preloaded.
2084 if(iface == device->lastActiveRenderTarget && device->isInDraw)
2085 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
2087 /* Otherwise: System memory copy must be most up to date */
2089 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
2090 This->Flags |= SFLAG_GLCKEY;
2091 This->glCKey = This->SrcBltCKey;
2093 else This->Flags &= ~SFLAG_GLCKEY;
2095 /* The width is in 'length' not in bytes */
2096 width = This->currentDesc.Width;
2097 pitch = IWineD3DSurface_GetPitch(iface);
2099 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
2100 int height = This->currentDesc.Height;
2102 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
2103 outpitch = width * bpp;
2104 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
2106 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
2107 if(!mem) {
2108 ERR("Out of memory %d, %d!\n", outpitch, height);
2109 return WINED3DERR_OUTOFVIDEOMEMORY;
2111 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
2113 This->Flags |= SFLAG_CONVERTED;
2114 } else if (This->resource.format == WINED3DFMT_P8 && GL_SUPPORT(EXT_PALETTED_TEXTURE)) {
2115 d3dfmt_p8_upload_palette(iface, convert);
2116 This->Flags &= ~SFLAG_CONVERTED;
2117 mem = This->resource.allocatedMemory;
2118 } else {
2119 This->Flags &= ~SFLAG_CONVERTED;
2120 mem = This->resource.allocatedMemory;
2123 /* Make sure the correct pitch is used */
2124 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
2126 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
2127 TRACE("non power of two support\n");
2128 if(!(This->Flags & SFLAG_ALLOCATED)) {
2129 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
2131 if (mem) {
2132 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
2134 } else {
2135 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
2136 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
2138 if(!(This->Flags & SFLAG_ALLOCATED)) {
2139 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
2141 if (mem) {
2142 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
2146 /* Restore the default pitch */
2147 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2149 if (mem != This->resource.allocatedMemory)
2150 HeapFree(GetProcessHeap(), 0, mem);
2152 #if 0
2154 static unsigned int gen = 0;
2155 char buffer[4096];
2156 ++gen;
2157 if ((gen % 10) == 0) {
2158 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2159 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2162 * debugging crash code
2163 if (gen == 250) {
2164 void** test = NULL;
2165 *test = 0;
2169 #endif
2171 if (!(This->Flags & SFLAG_DONOTFREE)) {
2172 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
2173 This->resource.allocatedMemory = NULL;
2174 This->Flags &= ~SFLAG_INSYSMEM;
2177 return WINED3D_OK;
2180 #include <errno.h>
2181 #include <stdio.h>
2182 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2183 FILE* f = NULL;
2184 UINT i, y;
2185 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2186 char *allocatedMemory;
2187 char *textureRow;
2188 IWineD3DSwapChain *swapChain = NULL;
2189 int width, height;
2190 GLuint tmpTexture = 0;
2191 DWORD color;
2192 /*FIXME:
2193 Textures may not be stored in ->allocatedgMemory and a GlTexture
2194 so we should lock the surface before saving a snapshot, or at least check that
2196 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2197 by calling GetTexImage and in compressed form by calling
2198 GetCompressedTexImageARB. Queried compressed images can be saved and
2199 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2200 texture images do not need to be processed by the GL and should
2201 significantly improve texture loading performance relative to uncompressed
2202 images. */
2204 /* Setup the width and height to be the internal texture width and height. */
2205 width = This->pow2Width;
2206 height = This->pow2Height;
2207 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2208 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2210 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2211 /* if were not a real texture then read the back buffer into a real texture */
2212 /* we don't want to interfere with the back buffer so read the data into a temporary
2213 * texture and then save the data out of the temporary texture
2215 GLint prevRead;
2216 ENTER_GL();
2217 TRACE("(%p) Reading render target into texture\n", This);
2218 glEnable(GL_TEXTURE_2D);
2220 glGenTextures(1, &tmpTexture);
2221 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2223 glTexImage2D(GL_TEXTURE_2D,
2225 GL_RGBA,
2226 width,
2227 height,
2228 0/*border*/,
2229 GL_RGBA,
2230 GL_UNSIGNED_INT_8_8_8_8_REV,
2231 NULL);
2233 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2234 vcheckGLcall("glGetIntegerv");
2235 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2236 vcheckGLcall("glReadBuffer");
2237 glCopyTexImage2D(GL_TEXTURE_2D,
2239 GL_RGBA,
2242 width,
2243 height,
2246 checkGLcall("glCopyTexImage2D");
2247 glReadBuffer(prevRead);
2248 LEAVE_GL();
2250 } else { /* bind the real texture, and make sure it up to date */
2251 IWineD3DSurface_PreLoad(iface);
2253 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2254 ENTER_GL();
2255 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2256 glGetTexImage(GL_TEXTURE_2D,
2257 This->glDescription.level,
2258 GL_RGBA,
2259 GL_UNSIGNED_INT_8_8_8_8_REV,
2260 allocatedMemory);
2261 checkGLcall("glTexImage2D");
2262 if (tmpTexture) {
2263 glBindTexture(GL_TEXTURE_2D, 0);
2264 glDeleteTextures(1, &tmpTexture);
2266 LEAVE_GL();
2268 f = fopen(filename, "w+");
2269 if (NULL == f) {
2270 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2271 return WINED3DERR_INVALIDCALL;
2273 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2274 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2275 /* TGA header */
2276 fputc(0,f);
2277 fputc(0,f);
2278 fputc(2,f);
2279 fputc(0,f);
2280 fputc(0,f);
2281 fputc(0,f);
2282 fputc(0,f);
2283 fputc(0,f);
2284 fputc(0,f);
2285 fputc(0,f);
2286 fputc(0,f);
2287 fputc(0,f);
2288 /* short width*/
2289 fwrite(&width,2,1,f);
2290 /* short height */
2291 fwrite(&height,2,1,f);
2292 /* format rgba */
2293 fputc(0x20,f);
2294 fputc(0x28,f);
2295 /* raw data */
2296 /* 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 */
2297 if(swapChain)
2298 textureRow = allocatedMemory + (width * (height - 1) *4);
2299 else
2300 textureRow = allocatedMemory;
2301 for (y = 0 ; y < height; y++) {
2302 for (i = 0; i < width; i++) {
2303 color = *((DWORD*)textureRow);
2304 fputc((color >> 16) & 0xFF, f); /* B */
2305 fputc((color >> 8) & 0xFF, f); /* G */
2306 fputc((color >> 0) & 0xFF, f); /* R */
2307 fputc((color >> 24) & 0xFF, f); /* A */
2308 textureRow += 4;
2310 /* take two rows of the pointer to the texture memory */
2311 if(swapChain)
2312 (textureRow-= width << 3);
2315 TRACE("Closing file\n");
2316 fclose(f);
2318 if(swapChain) {
2319 IWineD3DSwapChain_Release(swapChain);
2321 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2322 return WINED3D_OK;
2326 * Slightly inefficient way to handle multiple dirty rects but it works :)
2328 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2329 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2330 IWineD3DBaseTexture *baseTexture = NULL;
2331 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2332 surface_download_data(This);
2334 This->Flags &= ~(SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
2335 if (NULL != pDirtyRect) {
2336 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2337 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2338 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2339 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2340 } else {
2341 This->dirtyRect.left = 0;
2342 This->dirtyRect.top = 0;
2343 This->dirtyRect.right = This->currentDesc.Width;
2344 This->dirtyRect.bottom = This->currentDesc.Height;
2346 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2347 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2348 /* if the container is a basetexture then mark it dirty. */
2349 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2350 TRACE("Passing to container\n");
2351 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2352 IWineD3DBaseTexture_Release(baseTexture);
2354 return WINED3D_OK;
2357 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
2358 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2360 TRACE("This %p, container %p\n", This, container);
2362 /* We can't keep a reference to the container, since the container already keeps a reference to us. */
2364 TRACE("Setting container to %p from %p\n", container, This->container);
2365 This->container = container;
2367 return WINED3D_OK;
2370 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2371 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2372 const GlPixelFormatDesc *glDesc;
2373 const StaticPixelFormatDesc *formatEntry = getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2375 if (This->resource.format != WINED3DFMT_UNKNOWN) {
2376 FIXME("(%p) : The format of the surface must be WINED3DFORMAT_UNKNOWN\n", This);
2377 return WINED3DERR_INVALIDCALL;
2380 TRACE("(%p) : Setting texture format to (%d,%s)\n", This, format, debug_d3dformat(format));
2381 if (format == WINED3DFMT_UNKNOWN) {
2382 This->resource.size = 0;
2383 } else if (format == WINED3DFMT_DXT1) {
2384 /* DXT1 is half byte per pixel */
2385 This->resource.size = ((max(This->pow2Width, 4) * formatEntry->bpp) * max(This->pow2Height, 4)) >> 1;
2387 } else if (format == WINED3DFMT_DXT2 || format == WINED3DFMT_DXT3 ||
2388 format == WINED3DFMT_DXT4 || format == WINED3DFMT_DXT5) {
2389 This->resource.size = ((max(This->pow2Width, 4) * formatEntry->bpp) * max(This->pow2Height, 4));
2390 } else {
2391 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
2392 This->resource.size = ((This->pow2Width * formatEntry->bpp) + alignment - 1) & ~(alignment - 1);
2393 This->resource.size *= This->pow2Height;
2397 /* Setup some glformat defaults */
2398 This->glDescription.glFormat = glDesc->glFormat;
2399 This->glDescription.glFormatInternal = glDesc->glInternal;
2400 This->glDescription.glType = glDesc->glType;
2402 if (format != WINED3DFMT_UNKNOWN) {
2403 This->bytesPerPixel = formatEntry->bpp;
2404 } else {
2405 This->bytesPerPixel = 0;
2408 This->Flags |= (WINED3DFMT_D16_LOCKABLE == format) ? SFLAG_LOCKABLE : 0;
2409 This->Flags &= ~SFLAG_ALLOCATED;
2411 This->resource.format = format;
2413 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);
2415 return WINED3D_OK;
2418 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2419 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2421 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer */
2422 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
2423 ERR("Not supported on render targets\n");
2424 return WINED3DERR_INVALIDCALL;
2427 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2428 WARN("Surface is locked or the HDC is in use\n");
2429 return WINED3DERR_INVALIDCALL;
2432 if(Mem && Mem != This->resource.allocatedMemory) {
2433 void *release = NULL;
2435 /* Do I have to copy the old surface content? */
2436 if(This->Flags & SFLAG_DIBSECTION) {
2437 /* Release the DC. No need to hold the critical section for the update
2438 * Thread because this thread runs only on front buffers, but this method
2439 * fails for render targets in the check above.
2441 SelectObject(This->hDC, This->dib.holdbitmap);
2442 DeleteDC(This->hDC);
2443 /* Release the DIB section */
2444 DeleteObject(This->dib.DIBsection);
2445 This->dib.bitmap_data = NULL;
2446 This->resource.allocatedMemory = NULL;
2447 This->hDC = NULL;
2448 This->Flags &= ~SFLAG_DIBSECTION;
2449 } else if(!(This->Flags & SFLAG_USERPTR)) {
2450 release = This->resource.allocatedMemory;
2452 This->resource.allocatedMemory = Mem;
2453 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2455 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2456 This->Flags &= ~(SFLAG_INDRAWABLE | SFLAG_INTEXTURE);
2458 /* For client textures opengl has to be notified */
2459 if(This->Flags & SFLAG_CLIENT) {
2460 This->Flags &= ~SFLAG_ALLOCATED;
2461 IWineD3DSurface_PreLoad(iface);
2462 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2465 /* Now free the old memory if any */
2466 HeapFree(GetProcessHeap(), 0, release);
2467 } else if(This->Flags & SFLAG_USERPTR) {
2468 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2469 This->resource.allocatedMemory = NULL;
2470 This->Flags &= ~SFLAG_USERPTR;
2472 if(This->Flags & SFLAG_CLIENT) {
2473 This->Flags &= ~SFLAG_ALLOCATED;
2474 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2475 IWineD3DSurface_PreLoad(iface);
2478 return WINED3D_OK;
2481 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2482 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2483 IWineD3DSwapChainImpl *swapchain = NULL;
2484 HRESULT hr;
2485 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2487 /* Flipping is only supported on RenderTargets */
2488 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2490 if(override) {
2491 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2492 * FIXME("(%p) Target override is not supported by now\n", This);
2493 * Additionally, it isn't really possible to support triple-buffering
2494 * properly on opengl at all
2498 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2499 if(!swapchain) {
2500 ERR("Flipped surface is not on a swapchain\n");
2501 return WINEDDERR_NOTFLIPPABLE;
2504 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2505 * and only d3d8 and d3d9 apps specify the presentation interval
2507 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2508 /* Most common case first to avoid wasting time on all the other cases */
2509 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2510 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2511 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2512 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2513 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2514 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2515 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2516 } else {
2517 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2520 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2521 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2522 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2523 return hr;
2526 /* Does a direct frame buffer -> texture copy. Stretching is done
2527 * with single pixel copy calls
2529 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2530 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2531 float xrel, yrel;
2532 UINT row;
2533 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2536 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2537 ENTER_GL();
2538 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2540 /* Bind the target texture */
2541 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
2542 checkGLcall("glBindTexture");
2543 if(!swapchain) {
2544 glReadBuffer(myDevice->offscreenBuffer);
2545 } else {
2546 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2547 glReadBuffer(buffer);
2549 checkGLcall("glReadBuffer");
2551 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2552 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2554 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2555 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2557 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2558 ERR("Texture filtering not supported in direct blit\n");
2560 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2561 ERR("Texture filtering not supported in direct blit\n");
2564 if(upsidedown &&
2565 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2566 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2567 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2569 glCopyTexSubImage2D(This->glDescription.target,
2570 This->glDescription.level,
2571 drect->x1, drect->y1, /* xoffset, yoffset */
2572 srect->x1, Src->currentDesc.Height - srect->y2,
2573 drect->x2 - drect->x1, drect->y2 - drect->y1);
2574 } else {
2575 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2576 /* I have to process this row by row to swap the image,
2577 * otherwise it would be upside down, so stretching in y direction
2578 * doesn't cost extra time
2580 * However, stretching in x direction can be avoided if not necessary
2582 for(row = drect->y1; row < drect->y2; row++) {
2583 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2584 /* Well, that stuff works, but it's very slow.
2585 * find a better way instead
2587 UINT col;
2589 for(col = drect->x1; col < drect->x2; col++) {
2590 glCopyTexSubImage2D(This->glDescription.target,
2591 This->glDescription.level,
2592 drect->x1 + col, row, /* xoffset, yoffset */
2593 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2594 1, 1);
2596 } else {
2597 glCopyTexSubImage2D(This->glDescription.target,
2598 This->glDescription.level,
2599 drect->x1, row, /* xoffset, yoffset */
2600 srect->x1, yoffset - (int) (row * yrel),
2601 drect->x2-drect->x1, 1);
2606 vcheckGLcall("glCopyTexSubImage2D");
2607 LEAVE_GL();
2610 /* Uses the hardware to stretch and flip the image */
2611 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2612 GLuint src, backup = 0;
2613 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2614 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2615 float left, right, top, bottom; /* Texture coordinates */
2616 UINT fbwidth = Src->currentDesc.Width;
2617 UINT fbheight = Src->currentDesc.Height;
2618 GLenum drawBuffer = GL_BACK;
2620 TRACE("Using hwstretch blit\n");
2621 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2622 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2623 ENTER_GL();
2624 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2626 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2627 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2629 if(GL_LIMITS(aux_buffers) >= 2) {
2630 /* Got more than one aux buffer? Use the 2nd aux buffer */
2631 drawBuffer = GL_AUX1;
2632 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2633 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2634 drawBuffer = GL_AUX0;
2637 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2638 glGenTextures(1, &backup);
2639 checkGLcall("glGenTextures\n");
2640 glBindTexture(GL_TEXTURE_2D, backup);
2641 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2642 } else {
2643 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2644 * we are reading from the back buffer, the backup can be used as source texture
2646 if(Src->glDescription.textureName == 0) {
2647 /* Get it a description */
2648 IWineD3DSurface_PreLoad(SrcSurface);
2650 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
2651 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2653 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2654 Src->Flags &= ~SFLAG_INTEXTURE;
2657 glReadBuffer(GL_BACK);
2658 checkGLcall("glReadBuffer(GL_BACK)");
2660 /* TODO: Only back up the part that will be overwritten */
2661 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2662 0, 0 /* read offsets */,
2663 0, 0,
2664 fbwidth,
2665 fbheight);
2667 checkGLcall("glCopyTexSubImage2D");
2669 /* No issue with overriding these - the sampler is dirty due to blit usage */
2670 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
2671 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2672 checkGLcall("glTexParameteri");
2673 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
2674 minMipLookup[Filter][WINED3DTEXF_NONE]);
2675 checkGLcall("glTexParameteri");
2677 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2678 src = backup ? backup : Src->glDescription.textureName;
2679 } else {
2680 glReadBuffer(GL_FRONT);
2681 checkGLcall("glReadBuffer(GL_FRONT)");
2683 glGenTextures(1, &src);
2684 checkGLcall("glGenTextures(1, &src)");
2685 glBindTexture(GL_TEXTURE_2D, src);
2686 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2688 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2689 * out for power of 2 sizes
2691 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2692 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2693 checkGLcall("glTexImage2D");
2694 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2695 0, 0 /* read offsets */,
2696 0, 0,
2697 fbwidth,
2698 fbheight);
2700 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2701 checkGLcall("glTexParameteri");
2702 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2703 checkGLcall("glTexParameteri");
2705 glReadBuffer(GL_BACK);
2706 checkGLcall("glReadBuffer(GL_BACK)");
2708 checkGLcall("glEnd and previous");
2710 left = (float) srect->x1 / (float) Src->pow2Width;
2711 right = (float) srect->x2 / (float) Src->pow2Width;
2713 if(upsidedown) {
2714 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2715 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2716 } else {
2717 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2718 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2721 /* draw the source texture stretched and upside down. The correct surface is bound already */
2722 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
2723 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
2725 glDrawBuffer(drawBuffer);
2726 glReadBuffer(drawBuffer);
2728 glBegin(GL_QUADS);
2729 /* bottom left */
2730 glTexCoord2f(left, bottom);
2731 glVertex2i(0, fbheight);
2733 /* top left */
2734 glTexCoord2f(left, top);
2735 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2737 /* top right */
2738 glTexCoord2f(right, top);
2739 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2741 /* bottom right */
2742 glTexCoord2f(right, bottom);
2743 glVertex2i(drect->x2 - drect->x1, fbheight);
2744 glEnd();
2745 checkGLcall("glEnd and previous");
2747 /* Now read the stretched and upside down image into the destination texture */
2748 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2749 checkGLcall("glBindTexture");
2750 glCopyTexSubImage2D(This->glDescription.target,
2752 drect->x1, drect->y1, /* xoffset, yoffset */
2753 0, 0, /* We blitted the image to the origin */
2754 drect->x2 - drect->x1, drect->y2 - drect->y1);
2755 checkGLcall("glCopyTexSubImage2D");
2757 /* Write the back buffer backup back */
2758 glBindTexture(GL_TEXTURE_2D, backup ? backup : Src->glDescription.textureName);
2759 checkGLcall("glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName)");
2761 if(drawBuffer == GL_BACK) {
2762 glBegin(GL_QUADS);
2763 /* top left */
2764 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2765 glVertex2i(0, 0);
2767 /* bottom left */
2768 glTexCoord2f(0.0, 0.0);
2769 glVertex2i(0, fbheight);
2771 /* bottom right */
2772 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2773 glVertex2i(fbwidth, Src->currentDesc.Height);
2775 /* top right */
2776 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2777 glVertex2i(fbwidth, 0);
2778 glEnd();
2779 } else {
2780 /* Restore the old draw buffer */
2781 glDrawBuffer(GL_BACK);
2784 /* Cleanup */
2785 if(src != Src->glDescription.textureName && src != backup) {
2786 glDeleteTextures(1, &src);
2787 checkGLcall("glDeleteTextures(1, &src)");
2789 if(backup) {
2790 glDeleteTextures(1, &backup);
2791 checkGLcall("glDeleteTextures(1, &backup)");
2793 LEAVE_GL();
2796 /* Not called from the VTable */
2797 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2798 WINED3DRECT rect;
2799 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2800 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2801 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2803 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2805 /* Get the swapchain. One of the surfaces has to be a primary surface */
2806 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2807 WARN("Destination is in sysmem, rejecting gl blt\n");
2808 return WINED3DERR_INVALIDCALL;
2810 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2811 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2812 if(Src) {
2813 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2814 WARN("Src is in sysmem, rejecting gl blt\n");
2815 return WINED3DERR_INVALIDCALL;
2817 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2818 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2821 /* Early sort out of cases where no render target is used */
2822 if(!dstSwapchain && !srcSwapchain &&
2823 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2824 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2825 return WINED3DERR_INVALIDCALL;
2828 /* No destination color keying supported */
2829 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
2830 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2831 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2832 return WINED3DERR_INVALIDCALL;
2835 if (DestRect) {
2836 rect.x1 = DestRect->left;
2837 rect.y1 = DestRect->top;
2838 rect.x2 = DestRect->right;
2839 rect.y2 = DestRect->bottom;
2840 } else {
2841 rect.x1 = 0;
2842 rect.y1 = 0;
2843 rect.x2 = This->currentDesc.Width;
2844 rect.y2 = This->currentDesc.Height;
2847 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2848 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
2849 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
2850 /* Half-life does a Blt from the back buffer to the front buffer,
2851 * Full surface size, no flags... Use present instead
2853 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
2856 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2857 while(1)
2859 RECT mySrcRect;
2860 TRACE("Looking if a Present can be done...\n");
2861 /* Source Rectangle must be full surface */
2862 if( SrcRect ) {
2863 if(SrcRect->left != 0 || SrcRect->top != 0 ||
2864 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
2865 TRACE("No, Source rectangle doesn't match\n");
2866 break;
2869 mySrcRect.left = 0;
2870 mySrcRect.top = 0;
2871 mySrcRect.right = Src->currentDesc.Width;
2872 mySrcRect.bottom = Src->currentDesc.Height;
2874 /* No stretching may occur */
2875 if(mySrcRect.right != rect.x2 - rect.x1 ||
2876 mySrcRect.bottom != rect.y2 - rect.y1) {
2877 TRACE("No, stretching is done\n");
2878 break;
2881 /* Destination must be full surface or match the clipping rectangle */
2882 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
2884 RECT cliprect;
2885 POINT pos[2];
2886 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
2887 pos[0].x = rect.x1;
2888 pos[0].y = rect.y1;
2889 pos[1].x = rect.x2;
2890 pos[1].y = rect.y2;
2891 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
2892 pos, 2);
2894 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
2895 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
2897 TRACE("No, dest rectangle doesn't match(clipper)\n");
2898 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
2899 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
2900 break;
2903 else
2905 if(rect.x1 != 0 || rect.y1 != 0 ||
2906 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
2907 TRACE("No, dest rectangle doesn't match(surface size)\n");
2908 break;
2912 TRACE("Yes\n");
2914 /* These flags are unimportant for the flag check, remove them */
2915 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
2916 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
2918 /* The idea behind this is that a glReadPixels and a glDrawPixels call
2919 * take very long, while a flip is fast.
2920 * This applies to Half-Life, which does such Blts every time it finished
2921 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
2922 * menu. This is also used by all apps when they do windowed rendering
2924 * The problem is that flipping is not really the same as copying. After a
2925 * Blt the front buffer is a copy of the back buffer, and the back buffer is
2926 * untouched. Therefore it's necessary to override the swap effect
2927 * and to set it back after the flip.
2929 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
2930 * testcases.
2933 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
2934 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2936 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
2937 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
2939 dstSwapchain->presentParms.SwapEffect = orig_swap;
2941 return WINED3D_OK;
2943 break;
2946 TRACE("Unsupported blit between buffers on the same swapchain\n");
2947 return WINED3DERR_INVALIDCALL;
2948 } else if((dstSwapchain || This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) &&
2949 (srcSwapchain || SrcSurface == myDevice->render_targets[0]) ) {
2950 ERR("Can't perform hardware blit between 2 different swapchains, falling back to software\n");
2951 return WINED3DERR_INVALIDCALL;
2954 if(srcSwapchain || SrcSurface == myDevice->render_targets[0]) {
2955 /* Blit from render target to texture */
2956 WINED3DRECT srect;
2957 BOOL upsideDown, stretchx;
2959 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
2960 TRACE("Color keying not supported by frame buffer to texture blit\n");
2961 return WINED3DERR_INVALIDCALL;
2962 /* Destination color key is checked above */
2965 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2966 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2968 if(SrcRect) {
2969 if(SrcRect->top < SrcRect->bottom) {
2970 srect.y1 = SrcRect->top;
2971 srect.y2 = SrcRect->bottom;
2972 upsideDown = FALSE;
2973 } else {
2974 srect.y1 = SrcRect->bottom;
2975 srect.y2 = SrcRect->top;
2976 upsideDown = TRUE;
2978 srect.x1 = SrcRect->left;
2979 srect.x2 = SrcRect->right;
2980 } else {
2981 srect.x1 = 0;
2982 srect.y1 = 0;
2983 srect.x2 = Src->currentDesc.Width;
2984 srect.y2 = Src->currentDesc.Height;
2985 upsideDown = FALSE;
2987 if(rect.x1 > rect.x2) {
2988 UINT tmp = rect.x2;
2989 rect.x2 = rect.x1;
2990 rect.x1 = tmp;
2991 upsideDown = !upsideDown;
2993 if(!srcSwapchain) {
2994 TRACE("Reading from an offscreen target\n");
2995 upsideDown = !upsideDown;
2998 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
2999 stretchx = TRUE;
3000 } else {
3001 stretchx = FALSE;
3004 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3005 * flip the image nor scale it.
3007 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3008 * -> If the app wants a image width an unscaled width, copy it line per line
3009 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3010 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3011 * back buffer. This is slower than reading line per line, thus not used for flipping
3012 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3013 * pixel by pixel
3015 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3016 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3017 * backends.
3019 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3020 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3021 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3022 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3023 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3024 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3025 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3026 } else {
3027 TRACE("Using hardware stretching to flip / stretch the texture\n");
3028 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3031 if(!(This->Flags & SFLAG_DONOTFREE)) {
3032 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
3033 This->resource.allocatedMemory = NULL;
3034 } else {
3035 This->Flags &= ~SFLAG_INSYSMEM;
3037 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3038 * path is never entered
3040 This->Flags |= SFLAG_INTEXTURE;
3042 return WINED3D_OK;
3043 } else if(Src) {
3044 /* Blit from offscreen surface to render target */
3045 float glTexCoord[4];
3046 DWORD oldCKeyFlags = Src->CKeyFlags;
3047 WINEDDCOLORKEY oldBltCKey = This->SrcBltCKey;
3048 RECT SourceRectangle;
3050 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3052 if(SrcRect) {
3053 SourceRectangle.left = SrcRect->left;
3054 SourceRectangle.right = SrcRect->right;
3055 SourceRectangle.top = SrcRect->top;
3056 SourceRectangle.bottom = SrcRect->bottom;
3057 } else {
3058 SourceRectangle.left = 0;
3059 SourceRectangle.right = Src->currentDesc.Width;
3060 SourceRectangle.top = 0;
3061 SourceRectangle.bottom = Src->currentDesc.Height;
3064 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3065 /* Fall back to software */
3066 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3067 SourceRectangle.left, SourceRectangle.top,
3068 SourceRectangle.right, SourceRectangle.bottom);
3069 return WINED3DERR_INVALIDCALL;
3072 /* Color keying: Check if we have to do a color keyed blt,
3073 * and if not check if a color key is activated.
3075 * Just modify the color keying parameters in the surface and restore them afterwards
3076 * The surface keeps track of the color key last used to load the opengl surface.
3077 * PreLoad will catch the change to the flags and color key and reload if necessary.
3079 if(Flags & WINEDDBLT_KEYSRC) {
3080 /* Use color key from surface */
3081 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3082 /* Use color key from DDBltFx */
3083 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3084 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3085 } else {
3086 /* Do not use color key */
3087 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3090 /* Now load the surface */
3091 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3094 /* Activate the destination context, set it up for blitting */
3095 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3096 ENTER_GL();
3098 if(!dstSwapchain) {
3099 TRACE("Drawing to offscreen buffer\n");
3100 glDrawBuffer(myDevice->offscreenBuffer);
3101 checkGLcall("glDrawBuffer");
3102 } else {
3103 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
3104 TRACE("Drawing to %#x buffer\n", buffer);
3105 glDrawBuffer(buffer);
3106 checkGLcall("glDrawBuffer");
3109 /* Bind the texture */
3110 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
3111 checkGLcall("glBindTexture");
3113 /* Filtering for StretchRect */
3114 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
3115 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
3116 checkGLcall("glTexParameteri");
3117 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
3118 minMipLookup[Filter][WINED3DTEXF_NONE]);
3119 checkGLcall("glTexParameteri");
3120 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
3121 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
3122 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3123 checkGLcall("glTexEnvi");
3125 /* This is for color keying */
3126 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3127 glEnable(GL_ALPHA_TEST);
3128 checkGLcall("glEnable GL_ALPHA_TEST");
3129 glAlphaFunc(GL_NOTEQUAL, 0.0);
3130 checkGLcall("glAlphaFunc\n");
3131 } else {
3132 glDisable(GL_ALPHA_TEST);
3133 checkGLcall("glDisable GL_ALPHA_TEST");
3136 /* Draw a textured quad
3138 glBegin(GL_QUADS);
3140 glColor3d(1.0f, 1.0f, 1.0f);
3141 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3142 glVertex3f(rect.x1,
3143 rect.y1,
3144 0.0);
3146 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3147 glVertex3f(rect.x1, rect.y2, 0.0);
3149 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3150 glVertex3f(rect.x2,
3151 rect.y2,
3152 0.0);
3154 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3155 glVertex3f(rect.x2,
3156 rect.y1,
3157 0.0);
3158 glEnd();
3159 checkGLcall("glEnd");
3161 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3162 glDisable(GL_ALPHA_TEST);
3163 checkGLcall("glDisable(GL_ALPHA_TEST)");
3166 /* Unbind the texture */
3167 glBindTexture(GL_TEXTURE_2D, 0);
3168 checkGLcall("glEnable glBindTexture");
3170 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3171 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3173 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3174 glDrawBuffer(GL_BACK);
3175 checkGLcall("glDrawBuffer");
3177 /* Restore the color key parameters */
3178 Src->CKeyFlags = oldCKeyFlags;
3179 This->SrcBltCKey = oldBltCKey;
3181 LEAVE_GL();
3183 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3184 This->Flags &= ~SFLAG_INSYSMEM;
3185 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3186 * is outdated now
3188 if(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO) {
3189 This->Flags |= SFLAG_INDRAWABLE;
3190 This->Flags &= ~SFLAG_INTEXTURE;
3191 } else {
3192 This->Flags |= SFLAG_INTEXTURE;
3195 return WINED3D_OK;
3196 } else {
3197 /* Source-Less Blit to render target */
3198 if (Flags & WINEDDBLT_COLORFILL) {
3199 /* This is easy to handle for the D3D Device... */
3200 DWORD color;
3202 TRACE("Colorfill\n");
3204 /* The color as given in the Blt function is in the format of the frame-buffer...
3205 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3207 if (This->resource.format == WINED3DFMT_P8) {
3208 if (This->palette) {
3209 color = ((0xFF000000) |
3210 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3211 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3212 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3213 } else {
3214 color = 0xFF000000;
3217 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3218 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3219 color = 0xFFFFFFFF;
3220 } else {
3221 color = ((0xFF000000) |
3222 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3223 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3224 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3227 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3228 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3229 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3231 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3232 color = DDBltFx->u5.dwFillColor;
3234 else {
3235 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3236 return WINED3DERR_INVALIDCALL;
3239 TRACE("Calling GetSwapChain with mydevice = %p\n", myDevice);
3240 if(dstSwapchain && dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]) {
3241 glDrawBuffer(GL_BACK);
3242 checkGLcall("glDrawBuffer(GL_BACK)");
3243 } else if (dstSwapchain && This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer) {
3244 glDrawBuffer(GL_FRONT);
3245 checkGLcall("glDrawBuffer(GL_FRONT)");
3246 } else if(This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3247 glDrawBuffer(myDevice->offscreenBuffer);
3248 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer3)");
3249 } else {
3250 TRACE("Surface is higher back buffer, falling back to software\n");
3251 return WINED3DERR_INVALIDCALL;
3254 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3256 IWineD3DDevice_Clear( (IWineD3DDevice *) myDevice,
3257 1 /* Number of rectangles */,
3258 &rect,
3259 WINED3DCLEAR_TARGET,
3260 color,
3261 0.0 /* Z */,
3262 0 /* Stencil */);
3264 /* Restore the original draw buffer */
3265 if(!dstSwapchain) {
3266 glDrawBuffer(myDevice->offscreenBuffer);
3267 } else if(dstSwapchain->backBuffer && dstSwapchain->backBuffer[0]) {
3268 glDrawBuffer(GL_BACK);
3270 vcheckGLcall("glDrawBuffer");
3272 return WINED3D_OK;
3276 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3277 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3278 return WINED3DERR_INVALIDCALL;
3281 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3283 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3285 if (Flags & WINEDDBLT_DEPTHFILL)
3286 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3287 DestRect == NULL ? 0 : 1,
3288 (WINED3DRECT *) DestRect,
3289 WINED3DCLEAR_ZBUFFER,
3290 0x00000000,
3291 (float) DDBltFx->u5.dwFillDepth / (float) MAXDWORD,
3292 0x00000000);
3294 FIXME("(%p): Unsupp depthstencil blit\n", This);
3295 return WINED3DERR_INVALIDCALL;
3298 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3299 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3300 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3301 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3302 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3303 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3305 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3306 * except depth blits, which seem to work
3308 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3309 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3310 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3311 return WINED3DERR_INVALIDCALL;
3312 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3313 TRACE("Z Blit override handled the blit\n");
3314 return WINED3D_OK;
3318 /* Special cases for RenderTargets */
3319 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3320 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3321 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3324 /* For the rest call the X11 surface implementation.
3325 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3326 * other Blts are rather rare
3328 return IWineGDISurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3331 HRESULT WINAPI IWineD3DSurfaceImpl_GetBltStatus(IWineD3DSurface *iface, DWORD Flags) {
3332 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3333 TRACE("(%p)->(%x)\n", This, Flags);
3335 switch (Flags)
3337 case WINEDDGBS_CANBLT:
3338 case WINEDDGBS_ISBLTDONE:
3339 return WINED3D_OK;
3341 default:
3342 return WINED3DERR_INVALIDCALL;
3346 HRESULT WINAPI IWineD3DSurfaceImpl_GetFlipStatus(IWineD3DSurface *iface, DWORD Flags) {
3347 /* XXX: DDERR_INVALIDSURFACETYPE */
3349 TRACE("(%p)->(%08x)\n",iface,Flags);
3350 switch (Flags) {
3351 case WINEDDGFS_CANFLIP:
3352 case WINEDDGFS_ISFLIPDONE:
3353 return WINED3D_OK;
3355 default:
3356 return WINED3DERR_INVALIDCALL;
3360 HRESULT WINAPI IWineD3DSurfaceImpl_IsLost(IWineD3DSurface *iface) {
3361 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3362 TRACE("(%p)\n", This);
3364 /* D3D8 and 9 loose full devices, ddraw only surfaces */
3365 return This->Flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
3368 HRESULT WINAPI IWineD3DSurfaceImpl_Restore(IWineD3DSurface *iface) {
3369 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3370 TRACE("(%p)\n", This);
3372 /* So far we don't lose anything :) */
3373 This->Flags &= ~SFLAG_LOST;
3374 return WINED3D_OK;
3377 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3378 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3379 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3380 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3381 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3383 if(myDevice->inScene &&
3384 (iface == myDevice->stencilBufferTarget ||
3385 (Source && Source == myDevice->stencilBufferTarget))) {
3386 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3387 return WINED3DERR_INVALIDCALL;
3390 /* Special cases for RenderTargets */
3391 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3392 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3394 RECT SrcRect, DstRect;
3395 DWORD Flags=0;
3397 if(rsrc) {
3398 SrcRect.left = rsrc->left;
3399 SrcRect.top= rsrc->top;
3400 SrcRect.bottom = rsrc->bottom;
3401 SrcRect.right = rsrc->right;
3402 } else {
3403 SrcRect.left = 0;
3404 SrcRect.top = 0;
3405 SrcRect.right = srcImpl->currentDesc.Width;
3406 SrcRect.bottom = srcImpl->currentDesc.Height;
3409 DstRect.left = dstx;
3410 DstRect.top=dsty;
3411 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3412 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3414 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3415 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3416 Flags |= WINEDDBLT_KEYSRC;
3417 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3418 Flags |= WINEDDBLT_KEYDEST;
3419 if(trans & WINEDDBLTFAST_WAIT)
3420 Flags |= WINEDDBLT_WAIT;
3421 if(trans & WINEDDBLTFAST_DONOTWAIT)
3422 Flags |= WINEDDBLT_DONOTWAIT;
3424 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3428 return IWineGDISurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3431 HRESULT WINAPI IWineD3DSurfaceImpl_GetPalette(IWineD3DSurface *iface, IWineD3DPalette **Pal) {
3432 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3433 TRACE("(%p)->(%p)\n", This, Pal);
3435 *Pal = (IWineD3DPalette *) This->palette;
3436 return WINED3D_OK;
3439 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
3440 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3441 RGBQUAD col[256];
3442 IWineD3DPaletteImpl *pal = This->palette;
3443 unsigned int n;
3444 TRACE("(%p)\n", This);
3446 if(This->resource.format == WINED3DFMT_P8 ||
3447 This->resource.format == WINED3DFMT_A8P8)
3449 if(!This->Flags & SFLAG_INSYSMEM) {
3450 FIXME("Palette changed with surface that does not have an up to date system memory copy\n");
3452 TRACE("Dirtifying surface\n");
3453 This->Flags &= ~(SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3456 if(This->Flags & SFLAG_DIBSECTION) {
3457 TRACE("(%p): Updating the hdc's palette\n", This);
3458 for (n=0; n<256; n++) {
3459 if(pal) {
3460 col[n].rgbRed = pal->palents[n].peRed;
3461 col[n].rgbGreen = pal->palents[n].peGreen;
3462 col[n].rgbBlue = pal->palents[n].peBlue;
3463 } else {
3464 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3465 /* Use the default device palette */
3466 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
3467 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
3468 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
3470 col[n].rgbReserved = 0;
3472 SetDIBColorTable(This->hDC, 0, 256, col);
3475 return WINED3D_OK;
3478 HRESULT WINAPI IWineD3DSurfaceImpl_SetPalette(IWineD3DSurface *iface, IWineD3DPalette *Pal) {
3479 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3480 IWineD3DPaletteImpl *PalImpl = (IWineD3DPaletteImpl *) Pal;
3481 TRACE("(%p)->(%p)\n", This, Pal);
3483 if(This->palette != NULL)
3484 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3485 This->palette->Flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
3487 if(PalImpl != NULL) {
3488 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3489 /* Set the device's main palette if the palette
3490 * wasn't a primary palette before
3492 if(!(PalImpl->Flags & WINEDDPCAPS_PRIMARYSURFACE)) {
3493 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3494 unsigned int i;
3496 for(i=0; i < 256; i++) {
3497 device->palettes[device->currentPalette][i] = PalImpl->palents[i];
3501 (PalImpl)->Flags |= WINEDDPCAPS_PRIMARYSURFACE;
3504 This->palette = PalImpl;
3506 return IWineD3DSurface_RealizePalette(iface);
3509 HRESULT WINAPI IWineD3DSurfaceImpl_SetColorKey(IWineD3DSurface *iface, DWORD Flags, WINEDDCOLORKEY *CKey) {
3510 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3511 TRACE("(%p)->(%08x,%p)\n", This, Flags, CKey);
3513 if ((Flags & WINEDDCKEY_COLORSPACE) != 0) {
3514 FIXME(" colorkey value not supported (%08x) !\n", Flags);
3515 return WINED3DERR_INVALIDCALL;
3518 /* Dirtify the surface, but only if a key was changed */
3519 if(CKey) {
3520 switch (Flags & ~WINEDDCKEY_COLORSPACE) {
3521 case WINEDDCKEY_DESTBLT:
3522 This->DestBltCKey = *CKey;
3523 This->CKeyFlags |= WINEDDSD_CKDESTBLT;
3524 break;
3526 case WINEDDCKEY_DESTOVERLAY:
3527 This->DestOverlayCKey = *CKey;
3528 This->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3529 break;
3531 case WINEDDCKEY_SRCOVERLAY:
3532 This->SrcOverlayCKey = *CKey;
3533 This->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3534 break;
3536 case WINEDDCKEY_SRCBLT:
3537 This->SrcBltCKey = *CKey;
3538 This->CKeyFlags |= WINEDDSD_CKSRCBLT;
3539 break;
3542 else {
3543 switch (Flags & ~WINEDDCKEY_COLORSPACE) {
3544 case WINEDDCKEY_DESTBLT:
3545 This->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3546 break;
3548 case WINEDDCKEY_DESTOVERLAY:
3549 This->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3550 break;
3552 case WINEDDCKEY_SRCOVERLAY:
3553 This->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3554 break;
3556 case WINEDDCKEY_SRCBLT:
3557 This->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3558 break;
3562 return WINED3D_OK;
3565 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3566 /** Check against the maximum texture sizes supported by the video card **/
3567 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3568 unsigned int pow2Width, pow2Height;
3569 const GlPixelFormatDesc *glDesc;
3571 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3572 /* Setup some glformat defaults */
3573 This->glDescription.glFormat = glDesc->glFormat;
3574 This->glDescription.glFormatInternal = glDesc->glInternal;
3575 This->glDescription.glType = glDesc->glType;
3577 This->glDescription.textureName = 0;
3578 This->glDescription.target = GL_TEXTURE_2D;
3580 /* Non-power2 support */
3581 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO)) {
3582 pow2Width = This->currentDesc.Width;
3583 pow2Height = This->currentDesc.Height;
3584 } else {
3585 /* Find the nearest pow2 match */
3586 pow2Width = pow2Height = 1;
3587 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3588 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3590 This->pow2Width = pow2Width;
3591 This->pow2Height = pow2Height;
3593 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3594 WINED3DFORMAT Format = This->resource.format;
3595 /** TODO: add support for non power two compressed textures **/
3596 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3597 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5) {
3598 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3599 This, This->currentDesc.Width, This->currentDesc.Height);
3600 return WINED3DERR_NOTAVAILABLE;
3604 if(pow2Width != This->currentDesc.Width ||
3605 pow2Height != This->currentDesc.Height) {
3606 This->Flags |= SFLAG_NONPOW2;
3609 TRACE("%p\n", This);
3610 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3611 /* one of three options
3612 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)
3613 2: Set the texture to the maximum size (bad idea)
3614 3: WARN and return WINED3DERR_NOTAVAILABLE;
3615 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.
3617 WARN("(%p) Creating an oversized surface\n", This);
3618 This->Flags |= SFLAG_OVERSIZE;
3620 /* This will be initialized on the first blt */
3621 This->glRect.left = 0;
3622 This->glRect.top = 0;
3623 This->glRect.right = 0;
3624 This->glRect.bottom = 0;
3625 } else {
3626 /* No oversize, gl rect is the full texture size */
3627 This->Flags &= ~SFLAG_OVERSIZE;
3628 This->glRect.left = 0;
3629 This->glRect.top = 0;
3630 This->glRect.right = This->pow2Width;
3631 This->glRect.bottom = This->pow2Height;
3634 if(This->resource.allocatedMemory == NULL) {
3635 /* Make sure memory exists from the start, and it is initialized properly. D3D initializes surfaces,
3636 * gl does not, so we need to upload zeroes to init the gl texture.
3638 This->resource.allocatedMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->resource.size + 4);
3640 This->Flags |= SFLAG_INSYSMEM;
3642 return WINED3D_OK;
3645 DWORD WINAPI IWineD3DSurfaceImpl_GetPitch(IWineD3DSurface *iface) {
3646 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3647 DWORD ret;
3648 TRACE("(%p)\n", This);
3650 /* DXTn formats don't have exact pitches as they are to the new row of blocks,
3651 where each block is 4x4 pixels, 8 bytes (dxt1) and 16 bytes (dxt2/3/4/5)
3652 ie pitch = (width/4) * bytes per block */
3653 if (This->resource.format == WINED3DFMT_DXT1) /* DXT1 is 8 bytes per block */
3654 ret = ((This->currentDesc.Width + 3) >> 2) << 3;
3655 else if (This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
3656 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) /* DXT2/3/4/5 is 16 bytes per block */
3657 ret = ((This->currentDesc.Width + 3) >> 2) << 4;
3658 else {
3659 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
3660 ret = This->bytesPerPixel * This->currentDesc.Width; /* Bytes / row */
3661 ret = (ret + alignment - 1) & ~(alignment - 1);
3663 TRACE("(%p) Returning %d\n", This, ret);
3664 return ret;
3667 HRESULT WINAPI IWineD3DSurfaceImpl_SetOverlayPosition(IWineD3DSurface *iface, LONG X, LONG Y) {
3668 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3670 FIXME("(%p)->(%d,%d) Stub!\n", This, X, Y);
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_GetOverlayPosition(IWineD3DSurface *iface, LONG *X, LONG *Y) {
3682 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3684 FIXME("(%p)->(%p,%p) Stub!\n", This, X, Y);
3686 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3688 TRACE("(%p): Not an overlay surface\n", This);
3689 return WINEDDERR_NOTAOVERLAYSURFACE;
3692 return WINED3D_OK;
3695 HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlayZOrder(IWineD3DSurface *iface, DWORD Flags, IWineD3DSurface *Ref) {
3696 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3697 IWineD3DSurfaceImpl *RefImpl = (IWineD3DSurfaceImpl *) Ref;
3699 FIXME("(%p)->(%08x,%p) Stub!\n", This, Flags, RefImpl);
3701 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3703 TRACE("(%p): Not an overlay surface\n", This);
3704 return WINEDDERR_NOTAOVERLAYSURFACE;
3707 return WINED3D_OK;
3710 HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlay(IWineD3DSurface *iface, RECT *SrcRect, IWineD3DSurface *DstSurface, RECT *DstRect, DWORD Flags, WINEDDOVERLAYFX *FX) {
3711 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3712 IWineD3DSurfaceImpl *Dst = (IWineD3DSurfaceImpl *) DstSurface;
3713 FIXME("(%p)->(%p, %p, %p, %08x, %p)\n", This, SrcRect, Dst, DstRect, Flags, FX);
3715 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3717 TRACE("(%p): Not an overlay surface\n", This);
3718 return WINEDDERR_NOTAOVERLAYSURFACE;
3721 return WINED3D_OK;
3724 HRESULT WINAPI IWineD3DSurfaceImpl_SetClipper(IWineD3DSurface *iface, IWineD3DClipper *clipper)
3726 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3727 TRACE("(%p)->(%p)\n", This, clipper);
3729 This->clipper = clipper;
3730 return WINED3D_OK;
3733 HRESULT WINAPI IWineD3DSurfaceImpl_GetClipper(IWineD3DSurface *iface, IWineD3DClipper **clipper)
3735 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3736 TRACE("(%p)->(%p)\n", This, clipper);
3738 *clipper = This->clipper;
3739 if(*clipper) {
3740 IWineD3DClipper_AddRef(*clipper);
3742 return WINED3D_OK;
3745 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
3747 /* IUnknown */
3748 IWineD3DSurfaceImpl_QueryInterface,
3749 IWineD3DSurfaceImpl_AddRef,
3750 IWineD3DSurfaceImpl_Release,
3751 /* IWineD3DResource */
3752 IWineD3DSurfaceImpl_GetParent,
3753 IWineD3DSurfaceImpl_GetDevice,
3754 IWineD3DSurfaceImpl_SetPrivateData,
3755 IWineD3DSurfaceImpl_GetPrivateData,
3756 IWineD3DSurfaceImpl_FreePrivateData,
3757 IWineD3DSurfaceImpl_SetPriority,
3758 IWineD3DSurfaceImpl_GetPriority,
3759 IWineD3DSurfaceImpl_PreLoad,
3760 IWineD3DSurfaceImpl_GetType,
3761 /* IWineD3DSurface */
3762 IWineD3DSurfaceImpl_GetContainer,
3763 IWineD3DSurfaceImpl_GetDesc,
3764 IWineD3DSurfaceImpl_LockRect,
3765 IWineD3DSurfaceImpl_UnlockRect,
3766 IWineD3DSurfaceImpl_GetDC,
3767 IWineD3DSurfaceImpl_ReleaseDC,
3768 IWineD3DSurfaceImpl_Flip,
3769 IWineD3DSurfaceImpl_Blt,
3770 IWineD3DSurfaceImpl_GetBltStatus,
3771 IWineD3DSurfaceImpl_GetFlipStatus,
3772 IWineD3DSurfaceImpl_IsLost,
3773 IWineD3DSurfaceImpl_Restore,
3774 IWineD3DSurfaceImpl_BltFast,
3775 IWineD3DSurfaceImpl_GetPalette,
3776 IWineD3DSurfaceImpl_SetPalette,
3777 IWineD3DSurfaceImpl_RealizePalette,
3778 IWineD3DSurfaceImpl_SetColorKey,
3779 IWineD3DSurfaceImpl_GetPitch,
3780 IWineD3DSurfaceImpl_SetMem,
3781 IWineD3DSurfaceImpl_SetOverlayPosition,
3782 IWineD3DSurfaceImpl_GetOverlayPosition,
3783 IWineD3DSurfaceImpl_UpdateOverlayZOrder,
3784 IWineD3DSurfaceImpl_UpdateOverlay,
3785 IWineD3DSurfaceImpl_SetClipper,
3786 IWineD3DSurfaceImpl_GetClipper,
3787 /* Internal use: */
3788 IWineD3DSurfaceImpl_AddDirtyRect,
3789 IWineD3DSurfaceImpl_LoadTexture,
3790 IWineD3DSurfaceImpl_SaveSnapshot,
3791 IWineD3DSurfaceImpl_SetContainer,
3792 IWineD3DSurfaceImpl_SetGlTextureDesc,
3793 IWineD3DSurfaceImpl_GetGlDesc,
3794 IWineD3DSurfaceImpl_GetData,
3795 IWineD3DSurfaceImpl_SetFormat,
3796 IWineD3DSurfaceImpl_PrivateSetup