wined3d: Fix a null pointer dereference.
[wine.git] / dlls / wined3d / surface.c
blob20ade5c8708634aa2e8cd40200aaf248fdcacaaa
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_LOCKABLE)) {
677 /* Note: UpdateTextures calls CopyRects which calls this routine to populate the
678 texture regions, and since the destination is an unlockable region we need
679 to tolerate this */
680 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
681 /*return WINED3DERR_INVALIDCALL; */
684 pLockedRect->Pitch = IWineD3DSurface_GetPitch(iface);
686 /* Mark the surface locked */
687 This->Flags |= SFLAG_LOCKED;
689 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy */
690 if(!This->resource.allocatedMemory) {
691 This->resource.allocatedMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + 4);
692 This->Flags &= ~SFLAG_INSYSMEM; /* This is the marker that surface data has to be downloaded */
695 /* Calculate the correct start address to report */
696 if (NULL == pRect) {
697 pLockedRect->pBits = This->resource.allocatedMemory;
698 This->lockedRect.left = 0;
699 This->lockedRect.top = 0;
700 This->lockedRect.right = This->currentDesc.Width;
701 This->lockedRect.bottom = This->currentDesc.Height;
702 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);
703 } else {
704 TRACE("Lock Rect (%p) = l %d, t %d, r %d, b %d\n", pRect, pRect->left, pRect->top, pRect->right, pRect->bottom);
706 /* DXTn textures are based on compressed blocks of 4x4 pixels, each
707 * 16 bytes large (8 bytes in case of DXT1). Because of that Pitch has
708 * slightly different meaning compared to regular textures. For DXTn
709 * textures Pitch is the size of a row of blocks, 4 high and "width"
710 * long. The x offset is calculated differently as well, since moving 4
711 * pixels to the right actually moves an entire 4x4 block to right, ie
712 * 16 bytes (8 in case of DXT1). */
713 if (This->resource.format == WINED3DFMT_DXT1) {
714 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top / 4) + (pRect->left * 2);
715 } else if (This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3
716 || This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
717 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top / 4) + (pRect->left * 4);
718 } else {
719 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top) + (pRect->left * This->bytesPerPixel);
721 This->lockedRect.left = pRect->left;
722 This->lockedRect.top = pRect->top;
723 This->lockedRect.right = pRect->right;
724 This->lockedRect.bottom = pRect->bottom;
727 if (This->Flags & SFLAG_NONPOW2) {
728 TRACE("Locking non-power 2 texture\n");
731 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
732 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
733 * changed
735 if(!(This->Flags & SFLAG_DYNLOCK)) {
736 This->lockCount++;
737 /* MAXLOCKCOUNT is defined in wined3d_private.h */
738 if(This->lockCount > MAXLOCKCOUNT) {
739 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
740 This->Flags |= SFLAG_DYNLOCK;
744 if (Flags & WINED3DLOCK_DISCARD) {
745 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
746 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
747 This->Flags |= SFLAG_INSYSMEM;
750 if (This->Flags & SFLAG_INSYSMEM) {
751 TRACE("Local copy is up to date, not downloading data\n");
752 goto lock_end;
755 /* Now download the surface content from opengl
756 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
757 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
759 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
760 if(swapchain || iface == myDevice->render_targets[0]) {
761 BOOL srcIsUpsideDown;
763 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
764 static BOOL warned = FALSE;
765 if(!warned) {
766 ERR("The application tries to lock the render target, but render target locking is disabled\n");
767 warned = TRUE;
769 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
770 return WINED3D_OK;
773 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
774 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
775 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
776 * context->last_was_blit set on the unlock.
778 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
779 ENTER_GL();
781 /* Select the correct read buffer, and give some debug output.
782 * There is no need to keep track of the current read buffer or reset it, every part of the code
783 * that reads sets the read buffer as desired.
785 if(!swapchain) {
786 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
787 * Read from the back buffer
789 TRACE("Locking offscreen render target\n");
790 glReadBuffer(myDevice->offscreenBuffer);
791 srcIsUpsideDown = TRUE;
792 } else {
793 GLenum buffer = surface_get_gl_buffer(iface, (IWineD3DSwapChain *)swapchain);
794 TRACE("Locking %#x buffer\n", buffer);
795 glReadBuffer(buffer);
796 checkGLcall("glReadBuffer");
798 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
799 srcIsUpsideDown = FALSE;
802 switch(wined3d_settings.rendertargetlock_mode) {
803 case RTL_AUTO:
804 case RTL_READDRAW:
805 case RTL_READTEX:
806 read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
807 break;
809 case RTL_TEXDRAW:
810 case RTL_TEXTEX:
811 read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
812 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
813 break;
815 LEAVE_GL();
817 /* Mark the local copy up to date if a full download was done */
818 if(This->lockedRect.left == 0 &&
819 This->lockedRect.top == 0 &&
820 This->lockedRect.right == This->currentDesc.Width &&
821 This->lockedRect.bottom == This->currentDesc.Height) {
822 This->Flags |= SFLAG_INSYSMEM;
824 } else if(iface == myDevice->stencilBufferTarget) {
825 /** the depth stencil in openGL has a format of GL_FLOAT
826 * which should be good for WINED3DFMT_D16_LOCKABLE
827 * and WINED3DFMT_D16
828 * it is unclear what format the stencil buffer is in except.
829 * 'Each index is converted to fixed point...
830 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
831 * mappings in the table GL_PIXEL_MAP_S_TO_S.
832 * glReadPixels(This->lockedRect.left,
833 * This->lockedRect.bottom - j - 1,
834 * This->lockedRect.right - This->lockedRect.left,
835 * 1,
836 * GL_DEPTH_COMPONENT,
837 * type,
838 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
840 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
841 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
842 * none of that is the case the problem is not in this function :-)
843 ********************************************/
844 FIXME("Depth stencil locking not supported yet\n");
845 } else {
846 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
847 TRACE("locking an ordinary surface\n");
849 if (0 != This->glDescription.textureName) {
850 /* Now I have to copy thing bits back */
852 if(myDevice->createParms.BehaviorFlags & WINED3DCREATE_MULTITHREADED) {
853 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
856 ENTER_GL();
857 /* Make sure that a proper texture unit is selected, bind the texture and dirtify the sampler to restore the texture on the next draw */
858 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
859 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
860 checkGLcall("glActiveTextureARB");
862 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(0));
863 IWineD3DSurface_PreLoad(iface);
865 surface_download_data(This);
866 LEAVE_GL();
870 lock_end:
871 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
872 /* Don't dirtify */
873 } else {
874 IWineD3DBaseTexture *pBaseTexture;
876 * Dirtify on lock
877 * as seen in msdn docs
879 IWineD3DSurface_AddDirtyRect(iface, &This->lockedRect);
881 /** Dirtify Container if needed */
882 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
883 TRACE("Making container dirty\n");
884 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
885 IWineD3DBaseTexture_Release(pBaseTexture);
886 } else {
887 TRACE("Surface is standalone, no need to dirty the container\n");
891 TRACE("returning memory@%p, pitch(%d) dirtyfied(%d)\n", pLockedRect->pBits, pLockedRect->Pitch,
892 This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
893 return WINED3D_OK;
896 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This) {
897 GLint prev_store;
898 GLint prev_rasterpos[4];
899 GLint skipBytes = 0;
900 BOOL storechanged = FALSE, memory_allocated = FALSE;
901 GLint fmt, type;
902 BYTE *mem;
903 UINT bpp;
904 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
906 glDisable(GL_TEXTURE_2D);
907 vcheckGLcall("glDisable(GL_TEXTURE_2D)");
909 glFlush();
910 vcheckGLcall("glFlush");
911 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
912 vcheckGLcall("glIntegerv");
913 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
914 vcheckGLcall("glIntegerv");
915 glPixelZoom(1.0, -1.0);
916 vcheckGLcall("glPixelZoom");
918 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
919 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
920 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
922 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
923 vcheckGLcall("glRasterPos2f");
925 /* Some drivers(radeon dri, others?) don't like exceptions during
926 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
927 * after ReleaseDC. Reading it will cause an exception, which x11drv will
928 * catch to put the dib section in InSync mode, which leads to a crash
929 * and a blocked x server on my radeon card.
931 * The following lines read the dib section so it is put in inSync mode
932 * before glDrawPixels is called and the crash is prevented. There won't
933 * be any interfering gdi accesses, because UnlockRect is called from
934 * ReleaseDC, and the app won't use the dc any more afterwards.
936 if(This->Flags & SFLAG_DIBSECTION) {
937 volatile BYTE read;
938 read = This->resource.allocatedMemory[0];
941 switch (This->resource.format) {
942 /* No special care needed */
943 case WINED3DFMT_A4R4G4B4:
944 case WINED3DFMT_R5G6B5:
945 case WINED3DFMT_A1R5G5B5:
946 case WINED3DFMT_R8G8B8:
947 type = This->glDescription.glType;
948 fmt = This->glDescription.glFormat;
949 mem = This->resource.allocatedMemory;
950 bpp = This->bytesPerPixel;
951 break;
953 case WINED3DFMT_X4R4G4B4:
955 int size;
956 unsigned short *data;
957 data = (unsigned short *)This->resource.allocatedMemory;
958 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
959 while(size > 0) {
960 *data |= 0xF000;
961 data++;
962 size--;
964 type = This->glDescription.glType;
965 fmt = This->glDescription.glFormat;
966 mem = This->resource.allocatedMemory;
967 bpp = This->bytesPerPixel;
969 break;
971 case WINED3DFMT_X1R5G5B5:
973 int size;
974 unsigned short *data;
975 data = (unsigned short *)This->resource.allocatedMemory;
976 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
977 while(size > 0) {
978 *data |= 0x8000;
979 data++;
980 size--;
982 type = This->glDescription.glType;
983 fmt = This->glDescription.glFormat;
984 mem = This->resource.allocatedMemory;
985 bpp = This->bytesPerPixel;
987 break;
989 case WINED3DFMT_X8R8G8B8:
991 /* make sure the X byte is set to alpha on, since it
992 could be any random value. This fixes the intro movie in Pirates! */
993 int size;
994 unsigned int *data;
995 data = (unsigned int *)This->resource.allocatedMemory;
996 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
997 while(size > 0) {
998 *data |= 0xFF000000;
999 data++;
1000 size--;
1003 /* Fall through */
1005 case WINED3DFMT_A8R8G8B8:
1007 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
1008 vcheckGLcall("glPixelStorei");
1009 storechanged = TRUE;
1010 type = This->glDescription.glType;
1011 fmt = This->glDescription.glFormat;
1012 mem = This->resource.allocatedMemory;
1013 bpp = This->bytesPerPixel;
1015 break;
1017 case WINED3DFMT_A2R10G10B10:
1019 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
1020 vcheckGLcall("glPixelStorei");
1021 storechanged = TRUE;
1022 type = This->glDescription.glType;
1023 fmt = This->glDescription.glFormat;
1024 mem = This->resource.allocatedMemory;
1025 bpp = This->bytesPerPixel;
1027 break;
1029 case WINED3DFMT_P8:
1031 int height = This->glRect.bottom - This->glRect.top;
1032 type = GL_UNSIGNED_BYTE;
1033 fmt = GL_RGBA;
1035 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * sizeof(DWORD));
1036 if(!mem) {
1037 ERR("Out of memory\n");
1038 return;
1040 memory_allocated = TRUE;
1041 d3dfmt_convert_surface(This->resource.allocatedMemory,
1042 mem,
1043 pitch,
1044 pitch,
1045 height,
1046 pitch * 4,
1047 CONVERT_PALETTED,
1048 This);
1049 bpp = This->bytesPerPixel * 4;
1050 pitch *= 4;
1052 break;
1054 default:
1055 FIXME("Unsupported Format %u in locking func\n", This->resource.format);
1057 /* Give it a try */
1058 type = This->glDescription.glType;
1059 fmt = This->glDescription.glFormat;
1060 mem = This->resource.allocatedMemory;
1061 bpp = This->bytesPerPixel;
1064 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1065 (This->lockedRect.bottom - This->lockedRect.top)-1,
1066 fmt, type,
1067 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1068 checkGLcall("glDrawPixels");
1069 glPixelZoom(1.0,1.0);
1070 vcheckGLcall("glPixelZoom");
1072 glRasterPos3iv(&prev_rasterpos[0]);
1073 vcheckGLcall("glRasterPos3iv");
1075 /* Reset to previous pack row length */
1076 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1077 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1078 if(storechanged) {
1079 glPixelStorei(GL_PACK_SWAP_BYTES, prev_store);
1080 vcheckGLcall("glPixelStorei GL_PACK_SWAP_BYTES");
1083 /* Blitting environment requires that 2D texturing is enabled. It was turned off before,
1084 * turn it on again
1086 glEnable(GL_TEXTURE_2D);
1087 checkGLcall("glEnable(GL_TEXTURE_2D)");
1089 if(memory_allocated) HeapFree(GetProcessHeap(), 0, mem);
1090 return;
1093 static void flush_to_framebuffer_texture(IWineD3DSurfaceImpl *This) {
1094 float glTexCoord[4];
1096 glTexCoord[0] = (float) This->lockedRect.left / (float) This->pow2Width; /* left */
1097 glTexCoord[1] = (float) This->lockedRect.right / (float) This->pow2Width; /* right */
1098 glTexCoord[2] = (float) This->lockedRect.top / (float) This->pow2Height; /* top */
1099 glTexCoord[3] = (float) This->lockedRect.bottom / (float) This->pow2Height; /* bottom */
1101 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
1103 ENTER_GL();
1105 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
1106 checkGLcall("glEnable glBindTexture");
1108 /* No filtering for blts */
1109 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1110 checkGLcall("glTexParameteri");
1111 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1112 checkGLcall("glTexParameteri");
1114 /* Start drawing a quad */
1115 glBegin(GL_QUADS);
1117 glColor3d(1.0f, 1.0f, 1.0f);
1118 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
1119 glVertex3f(This->lockedRect.left, This->lockedRect.top, 0.0);
1121 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
1122 glVertex3f(This->lockedRect.left, This->lockedRect.bottom, 0.0);
1124 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
1125 glVertex3d(This->lockedRect.right, This->lockedRect.bottom, 0.0);
1127 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
1128 glVertex3f(This->lockedRect.right, This->lockedRect.top, 0.0);
1130 glEnd();
1131 checkGLcall("glEnd");
1133 /* Unbind the texture */
1134 glBindTexture(GL_TEXTURE_2D, 0);
1135 checkGLcall("glEnable glBindTexture");
1137 LEAVE_GL();
1140 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1141 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1142 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1143 IWineD3DSwapChainImpl *swapchain = NULL;
1145 if (!(This->Flags & SFLAG_LOCKED)) {
1146 WARN("trying to Unlock an unlocked surf@%p\n", This);
1147 return WINED3DERR_INVALIDCALL;
1150 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1152 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1153 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1154 goto unlock_end;
1157 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1158 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1159 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1160 static BOOL warned = FALSE;
1161 if(!warned) {
1162 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1163 warned = TRUE;
1165 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1166 goto unlock_end;
1169 /* Activate the correct context for the render target */
1170 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1171 ENTER_GL();
1173 if(!swapchain) {
1174 /* Primary offscreen render target */
1175 TRACE("Offscreen render target\n");
1176 glDrawBuffer(myDevice->offscreenBuffer);
1177 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1178 } else {
1179 GLenum buffer = surface_get_gl_buffer(iface, (IWineD3DSwapChain *)swapchain);
1180 TRACE("Unlocking %#x buffer\n", buffer);
1181 glDrawBuffer(buffer);
1182 checkGLcall("glDrawBuffer");
1184 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1187 switch(wined3d_settings.rendertargetlock_mode) {
1188 case RTL_AUTO:
1189 case RTL_READDRAW:
1190 case RTL_TEXDRAW:
1191 flush_to_framebuffer_drawpixels(This);
1192 break;
1194 case RTL_READTEX:
1195 case RTL_TEXTEX:
1196 flush_to_framebuffer_texture(This);
1197 break;
1199 if(!swapchain) {
1200 glDrawBuffer(myDevice->offscreenBuffer);
1201 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1202 } else if(swapchain->backBuffer) {
1203 glDrawBuffer(GL_BACK);
1204 checkGLcall("glDrawBuffer(GL_BACK)");
1205 } else {
1206 glDrawBuffer(GL_FRONT);
1207 checkGLcall("glDrawBuffer(GL_FRONT)");
1209 LEAVE_GL();
1211 This->dirtyRect.left = This->currentDesc.Width;
1212 This->dirtyRect.top = This->currentDesc.Height;
1213 This->dirtyRect.right = 0;
1214 This->dirtyRect.bottom = 0;
1215 This->Flags |= SFLAG_INDRAWABLE;
1216 } else if(iface == myDevice->stencilBufferTarget) {
1217 FIXME("Depth Stencil buffer locking is not implemented\n");
1218 } else {
1219 /* The rest should be a normal texture */
1220 IWineD3DBaseTextureImpl *impl;
1221 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1222 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1223 * states need resetting
1225 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1226 if(impl->baseTexture.bindCount) {
1227 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1229 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1233 unlock_end:
1234 This->Flags &= ~SFLAG_LOCKED;
1235 memset(&This->lockedRect, 0, sizeof(RECT));
1236 return WINED3D_OK;
1239 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1240 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1241 WINED3DLOCKED_RECT lock;
1242 UINT usage;
1243 BITMAPINFO* b_info;
1244 HDC ddc;
1245 DWORD *masks;
1246 HRESULT hr;
1247 RGBQUAD col[256];
1248 const StaticPixelFormatDesc *formatEntry = getFormatDescEntry(This->resource.format, NULL, NULL);
1250 TRACE("(%p)->(%p)\n",This,pHDC);
1252 if(This->Flags & SFLAG_USERPTR) {
1253 ERR("Not supported on surfaces with an application-provided surfaces\n");
1254 return WINEDDERR_NODC;
1257 /* Give more detailed info for ddraw */
1258 if (This->Flags & SFLAG_DCINUSE)
1259 return WINEDDERR_DCALREADYCREATED;
1261 /* Can't GetDC if the surface is locked */
1262 if (This->Flags & SFLAG_LOCKED)
1263 return WINED3DERR_INVALIDCALL;
1265 memset(&lock, 0, sizeof(lock)); /* To be sure */
1267 /* Create a DIB section if there isn't a hdc yet */
1268 if(!This->hDC) {
1269 int extraline = 0;
1270 SYSTEM_INFO sysInfo;
1271 void *oldmem = This->resource.allocatedMemory;
1273 switch (This->bytesPerPixel) {
1274 case 2:
1275 case 4:
1276 /* Allocate extra space to store the RGB bit masks. */
1277 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
1278 break;
1280 case 3:
1281 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
1282 break;
1284 default:
1285 /* Allocate extra space for a palette. */
1286 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1287 sizeof(BITMAPINFOHEADER)
1288 + sizeof(RGBQUAD)
1289 * (1 << (This->bytesPerPixel * 8)));
1290 break;
1293 if (!b_info)
1294 return E_OUTOFMEMORY;
1296 /* Some apps access the surface in via DWORDs, and do not take the necessary care at the end of the
1297 * surface. So we need at least extra 4 bytes at the end of the surface. Check against the page size,
1298 * if the last page used for the surface has at least 4 spare bytes we're safe, otherwise
1299 * add an extra line to the dib section
1301 GetSystemInfo(&sysInfo);
1302 if( ((This->resource.size + 3) % sysInfo.dwPageSize) < 4) {
1303 extraline = 1;
1304 TRACE("Adding an extra line to the dib section\n");
1307 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1308 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
1309 b_info->bmiHeader.biWidth = IWineD3DSurface_GetPitch(iface) / This->bytesPerPixel;
1310 b_info->bmiHeader.biHeight = -This->currentDesc.Height -extraline;
1311 b_info->bmiHeader.biSizeImage = ( This->currentDesc.Height + extraline) * IWineD3DSurface_GetPitch(iface);
1312 b_info->bmiHeader.biPlanes = 1;
1313 b_info->bmiHeader.biBitCount = This->bytesPerPixel * 8;
1315 b_info->bmiHeader.biXPelsPerMeter = 0;
1316 b_info->bmiHeader.biYPelsPerMeter = 0;
1317 b_info->bmiHeader.biClrUsed = 0;
1318 b_info->bmiHeader.biClrImportant = 0;
1320 /* Get the bit masks */
1321 masks = (DWORD *) &(b_info->bmiColors);
1322 switch (This->resource.format) {
1323 case WINED3DFMT_R8G8B8:
1324 usage = DIB_RGB_COLORS;
1325 b_info->bmiHeader.biCompression = BI_RGB;
1326 break;
1328 case WINED3DFMT_X1R5G5B5:
1329 case WINED3DFMT_A1R5G5B5:
1330 case WINED3DFMT_A4R4G4B4:
1331 case WINED3DFMT_X4R4G4B4:
1332 case WINED3DFMT_R3G3B2:
1333 case WINED3DFMT_A8R3G3B2:
1334 case WINED3DFMT_A2B10G10R10:
1335 case WINED3DFMT_A8B8G8R8:
1336 case WINED3DFMT_X8B8G8R8:
1337 case WINED3DFMT_A2R10G10B10:
1338 case WINED3DFMT_R5G6B5:
1339 case WINED3DFMT_A16B16G16R16:
1340 usage = 0;
1341 b_info->bmiHeader.biCompression = BI_BITFIELDS;
1342 masks[0] = formatEntry->redMask;
1343 masks[1] = formatEntry->greenMask;
1344 masks[2] = formatEntry->blueMask;
1345 break;
1347 default:
1348 /* Don't know palette */
1349 b_info->bmiHeader.biCompression = BI_RGB;
1350 usage = 0;
1351 break;
1354 ddc = GetDC(0);
1355 if (ddc == 0) {
1356 HeapFree(GetProcessHeap(), 0, b_info);
1357 return HRESULT_FROM_WIN32(GetLastError());
1360 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);
1361 This->dib.DIBsection = CreateDIBSection(ddc, b_info, usage, &This->dib.bitmap_data, 0 /* Handle */, 0 /* Offset */);
1362 ReleaseDC(0, ddc);
1364 if (!This->dib.DIBsection) {
1365 ERR("CreateDIBSection failed!\n");
1366 HeapFree(GetProcessHeap(), 0, b_info);
1367 return HRESULT_FROM_WIN32(GetLastError());
1370 TRACE("DIBSection at : %p\n", This->dib.bitmap_data);
1372 /* copy the existing surface to the dib section */
1373 if(This->resource.allocatedMemory) {
1374 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, b_info->bmiHeader.biSizeImage);
1375 /* We won't need that any more */
1376 } else {
1377 /* This is to make LockRect read the gl Texture although memory is allocated */
1378 This->Flags &= ~SFLAG_INSYSMEM;
1381 HeapFree(GetProcessHeap(), 0, b_info);
1383 /* Use the dib section from now on */
1384 This->resource.allocatedMemory = This->dib.bitmap_data;
1386 /* Now allocate a HDC */
1387 This->hDC = CreateCompatibleDC(0);
1388 This->dib.holdbitmap = SelectObject(This->hDC, This->dib.DIBsection);
1389 TRACE("using wined3d palette %p\n", This->palette);
1390 SelectPalette(This->hDC,
1391 This->palette ? This->palette->hpal : 0,
1392 FALSE);
1394 This->Flags |= SFLAG_DIBSECTION;
1396 if(This->Flags & SFLAG_CLIENT) {
1397 IWineD3DSurface_PreLoad(iface);
1399 HeapFree(GetProcessHeap(), 0, oldmem);
1402 /* Lock the surface */
1403 hr = IWineD3DSurface_LockRect(iface,
1404 &lock,
1405 NULL,
1407 if(FAILED(hr)) {
1408 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1409 /* keep the dib section */
1410 return hr;
1413 if(This->resource.format == WINED3DFMT_P8 ||
1414 This->resource.format == WINED3DFMT_A8P8) {
1415 unsigned int n;
1416 if(This->palette) {
1417 PALETTEENTRY ent[256];
1419 GetPaletteEntries(This->palette->hpal, 0, 256, ent);
1420 for (n=0; n<256; n++) {
1421 col[n].rgbRed = ent[n].peRed;
1422 col[n].rgbGreen = ent[n].peGreen;
1423 col[n].rgbBlue = ent[n].peBlue;
1424 col[n].rgbReserved = 0;
1426 } else {
1427 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1429 for (n=0; n<256; n++) {
1430 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
1431 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
1432 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
1433 col[n].rgbReserved = 0;
1437 SetDIBColorTable(This->hDC, 0, 256, col);
1440 *pHDC = This->hDC;
1441 TRACE("returning %p\n",*pHDC);
1442 This->Flags |= SFLAG_DCINUSE;
1444 return WINED3D_OK;
1447 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1448 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1450 TRACE("(%p)->(%p)\n",This,hDC);
1452 if (!(This->Flags & SFLAG_DCINUSE))
1453 return WINED3DERR_INVALIDCALL;
1455 /* we locked first, so unlock now */
1456 IWineD3DSurface_UnlockRect(iface);
1458 This->Flags &= ~SFLAG_DCINUSE;
1460 return WINED3D_OK;
1463 /* ******************************************************
1464 IWineD3DSurface Internal (No mapping to directx api) parts follow
1465 ****************************************************** */
1467 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) {
1468 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1469 const GlPixelFormatDesc *glDesc;
1470 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1472 /* Default values: From the surface */
1473 *format = glDesc->glFormat;
1474 *internal = srgb_mode?glDesc->glGammaInternal:glDesc->glInternal;
1475 *type = glDesc->glType;
1476 *convert = NO_CONVERSION;
1477 *target_bpp = This->bytesPerPixel;
1479 /* Ok, now look if we have to do any conversion */
1480 switch(This->resource.format) {
1481 case WINED3DFMT_P8:
1482 /* ****************
1483 Paletted Texture
1484 **************** */
1485 /* Use conversion when the paletted texture extension is not available, or when it is available make sure it is used
1486 * for texturing as it won't work for calls like glDraw-/glReadPixels and further also use conversion in case of color keying.
1488 if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) || colorkey_active || (!use_texturing && GL_SUPPORT(EXT_PALETTED_TEXTURE)) ) {
1489 *format = GL_RGBA;
1490 *internal = GL_RGBA;
1491 *type = GL_UNSIGNED_BYTE;
1492 *target_bpp = 4;
1493 if(colorkey_active) {
1494 *convert = CONVERT_PALETTED_CK;
1495 } else {
1496 *convert = CONVERT_PALETTED;
1500 break;
1502 case WINED3DFMT_R3G3B2:
1503 /* **********************
1504 GL_UNSIGNED_BYTE_3_3_2
1505 ********************** */
1506 if (colorkey_active) {
1507 /* This texture format will never be used.. So do not care about color keying
1508 up until the point in time it will be needed :-) */
1509 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1511 break;
1513 case WINED3DFMT_R5G6B5:
1514 if (colorkey_active) {
1515 *convert = CONVERT_CK_565;
1516 *format = GL_RGBA;
1517 *internal = GL_RGBA;
1518 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1520 break;
1522 case WINED3DFMT_X1R5G5B5:
1523 if (colorkey_active) {
1524 *convert = CONVERT_CK_5551;
1525 *format = GL_BGRA;
1526 *internal = GL_RGBA;
1527 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1529 break;
1531 case WINED3DFMT_R8G8B8:
1532 if (colorkey_active) {
1533 *convert = CONVERT_CK_RGB24;
1534 *format = GL_RGBA;
1535 *internal = GL_RGBA;
1536 *type = GL_UNSIGNED_INT_8_8_8_8;
1537 *target_bpp = 4;
1539 break;
1541 case WINED3DFMT_X8R8G8B8:
1542 if (colorkey_active) {
1543 *convert = CONVERT_RGB32_888;
1544 *format = GL_RGBA;
1545 *internal = GL_RGBA;
1546 *type = GL_UNSIGNED_INT_8_8_8_8;
1548 break;
1550 case WINED3DFMT_V8U8:
1551 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1552 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1553 *format = GL_DUDV_ATI;
1554 *internal = GL_DU8DV8_ATI;
1555 *type = GL_BYTE;
1556 /* No conversion - Just change the gl type */
1557 break;
1559 *convert = CONVERT_V8U8;
1560 *format = GL_BGR;
1561 *internal = GL_RGB8;
1562 *type = GL_UNSIGNED_BYTE;
1563 *target_bpp = 3;
1564 break;
1566 case WINED3DFMT_X8L8V8U8:
1567 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1568 *convert = CONVERT_X8L8V8U8;
1569 *format = GL_BGRA;
1570 *internal = GL_RGBA8;
1571 *type = GL_UNSIGNED_BYTE;
1572 *target_bpp = 4;
1573 /* Not supported by GL_ATI_envmap_bumpmap */
1574 break;
1576 case WINED3DFMT_Q8W8V8U8:
1577 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1578 *convert = CONVERT_Q8W8V8U8;
1579 *format = GL_BGRA;
1580 *internal = GL_RGBA8;
1581 *type = GL_UNSIGNED_BYTE;
1582 *target_bpp = 4;
1583 /* Not supported by GL_ATI_envmap_bumpmap */
1584 break;
1586 case WINED3DFMT_V16U16:
1587 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1588 *convert = CONVERT_V16U16;
1589 *format = GL_BGR;
1590 *internal = GL_RGB16;
1591 *type = GL_SHORT;
1592 *target_bpp = 6;
1593 /* What should I do here about GL_ATI_envmap_bumpmap?
1594 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1596 break;
1598 case WINED3DFMT_A4L4:
1599 /* A4L4 exists as an internal gl format, but for some reason there is not
1600 * format+type combination to load it. Thus convert it to A8L8, then load it
1601 * with A4L4 internal, but A8L8 format+type
1603 *convert = CONVERT_A4L4;
1604 *format = GL_LUMINANCE_ALPHA;
1605 *internal = GL_LUMINANCE4_ALPHA4;
1606 *type = GL_UNSIGNED_BYTE;
1607 *target_bpp = 2;
1608 break;
1610 case WINED3DFMT_R32F:
1611 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1612 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1613 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1614 * 1.0 instead.
1616 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1618 *convert = CONVERT_R32F;
1619 *format = GL_RGB;
1620 *internal = GL_RGB32F_ARB;
1621 *type = GL_FLOAT;
1622 *target_bpp = 12;
1623 break;
1625 case WINED3DFMT_R16F:
1626 /* Simmilar to R32F */
1627 *convert = CONVERT_R16F;
1628 *format = GL_RGB;
1629 *internal = GL_RGB16F_ARB;
1630 *type = GL_HALF_FLOAT_ARB;
1631 *target_bpp = 6;
1632 break;
1634 default:
1635 break;
1638 return WINED3D_OK;
1641 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf) {
1642 BYTE *source, *dest;
1643 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surf);
1645 switch (convert) {
1646 case NO_CONVERSION:
1648 memcpy(dst, src, pitch * height);
1649 break;
1651 case CONVERT_PALETTED:
1652 case CONVERT_PALETTED_CK:
1654 IWineD3DPaletteImpl* pal = surf->palette;
1655 BYTE table[256][4];
1656 unsigned int i;
1657 unsigned int x, y;
1659 if( pal == NULL) {
1660 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1663 if (pal == NULL) {
1664 /* Still no palette? Use the device's palette */
1665 /* Get the surface's palette */
1666 for (i = 0; i < 256; i++) {
1667 IWineD3DDeviceImpl *device = surf->resource.wineD3DDevice;
1669 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1670 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1671 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1672 if ((convert == CONVERT_PALETTED_CK) &&
1673 (i >= surf->SrcBltCKey.dwColorSpaceLowValue) &&
1674 (i <= surf->SrcBltCKey.dwColorSpaceHighValue)) {
1675 /* We should maybe here put a more 'neutral' color than the standard bright purple
1676 one often used by application to prevent the nice purple borders when bi-linear
1677 filtering is on */
1678 table[i][3] = 0x00;
1679 } else {
1680 table[i][3] = 0xFF;
1683 } else {
1684 TRACE("Using surface palette %p\n", pal);
1685 /* Get the surface's palette */
1686 for (i = 0; i < 256; i++) {
1687 table[i][0] = pal->palents[i].peRed;
1688 table[i][1] = pal->palents[i].peGreen;
1689 table[i][2] = pal->palents[i].peBlue;
1690 if ((convert == CONVERT_PALETTED_CK) &&
1691 (i >= surf->SrcBltCKey.dwColorSpaceLowValue) &&
1692 (i <= surf->SrcBltCKey.dwColorSpaceHighValue)) {
1693 /* We should maybe here put a more 'neutral' color than the standard bright purple
1694 one often used by application to prevent the nice purple borders when bi-linear
1695 filtering is on */
1696 table[i][3] = 0x00;
1697 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
1698 table[i][3] = pal->palents[i].peFlags;
1699 } else {
1700 table[i][3] = 0xFF;
1705 for (y = 0; y < height; y++)
1707 source = src + pitch * y;
1708 dest = dst + outpitch * y;
1709 /* This is an 1 bpp format, using the width here is fine */
1710 for (x = 0; x < width; x++) {
1711 BYTE color = *source++;
1712 *dest++ = table[color][0];
1713 *dest++ = table[color][1];
1714 *dest++ = table[color][2];
1715 *dest++ = table[color][3];
1719 break;
1721 case CONVERT_CK_565:
1723 /* Converting the 565 format in 5551 packed to emulate color-keying.
1725 Note : in all these conversion, it would be best to average the averaging
1726 pixels to get the color of the pixel that will be color-keyed to
1727 prevent 'color bleeding'. This will be done later on if ever it is
1728 too visible.
1730 Note2: Nvidia documents say that their driver does not support alpha + color keying
1731 on the same surface and disables color keying in such a case
1733 unsigned int x, y;
1734 WORD *Source;
1735 WORD *Dest;
1737 TRACE("Color keyed 565\n");
1739 for (y = 0; y < height; y++) {
1740 Source = (WORD *) (src + y * pitch);
1741 Dest = (WORD *) (dst + y * outpitch);
1742 for (x = 0; x < width; x++ ) {
1743 WORD color = *Source++;
1744 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1745 if ((color < surf->SrcBltCKey.dwColorSpaceLowValue) ||
1746 (color > surf->SrcBltCKey.dwColorSpaceHighValue)) {
1747 *Dest |= 0x0001;
1749 Dest++;
1753 break;
1755 case CONVERT_CK_5551:
1757 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1758 unsigned int x, y;
1759 WORD *Source;
1760 WORD *Dest;
1761 TRACE("Color keyed 5551\n");
1762 for (y = 0; y < height; y++) {
1763 Source = (WORD *) (src + y * pitch);
1764 Dest = (WORD *) (dst + y * outpitch);
1765 for (x = 0; x < width; x++ ) {
1766 WORD color = *Source++;
1767 *Dest = color;
1768 if ((color < surf->SrcBltCKey.dwColorSpaceLowValue) ||
1769 (color > surf->SrcBltCKey.dwColorSpaceHighValue)) {
1770 *Dest |= (1 << 15);
1772 else {
1773 *Dest &= ~(1 << 15);
1775 Dest++;
1779 break;
1781 case CONVERT_V8U8:
1783 unsigned int x, y;
1784 short *Source;
1785 unsigned char *Dest;
1786 for(y = 0; y < height; y++) {
1787 Source = (short *) (src + y * pitch);
1788 Dest = (unsigned char *) (dst + y * outpitch);
1789 for (x = 0; x < width; x++ ) {
1790 long color = (*Source++);
1791 /* B */ Dest[0] = 0xff;
1792 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1793 /* R */ Dest[2] = (color) + 128; /* U */
1794 Dest += 3;
1797 break;
1800 case CONVERT_Q8W8V8U8:
1802 unsigned int x, y;
1803 DWORD *Source;
1804 unsigned char *Dest;
1805 for(y = 0; y < height; y++) {
1806 Source = (DWORD *) (src + y * pitch);
1807 Dest = (unsigned char *) (dst + y * outpitch);
1808 for (x = 0; x < width; x++ ) {
1809 long color = (*Source++);
1810 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1811 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1812 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1813 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1814 Dest += 4;
1817 break;
1820 case CONVERT_A4L4:
1822 unsigned int x, y;
1823 unsigned char *Source;
1824 unsigned char *Dest;
1825 for(y = 0; y < height; y++) {
1826 Source = (unsigned char *) (src + y * pitch);
1827 Dest = (unsigned char *) (dst + y * outpitch);
1828 for (x = 0; x < width; x++ ) {
1829 unsigned char color = (*Source++);
1830 /* A */ Dest[1] = (color & 0xf0) << 0;
1831 /* L */ Dest[0] = (color & 0x0f) << 4;
1832 Dest += 2;
1835 break;
1838 case CONVERT_R32F:
1840 unsigned int x, y;
1841 float *Source;
1842 float *Dest;
1843 for(y = 0; y < height; y++) {
1844 Source = (float *) (src + y * pitch);
1845 Dest = (float *) (dst + y * outpitch);
1846 for (x = 0; x < width; x++ ) {
1847 float color = (*Source++);
1848 Dest[0] = color;
1849 Dest[1] = 1.0;
1850 Dest[2] = 1.0;
1851 Dest += 3;
1854 break;
1857 case CONVERT_R16F:
1859 unsigned int x, y;
1860 WORD *Source;
1861 WORD *Dest;
1862 WORD one = 0x3c00;
1863 for(y = 0; y < height; y++) {
1864 Source = (WORD *) (src + y * pitch);
1865 Dest = (WORD *) (dst + y * outpitch);
1866 for (x = 0; x < width; x++ ) {
1867 WORD color = (*Source++);
1868 Dest[0] = color;
1869 Dest[1] = one;
1870 Dest[2] = one;
1871 Dest += 3;
1874 break;
1876 default:
1877 ERR("Unsupported conversation type %d\n", convert);
1879 return WINED3D_OK;
1882 /* This function is used in case of 8bit paletted textures to upload the palette.
1883 For now it only supports GL_EXT_paletted_texture extension but support for other
1884 extensions like ARB_fragment_program and ATI_fragment_shaders will be added as well.
1886 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
1887 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1888 IWineD3DPaletteImpl* pal = This->palette;
1889 BYTE table[256][4];
1890 int i;
1892 if (pal == NULL) {
1893 /* Still no palette? Use the device's palette */
1894 /* Get the surface's palette */
1895 for (i = 0; i < 256; i++) {
1896 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1898 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1899 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1900 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1901 if ((convert == CONVERT_PALETTED_CK) &&
1902 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1903 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1904 /* We should maybe here put a more 'neutral' color than the standard bright purple
1905 one often used by application to prevent the nice purple borders when bi-linear
1906 filtering is on */
1907 table[i][3] = 0x00;
1908 } else {
1909 table[i][3] = 0xFF;
1912 } else {
1913 TRACE("Using surface palette %p\n", pal);
1914 /* Get the surface's palette */
1915 for (i = 0; i < 256; i++) {
1916 table[i][0] = pal->palents[i].peRed;
1917 table[i][1] = pal->palents[i].peGreen;
1918 table[i][2] = pal->palents[i].peBlue;
1919 if ((convert == CONVERT_PALETTED_CK) &&
1920 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1921 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1922 /* We should maybe here put a more 'neutral' color than the standard bright purple
1923 one often used by application to prevent the nice purple borders when bi-linear
1924 filtering is on */
1925 table[i][3] = 0x00;
1926 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
1927 table[i][3] = pal->palents[i].peFlags;
1928 } else {
1929 table[i][3] = 0xFF;
1933 GL_EXTCALL(glColorTableEXT(GL_TEXTURE_2D,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
1936 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
1937 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1939 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
1940 /* If a ddraw-style palette is attached assume no d3d9 palette change.
1941 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
1943 return FALSE;
1946 if(This->palette9) {
1947 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
1948 return FALSE;
1950 } else {
1951 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
1953 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
1954 return TRUE;
1957 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
1958 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1959 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1960 GLenum format, internal, type;
1961 CONVERT_TYPES convert;
1962 int bpp;
1963 int width, pitch, outpitch;
1964 BYTE *mem;
1966 if (!(This->Flags & SFLAG_INTEXTURE)) {
1967 TRACE("Reloading because surface is dirty\n");
1968 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
1969 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
1970 /* Reload: vice versa OR */
1971 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
1972 /* Also reload: Color key is active AND the color key has changed */
1973 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
1974 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
1975 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
1976 TRACE("Reloading because of color keying\n");
1977 } else if(palette9_changed(This)) {
1978 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
1979 } else {
1980 TRACE("surface is already in texture\n");
1981 return WINED3D_OK;
1984 This->Flags |= SFLAG_INTEXTURE;
1986 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
1987 * These resources are not bound by device size or format restrictions. Because of this,
1988 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
1989 * However, these resources can always be created, locked, and copied.
1991 if (This->resource.pool == WINED3DPOOL_SCRATCH )
1993 FIXME("(%p) Operation not supported for scratch textures\n",This);
1994 return WINED3DERR_INVALIDCALL;
1997 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb_mode);
1999 if (This->Flags & SFLAG_INDRAWABLE) {
2000 if (This->glDescription.level != 0)
2001 FIXME("Surface in texture is only supported for level 0\n");
2002 else if (This->resource.format == WINED3DFMT_P8 || This->resource.format == WINED3DFMT_A8P8 ||
2003 This->resource.format == WINED3DFMT_DXT1 || This->resource.format == WINED3DFMT_DXT2 ||
2004 This->resource.format == WINED3DFMT_DXT3 || This->resource.format == WINED3DFMT_DXT4 ||
2005 This->resource.format == WINED3DFMT_DXT5)
2006 FIXME("Format %d not supported\n", This->resource.format);
2007 else {
2008 GLint prevRead;
2010 ENTER_GL();
2011 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2012 vcheckGLcall("glGetIntegerv");
2013 glReadBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2014 vcheckGLcall("glReadBuffer");
2016 if(!(This->Flags & SFLAG_ALLOCATED)) {
2017 surface_allocate_surface(This, internal, This->pow2Width,
2018 This->pow2Height, format, type);
2021 glCopyTexSubImage2D(This->glDescription.target,
2022 This->glDescription.level,
2023 0, 0, 0, 0,
2024 This->currentDesc.Width,
2025 This->currentDesc.Height);
2026 checkGLcall("glCopyTexSubImage2D");
2028 glReadBuffer(prevRead);
2029 vcheckGLcall("glReadBuffer");
2031 LEAVE_GL();
2033 TRACE("Updated target %d\n", This->glDescription.target);
2035 return WINED3D_OK;
2036 } else
2037 /* The only place where LoadTexture() might get called when isInDraw=1
2038 * is ActivateContext where lastActiveRenderTarget is preloaded.
2040 if(iface == device->lastActiveRenderTarget && device->isInDraw)
2041 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
2043 /* Otherwise: System memory copy must be most up to date */
2045 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
2046 This->Flags |= SFLAG_GLCKEY;
2047 This->glCKey = This->SrcBltCKey;
2049 else This->Flags &= ~SFLAG_GLCKEY;
2051 /* The width is in 'length' not in bytes */
2052 width = This->currentDesc.Width;
2053 pitch = IWineD3DSurface_GetPitch(iface);
2055 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
2056 int height = This->currentDesc.Height;
2058 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
2059 outpitch = width * bpp;
2060 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
2062 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
2063 if(!mem) {
2064 ERR("Out of memory %d, %d!\n", outpitch, height);
2065 return WINED3DERR_OUTOFVIDEOMEMORY;
2067 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
2069 This->Flags |= SFLAG_CONVERTED;
2070 } else if (This->resource.format == WINED3DFMT_P8 && GL_SUPPORT(EXT_PALETTED_TEXTURE)) {
2071 d3dfmt_p8_upload_palette(iface, convert);
2072 This->Flags &= ~SFLAG_CONVERTED;
2073 mem = This->resource.allocatedMemory;
2074 } else {
2075 This->Flags &= ~SFLAG_CONVERTED;
2076 mem = This->resource.allocatedMemory;
2079 /* Make sure the correct pitch is used */
2080 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
2082 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
2083 TRACE("non power of two support\n");
2084 if(!(This->Flags & SFLAG_ALLOCATED)) {
2085 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
2087 if (mem) {
2088 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
2090 } else {
2091 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
2092 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
2094 if(!(This->Flags & SFLAG_ALLOCATED)) {
2095 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
2097 if (mem) {
2098 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
2102 /* Restore the default pitch */
2103 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2105 if (mem != This->resource.allocatedMemory)
2106 HeapFree(GetProcessHeap(), 0, mem);
2108 #if 0
2110 static unsigned int gen = 0;
2111 char buffer[4096];
2112 ++gen;
2113 if ((gen % 10) == 0) {
2114 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2115 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2118 * debugging crash code
2119 if (gen == 250) {
2120 void** test = NULL;
2121 *test = 0;
2125 #endif
2127 if (!(This->Flags & SFLAG_DONOTFREE)) {
2128 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
2129 This->resource.allocatedMemory = NULL;
2130 This->Flags &= ~SFLAG_INSYSMEM;
2133 return WINED3D_OK;
2136 #include <errno.h>
2137 #include <stdio.h>
2138 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2139 FILE* f = NULL;
2140 UINT i, y;
2141 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2142 char *allocatedMemory;
2143 char *textureRow;
2144 IWineD3DSwapChain *swapChain = NULL;
2145 int width, height;
2146 GLuint tmpTexture = 0;
2147 DWORD color;
2148 /*FIXME:
2149 Textures may not be stored in ->allocatedgMemory and a GlTexture
2150 so we should lock the surface before saving a snapshot, or at least check that
2152 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2153 by calling GetTexImage and in compressed form by calling
2154 GetCompressedTexImageARB. Queried compressed images can be saved and
2155 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2156 texture images do not need to be processed by the GL and should
2157 significantly improve texture loading performance relative to uncompressed
2158 images. */
2160 /* Setup the width and height to be the internal texture width and height. */
2161 width = This->pow2Width;
2162 height = This->pow2Height;
2163 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2164 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2166 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2167 /* if were not a real texture then read the back buffer into a real texture */
2168 /* we don't want to interfere with the back buffer so read the data into a temporary
2169 * texture and then save the data out of the temporary texture
2171 GLint prevRead;
2172 ENTER_GL();
2173 TRACE("(%p) Reading render target into texture\n", This);
2174 glEnable(GL_TEXTURE_2D);
2176 glGenTextures(1, &tmpTexture);
2177 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2179 glTexImage2D(GL_TEXTURE_2D,
2181 GL_RGBA,
2182 width,
2183 height,
2184 0/*border*/,
2185 GL_RGBA,
2186 GL_UNSIGNED_INT_8_8_8_8_REV,
2187 NULL);
2189 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2190 vcheckGLcall("glGetIntegerv");
2191 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2192 vcheckGLcall("glReadBuffer");
2193 glCopyTexImage2D(GL_TEXTURE_2D,
2195 GL_RGBA,
2198 width,
2199 height,
2202 checkGLcall("glCopyTexImage2D");
2203 glReadBuffer(prevRead);
2204 LEAVE_GL();
2206 } else { /* bind the real texture, and make sure it up to date */
2207 IWineD3DSurface_PreLoad(iface);
2209 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2210 ENTER_GL();
2211 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2212 glGetTexImage(GL_TEXTURE_2D,
2213 This->glDescription.level,
2214 GL_RGBA,
2215 GL_UNSIGNED_INT_8_8_8_8_REV,
2216 allocatedMemory);
2217 checkGLcall("glTexImage2D");
2218 if (tmpTexture) {
2219 glBindTexture(GL_TEXTURE_2D, 0);
2220 glDeleteTextures(1, &tmpTexture);
2222 LEAVE_GL();
2224 f = fopen(filename, "w+");
2225 if (NULL == f) {
2226 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2227 return WINED3DERR_INVALIDCALL;
2229 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2230 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2231 /* TGA header */
2232 fputc(0,f);
2233 fputc(0,f);
2234 fputc(2,f);
2235 fputc(0,f);
2236 fputc(0,f);
2237 fputc(0,f);
2238 fputc(0,f);
2239 fputc(0,f);
2240 fputc(0,f);
2241 fputc(0,f);
2242 fputc(0,f);
2243 fputc(0,f);
2244 /* short width*/
2245 fwrite(&width,2,1,f);
2246 /* short height */
2247 fwrite(&height,2,1,f);
2248 /* format rgba */
2249 fputc(0x20,f);
2250 fputc(0x28,f);
2251 /* raw data */
2252 /* 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 */
2253 if(swapChain)
2254 textureRow = allocatedMemory + (width * (height - 1) *4);
2255 else
2256 textureRow = allocatedMemory;
2257 for (y = 0 ; y < height; y++) {
2258 for (i = 0; i < width; i++) {
2259 color = *((DWORD*)textureRow);
2260 fputc((color >> 16) & 0xFF, f); /* B */
2261 fputc((color >> 8) & 0xFF, f); /* G */
2262 fputc((color >> 0) & 0xFF, f); /* R */
2263 fputc((color >> 24) & 0xFF, f); /* A */
2264 textureRow += 4;
2266 /* take two rows of the pointer to the texture memory */
2267 if(swapChain)
2268 (textureRow-= width << 3);
2271 TRACE("Closing file\n");
2272 fclose(f);
2274 if(swapChain) {
2275 IWineD3DSwapChain_Release(swapChain);
2277 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2278 return WINED3D_OK;
2282 * Slightly inefficient way to handle multiple dirty rects but it works :)
2284 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2285 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2286 IWineD3DBaseTexture *baseTexture = NULL;
2287 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2288 surface_download_data(This);
2290 This->Flags &= ~(SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
2291 if (NULL != pDirtyRect) {
2292 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2293 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2294 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2295 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2296 } else {
2297 This->dirtyRect.left = 0;
2298 This->dirtyRect.top = 0;
2299 This->dirtyRect.right = This->currentDesc.Width;
2300 This->dirtyRect.bottom = This->currentDesc.Height;
2302 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2303 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2304 /* if the container is a basetexture then mark it dirty. */
2305 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2306 TRACE("Passing to container\n");
2307 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2308 IWineD3DBaseTexture_Release(baseTexture);
2310 return WINED3D_OK;
2313 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
2314 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2316 TRACE("This %p, container %p\n", This, container);
2318 /* We can't keep a reference to the container, since the container already keeps a reference to us. */
2320 TRACE("Setting container to %p from %p\n", container, This->container);
2321 This->container = container;
2323 return WINED3D_OK;
2326 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2327 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2328 const GlPixelFormatDesc *glDesc;
2329 const StaticPixelFormatDesc *formatEntry = getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2331 if (This->resource.format != WINED3DFMT_UNKNOWN) {
2332 FIXME("(%p) : The format of the surface must be WINED3DFORMAT_UNKNOWN\n", This);
2333 return WINED3DERR_INVALIDCALL;
2336 TRACE("(%p) : Setting texture format to (%d,%s)\n", This, format, debug_d3dformat(format));
2337 if (format == WINED3DFMT_UNKNOWN) {
2338 This->resource.size = 0;
2339 } else if (format == WINED3DFMT_DXT1) {
2340 /* DXT1 is half byte per pixel */
2341 This->resource.size = ((max(This->pow2Width, 4) * formatEntry->bpp) * max(This->pow2Height, 4)) >> 1;
2343 } else if (format == WINED3DFMT_DXT2 || format == WINED3DFMT_DXT3 ||
2344 format == WINED3DFMT_DXT4 || format == WINED3DFMT_DXT5) {
2345 This->resource.size = ((max(This->pow2Width, 4) * formatEntry->bpp) * max(This->pow2Height, 4));
2346 } else {
2347 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
2348 This->resource.size = ((This->pow2Width * formatEntry->bpp) + alignment - 1) & ~(alignment - 1);
2349 This->resource.size *= This->pow2Height;
2353 /* Setup some glformat defaults */
2354 This->glDescription.glFormat = glDesc->glFormat;
2355 This->glDescription.glFormatInternal = glDesc->glInternal;
2356 This->glDescription.glType = glDesc->glType;
2358 if (format != WINED3DFMT_UNKNOWN) {
2359 This->bytesPerPixel = formatEntry->bpp;
2360 } else {
2361 This->bytesPerPixel = 0;
2364 This->Flags |= (WINED3DFMT_D16_LOCKABLE == format) ? SFLAG_LOCKABLE : 0;
2365 This->Flags &= ~SFLAG_ALLOCATED;
2367 This->resource.format = format;
2369 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);
2371 return WINED3D_OK;
2374 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2375 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2377 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer */
2378 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
2379 ERR("Not supported on render targets\n");
2380 return WINED3DERR_INVALIDCALL;
2383 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2384 WARN("Surface is locked or the HDC is in use\n");
2385 return WINED3DERR_INVALIDCALL;
2388 if(Mem && Mem != This->resource.allocatedMemory) {
2389 void *release = NULL;
2391 /* Do I have to copy the old surface content? */
2392 if(This->Flags & SFLAG_DIBSECTION) {
2393 /* Release the DC. No need to hold the critical section for the update
2394 * Thread because this thread runs only on front buffers, but this method
2395 * fails for render targets in the check above.
2397 SelectObject(This->hDC, This->dib.holdbitmap);
2398 DeleteDC(This->hDC);
2399 /* Release the DIB section */
2400 DeleteObject(This->dib.DIBsection);
2401 This->dib.bitmap_data = NULL;
2402 This->resource.allocatedMemory = NULL;
2403 This->hDC = NULL;
2404 This->Flags &= ~SFLAG_DIBSECTION;
2405 } else if(!(This->Flags & SFLAG_USERPTR)) {
2406 release = This->resource.allocatedMemory;
2408 This->resource.allocatedMemory = Mem;
2409 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2411 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2412 This->Flags &= ~(SFLAG_INDRAWABLE | SFLAG_INTEXTURE);
2414 /* For client textures opengl has to be notified */
2415 if(This->Flags & SFLAG_CLIENT) {
2416 This->Flags &= ~SFLAG_ALLOCATED;
2417 IWineD3DSurface_PreLoad(iface);
2418 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2421 /* Now free the old memory if any */
2422 HeapFree(GetProcessHeap(), 0, release);
2423 } else if(This->Flags & SFLAG_USERPTR) {
2424 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2425 This->resource.allocatedMemory = NULL;
2426 This->Flags &= ~SFLAG_USERPTR;
2428 if(This->Flags & SFLAG_CLIENT) {
2429 This->Flags &= ~SFLAG_ALLOCATED;
2430 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2431 IWineD3DSurface_PreLoad(iface);
2434 return WINED3D_OK;
2437 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2438 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2439 IWineD3DSwapChainImpl *swapchain = NULL;
2440 HRESULT hr;
2441 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2443 /* Flipping is only supported on RenderTargets */
2444 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2446 if(override) {
2447 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2448 * FIXME("(%p) Target override is not supported by now\n", This);
2449 * Additionally, it isn't really possible to support triple-buffering
2450 * properly on opengl at all
2454 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2455 if(!swapchain) {
2456 ERR("Flipped surface is not on a swapchain\n");
2457 return WINEDDERR_NOTFLIPPABLE;
2460 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2461 * and only d3d8 and d3d9 apps specify the presentation interval
2463 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2464 /* Most common case first to avoid wasting time on all the other cases */
2465 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2466 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2467 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2468 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2469 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2470 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2471 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2472 } else {
2473 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2476 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2477 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2478 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2479 return hr;
2482 /* Does a direct frame buffer -> texture copy. Stretching is done
2483 * with single pixel copy calls
2485 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2486 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2487 float xrel, yrel;
2488 UINT row;
2489 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2492 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2493 ENTER_GL();
2494 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2496 /* Bind the target texture */
2497 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
2498 checkGLcall("glBindTexture");
2499 if(!swapchain) {
2500 glReadBuffer(myDevice->offscreenBuffer);
2501 } else {
2502 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2503 glReadBuffer(buffer);
2505 checkGLcall("glReadBuffer");
2507 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2508 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2510 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2511 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2513 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2514 ERR("Texture filtering not supported in direct blit\n");
2516 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2517 ERR("Texture filtering not supported in direct blit\n");
2520 if(upsidedown &&
2521 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2522 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2523 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2525 glCopyTexSubImage2D(This->glDescription.target,
2526 This->glDescription.level,
2527 drect->x1, drect->y1, /* xoffset, yoffset */
2528 srect->x1, Src->currentDesc.Height - srect->y2,
2529 drect->x2 - drect->x1, drect->y2 - drect->y1);
2530 } else {
2531 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2532 /* I have to process this row by row to swap the image,
2533 * otherwise it would be upside down, so stretching in y direction
2534 * doesn't cost extra time
2536 * However, stretching in x direction can be avoided if not necessary
2538 for(row = drect->y1; row < drect->y2; row++) {
2539 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2540 /* Well, that stuff works, but it's very slow.
2541 * find a better way instead
2543 UINT col;
2545 for(col = drect->x1; col < drect->x2; col++) {
2546 glCopyTexSubImage2D(This->glDescription.target,
2547 This->glDescription.level,
2548 drect->x1 + col, row, /* xoffset, yoffset */
2549 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2550 1, 1);
2552 } else {
2553 glCopyTexSubImage2D(This->glDescription.target,
2554 This->glDescription.level,
2555 drect->x1, row, /* xoffset, yoffset */
2556 srect->x1, yoffset - (int) (row * yrel),
2557 drect->x2-drect->x1, 1);
2562 vcheckGLcall("glCopyTexSubImage2D");
2563 LEAVE_GL();
2566 /* Uses the hardware to stretch and flip the image */
2567 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2568 GLuint src, backup = 0;
2569 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2570 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2571 float left, right, top, bottom; /* Texture coordinates */
2572 UINT fbwidth = Src->currentDesc.Width;
2573 UINT fbheight = Src->currentDesc.Height;
2574 GLenum drawBuffer = GL_BACK;
2576 TRACE("Using hwstretch blit\n");
2577 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2578 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2579 ENTER_GL();
2580 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2582 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2583 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2585 if(GL_LIMITS(aux_buffers) >= 2) {
2586 /* Got more than one aux buffer? Use the 2nd aux buffer */
2587 drawBuffer = GL_AUX1;
2588 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2589 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2590 drawBuffer = GL_AUX0;
2593 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2594 glGenTextures(1, &backup);
2595 checkGLcall("glGenTextures\n");
2596 glBindTexture(GL_TEXTURE_2D, backup);
2597 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2598 } else {
2599 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2600 * we are reading from the back buffer, the backup can be used as source texture
2602 if(Src->glDescription.textureName == 0) {
2603 /* Get it a description */
2604 IWineD3DSurface_PreLoad(SrcSurface);
2606 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
2607 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2609 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2610 Src->Flags &= ~SFLAG_INTEXTURE;
2613 glReadBuffer(GL_BACK);
2614 checkGLcall("glReadBuffer(GL_BACK)");
2616 /* TODO: Only back up the part that will be overwritten */
2617 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2618 0, 0 /* read offsets */,
2619 0, 0,
2620 fbwidth,
2621 fbheight);
2623 checkGLcall("glCopyTexSubImage2D");
2625 /* No issue with overriding these - the sampler is dirty due to blit usage */
2626 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
2627 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2628 checkGLcall("glTexParameteri");
2629 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
2630 minMipLookup[Filter][WINED3DTEXF_NONE]);
2631 checkGLcall("glTexParameteri");
2633 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2634 src = backup ? backup : Src->glDescription.textureName;
2635 } else {
2636 glReadBuffer(GL_FRONT);
2637 checkGLcall("glReadBuffer(GL_FRONT)");
2639 glGenTextures(1, &src);
2640 checkGLcall("glGenTextures(1, &src)");
2641 glBindTexture(GL_TEXTURE_2D, src);
2642 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2644 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2645 * out for power of 2 sizes
2647 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2648 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2649 checkGLcall("glTexImage2D");
2650 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2651 0, 0 /* read offsets */,
2652 0, 0,
2653 fbwidth,
2654 fbheight);
2656 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2657 checkGLcall("glTexParameteri");
2658 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2659 checkGLcall("glTexParameteri");
2661 glReadBuffer(GL_BACK);
2662 checkGLcall("glReadBuffer(GL_BACK)");
2664 checkGLcall("glEnd and previous");
2666 left = (float) srect->x1 / (float) Src->pow2Width;
2667 right = (float) srect->x2 / (float) Src->pow2Width;
2669 if(upsidedown) {
2670 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2671 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2672 } else {
2673 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2674 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2677 /* draw the source texture stretched and upside down. The correct surface is bound already */
2678 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
2679 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
2681 glDrawBuffer(drawBuffer);
2682 glReadBuffer(drawBuffer);
2684 glBegin(GL_QUADS);
2685 /* bottom left */
2686 glTexCoord2f(left, bottom);
2687 glVertex2i(0, fbheight);
2689 /* top left */
2690 glTexCoord2f(left, top);
2691 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2693 /* top right */
2694 glTexCoord2f(right, top);
2695 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2697 /* bottom right */
2698 glTexCoord2f(right, bottom);
2699 glVertex2i(drect->x2 - drect->x1, fbheight);
2700 glEnd();
2701 checkGLcall("glEnd and previous");
2703 /* Now read the stretched and upside down image into the destination texture */
2704 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2705 checkGLcall("glBindTexture");
2706 glCopyTexSubImage2D(This->glDescription.target,
2708 drect->x1, drect->y1, /* xoffset, yoffset */
2709 0, 0, /* We blitted the image to the origin */
2710 drect->x2 - drect->x1, drect->y2 - drect->y1);
2711 checkGLcall("glCopyTexSubImage2D");
2713 /* Write the back buffer backup back */
2714 glBindTexture(GL_TEXTURE_2D, backup ? backup : Src->glDescription.textureName);
2715 checkGLcall("glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName)");
2717 if(drawBuffer == GL_BACK) {
2718 glBegin(GL_QUADS);
2719 /* top left */
2720 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2721 glVertex2i(0, 0);
2723 /* bottom left */
2724 glTexCoord2f(0.0, 0.0);
2725 glVertex2i(0, fbheight);
2727 /* bottom right */
2728 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2729 glVertex2i(fbwidth, Src->currentDesc.Height);
2731 /* top right */
2732 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2733 glVertex2i(fbwidth, 0);
2734 glEnd();
2735 } else {
2736 /* Restore the old draw buffer */
2737 glDrawBuffer(GL_BACK);
2740 /* Cleanup */
2741 if(src != Src->glDescription.textureName && src != backup) {
2742 glDeleteTextures(1, &src);
2743 checkGLcall("glDeleteTextures(1, &src)");
2745 if(backup) {
2746 glDeleteTextures(1, &backup);
2747 checkGLcall("glDeleteTextures(1, &backup)");
2749 LEAVE_GL();
2752 /* Not called from the VTable */
2753 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2754 WINED3DRECT rect;
2755 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2756 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2757 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2759 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2761 /* Get the swapchain. One of the surfaces has to be a primary surface */
2762 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2763 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2764 if(Src) {
2765 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2766 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2769 /* Early sort out of cases where no render target is used */
2770 if(!dstSwapchain && !srcSwapchain &&
2771 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2772 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2773 return WINED3DERR_INVALIDCALL;
2776 /* No destination color keying supported */
2777 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
2778 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2779 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2780 return WINED3DERR_INVALIDCALL;
2783 if (DestRect) {
2784 rect.x1 = DestRect->left;
2785 rect.y1 = DestRect->top;
2786 rect.x2 = DestRect->right;
2787 rect.y2 = DestRect->bottom;
2788 } else {
2789 rect.x1 = 0;
2790 rect.y1 = 0;
2791 rect.x2 = This->currentDesc.Width;
2792 rect.y2 = This->currentDesc.Height;
2795 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2796 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
2797 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
2798 /* Half-life does a Blt from the back buffer to the front buffer,
2799 * Full surface size, no flags... Use present instead
2801 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
2804 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2805 while(1)
2807 RECT mySrcRect;
2808 TRACE("Looking if a Present can be done...\n");
2809 /* Source Rectangle must be full surface */
2810 if( SrcRect ) {
2811 if(SrcRect->left != 0 || SrcRect->top != 0 ||
2812 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
2813 TRACE("No, Source rectangle doesn't match\n");
2814 break;
2817 mySrcRect.left = 0;
2818 mySrcRect.top = 0;
2819 mySrcRect.right = Src->currentDesc.Width;
2820 mySrcRect.bottom = Src->currentDesc.Height;
2822 /* No stretching may occur */
2823 if(mySrcRect.right != rect.x2 - rect.x1 ||
2824 mySrcRect.bottom != rect.y2 - rect.y1) {
2825 TRACE("No, stretching is done\n");
2826 break;
2829 /* Destination must be full surface or match the clipping rectangle */
2830 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
2832 RECT cliprect;
2833 POINT pos[2];
2834 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
2835 pos[0].x = rect.x1;
2836 pos[0].y = rect.y1;
2837 pos[1].x = rect.x2;
2838 pos[1].y = rect.y2;
2839 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
2840 pos, 2);
2842 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
2843 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
2845 TRACE("No, dest rectangle doesn't match(clipper)\n");
2846 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
2847 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
2848 break;
2851 else
2853 if(rect.x1 != 0 || rect.y1 != 0 ||
2854 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
2855 TRACE("No, dest rectangle doesn't match(surface size)\n");
2856 break;
2860 TRACE("Yes\n");
2862 /* These flags are unimportant for the flag check, remove them */
2863 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
2864 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
2866 /* The idea behind this is that a glReadPixels and a glDrawPixels call
2867 * take very long, while a flip is fast.
2868 * This applies to Half-Life, which does such Blts every time it finished
2869 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
2870 * menu. This is also used by all apps when they do windowed rendering
2872 * The problem is that flipping is not really the same as copying. After a
2873 * Blt the front buffer is a copy of the back buffer, and the back buffer is
2874 * untouched. Therefore it's necessary to override the swap effect
2875 * and to set it back after the flip.
2877 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
2878 * testcases.
2881 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
2882 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2884 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
2885 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
2887 dstSwapchain->presentParms.SwapEffect = orig_swap;
2889 return WINED3D_OK;
2891 break;
2894 TRACE("Unsupported blit between buffers on the same swapchain\n");
2895 return WINED3DERR_INVALIDCALL;
2896 } else if((dstSwapchain || This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) &&
2897 (srcSwapchain || SrcSurface == myDevice->render_targets[0]) ) {
2898 ERR("Can't perform hardware blit between 2 different swapchains, falling back to software\n");
2899 return WINED3DERR_INVALIDCALL;
2902 if(srcSwapchain || SrcSurface == myDevice->render_targets[0]) {
2903 /* Blit from render target to texture */
2904 WINED3DRECT srect;
2905 BOOL upsideDown, stretchx;
2907 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
2908 TRACE("Color keying not supported by frame buffer to texture blit\n");
2909 return WINED3DERR_INVALIDCALL;
2910 /* Destination color key is checked above */
2913 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2914 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2916 if(SrcRect) {
2917 if(SrcRect->top < SrcRect->bottom) {
2918 srect.y1 = SrcRect->top;
2919 srect.y2 = SrcRect->bottom;
2920 upsideDown = FALSE;
2921 } else {
2922 srect.y1 = SrcRect->bottom;
2923 srect.y2 = SrcRect->top;
2924 upsideDown = TRUE;
2926 srect.x1 = SrcRect->left;
2927 srect.x2 = SrcRect->right;
2928 } else {
2929 srect.x1 = 0;
2930 srect.y1 = 0;
2931 srect.x2 = Src->currentDesc.Width;
2932 srect.y2 = Src->currentDesc.Height;
2933 upsideDown = FALSE;
2935 if(rect.x1 > rect.x2) {
2936 UINT tmp = rect.x2;
2937 rect.x2 = rect.x1;
2938 rect.x1 = tmp;
2939 upsideDown = !upsideDown;
2941 if(!srcSwapchain) {
2942 TRACE("Reading from an offscreen target\n");
2943 upsideDown = !upsideDown;
2946 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
2947 stretchx = TRUE;
2948 } else {
2949 stretchx = FALSE;
2952 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2953 * flip the image nor scale it.
2955 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2956 * -> If the app wants a image width an unscaled width, copy it line per line
2957 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
2958 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2959 * back buffer. This is slower than reading line per line, thus not used for flipping
2960 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2961 * pixel by pixel
2963 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
2964 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
2965 * backends.
2967 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
2968 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
2969 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
2970 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
2971 rect.y2 - rect.y1 > Src->currentDesc.Height) {
2972 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
2973 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
2974 } else {
2975 TRACE("Using hardware stretching to flip / stretch the texture\n");
2976 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
2979 if(!(This->Flags & SFLAG_DONOTFREE)) {
2980 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
2981 This->resource.allocatedMemory = NULL;
2982 } else {
2983 This->Flags &= ~SFLAG_INSYSMEM;
2985 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
2986 * path is never entered
2988 This->Flags |= SFLAG_INTEXTURE;
2990 return WINED3D_OK;
2991 } else if(Src) {
2992 /* Blit from offscreen surface to render target */
2993 float glTexCoord[4];
2994 DWORD oldCKeyFlags = Src->CKeyFlags;
2995 WINEDDCOLORKEY oldBltCKey = This->SrcBltCKey;
2996 RECT SourceRectangle;
2998 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3000 if(SrcRect) {
3001 SourceRectangle.left = SrcRect->left;
3002 SourceRectangle.right = SrcRect->right;
3003 SourceRectangle.top = SrcRect->top;
3004 SourceRectangle.bottom = SrcRect->bottom;
3005 } else {
3006 SourceRectangle.left = 0;
3007 SourceRectangle.right = Src->currentDesc.Width;
3008 SourceRectangle.top = 0;
3009 SourceRectangle.bottom = Src->currentDesc.Height;
3012 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3013 /* Fall back to software */
3014 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3015 SourceRectangle.left, SourceRectangle.top,
3016 SourceRectangle.right, SourceRectangle.bottom);
3017 return WINED3DERR_INVALIDCALL;
3020 /* Color keying: Check if we have to do a color keyed blt,
3021 * and if not check if a color key is activated.
3023 * Just modify the color keying parameters in the surface and restore them afterwards
3024 * The surface keeps track of the color key last used to load the opengl surface.
3025 * PreLoad will catch the change to the flags and color key and reload if necessary.
3027 if(Flags & WINEDDBLT_KEYSRC) {
3028 /* Use color key from surface */
3029 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3030 /* Use color key from DDBltFx */
3031 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3032 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3033 } else {
3034 /* Do not use color key */
3035 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3038 /* Now load the surface */
3039 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3042 /* Activate the destination context, set it up for blitting */
3043 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3044 ENTER_GL();
3046 if(!dstSwapchain) {
3047 TRACE("Drawing to offscreen buffer\n");
3048 glDrawBuffer(myDevice->offscreenBuffer);
3049 checkGLcall("glDrawBuffer");
3050 } else {
3051 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
3052 TRACE("Drawing to %#x buffer\n", buffer);
3053 glDrawBuffer(buffer);
3054 checkGLcall("glDrawBuffer");
3057 /* Bind the texture */
3058 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
3059 checkGLcall("glBindTexture");
3061 /* Filtering for StretchRect */
3062 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
3063 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
3064 checkGLcall("glTexParameteri");
3065 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
3066 minMipLookup[Filter][WINED3DTEXF_NONE]);
3067 checkGLcall("glTexParameteri");
3068 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
3069 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
3070 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3071 checkGLcall("glTexEnvi");
3073 /* This is for color keying */
3074 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3075 glEnable(GL_ALPHA_TEST);
3076 checkGLcall("glEnable GL_ALPHA_TEST");
3077 glAlphaFunc(GL_NOTEQUAL, 0.0);
3078 checkGLcall("glAlphaFunc\n");
3079 } else {
3080 glDisable(GL_ALPHA_TEST);
3081 checkGLcall("glDisable GL_ALPHA_TEST");
3084 /* Draw a textured quad
3086 glBegin(GL_QUADS);
3088 glColor3d(1.0f, 1.0f, 1.0f);
3089 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3090 glVertex3f(rect.x1,
3091 rect.y1,
3092 0.0);
3094 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3095 glVertex3f(rect.x1, rect.y2, 0.0);
3097 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3098 glVertex3f(rect.x2,
3099 rect.y2,
3100 0.0);
3102 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3103 glVertex3f(rect.x2,
3104 rect.y1,
3105 0.0);
3106 glEnd();
3107 checkGLcall("glEnd");
3109 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3110 glDisable(GL_ALPHA_TEST);
3111 checkGLcall("glDisable(GL_ALPHA_TEST)");
3114 /* Unbind the texture */
3115 glBindTexture(GL_TEXTURE_2D, 0);
3116 checkGLcall("glEnable glBindTexture");
3118 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3119 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3121 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3122 glDrawBuffer(GL_BACK);
3123 checkGLcall("glDrawBuffer");
3125 /* Restore the color key parameters */
3126 Src->CKeyFlags = oldCKeyFlags;
3127 This->SrcBltCKey = oldBltCKey;
3129 LEAVE_GL();
3131 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3132 This->Flags &= ~SFLAG_INSYSMEM;
3133 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3134 * is outdated now
3136 if(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO) {
3137 This->Flags |= SFLAG_INDRAWABLE;
3138 This->Flags &= ~SFLAG_INTEXTURE;
3139 } else {
3140 This->Flags |= SFLAG_INTEXTURE;
3143 return WINED3D_OK;
3144 } else {
3145 /* Source-Less Blit to render target */
3146 if (Flags & WINEDDBLT_COLORFILL) {
3147 /* This is easy to handle for the D3D Device... */
3148 DWORD color;
3150 TRACE("Colorfill\n");
3152 /* The color as given in the Blt function is in the format of the frame-buffer...
3153 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3155 if (This->resource.format == WINED3DFMT_P8) {
3156 if (This->palette) {
3157 color = ((0xFF000000) |
3158 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3159 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3160 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3161 } else {
3162 color = 0xFF000000;
3165 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3166 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3167 color = 0xFFFFFFFF;
3168 } else {
3169 color = ((0xFF000000) |
3170 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3171 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3172 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3175 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3176 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3177 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3179 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3180 color = DDBltFx->u5.dwFillColor;
3182 else {
3183 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3184 return WINED3DERR_INVALIDCALL;
3187 TRACE("Calling GetSwapChain with mydevice = %p\n", myDevice);
3188 if(dstSwapchain && dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]) {
3189 glDrawBuffer(GL_BACK);
3190 checkGLcall("glDrawBuffer(GL_BACK)");
3191 } else if (dstSwapchain && This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer) {
3192 glDrawBuffer(GL_FRONT);
3193 checkGLcall("glDrawBuffer(GL_FRONT)");
3194 } else if(This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3195 glDrawBuffer(myDevice->offscreenBuffer);
3196 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer3)");
3197 } else {
3198 TRACE("Surface is higher back buffer, falling back to software\n");
3199 return WINED3DERR_INVALIDCALL;
3202 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3204 IWineD3DDevice_Clear( (IWineD3DDevice *) myDevice,
3205 1 /* Number of rectangles */,
3206 &rect,
3207 WINED3DCLEAR_TARGET,
3208 color,
3209 0.0 /* Z */,
3210 0 /* Stencil */);
3212 /* Restore the original draw buffer */
3213 if(!dstSwapchain) {
3214 glDrawBuffer(myDevice->offscreenBuffer);
3215 } else if(dstSwapchain->backBuffer && dstSwapchain->backBuffer[0]) {
3216 glDrawBuffer(GL_BACK);
3218 vcheckGLcall("glDrawBuffer");
3220 return WINED3D_OK;
3224 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3225 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3226 return WINED3DERR_INVALIDCALL;
3229 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3231 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3233 if (Flags & WINEDDBLT_DEPTHFILL)
3234 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3235 DestRect == NULL ? 0 : 1,
3236 (WINED3DRECT *) DestRect,
3237 WINED3DCLEAR_ZBUFFER,
3238 0x00000000,
3239 (float) DDBltFx->u5.dwFillDepth / (float) MAXDWORD,
3240 0x00000000);
3242 FIXME("(%p): Unsupp depthstencil blit\n", This);
3243 return WINED3DERR_INVALIDCALL;
3246 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3247 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3248 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3249 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3250 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3251 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3253 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3254 * except depth blits, which seem to work
3256 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3257 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3258 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3259 return WINED3DERR_INVALIDCALL;
3260 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3261 TRACE("Z Blit override handled the blit\n");
3262 return WINED3D_OK;
3266 /* Special cases for RenderTargets */
3267 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3268 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3269 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3272 /* For the rest call the X11 surface implementation.
3273 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3274 * other Blts are rather rare
3276 return IWineGDISurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3279 HRESULT WINAPI IWineD3DSurfaceImpl_GetBltStatus(IWineD3DSurface *iface, DWORD Flags) {
3280 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3281 TRACE("(%p)->(%x)\n", This, Flags);
3283 switch (Flags)
3285 case WINEDDGBS_CANBLT:
3286 case WINEDDGBS_ISBLTDONE:
3287 return WINED3D_OK;
3289 default:
3290 return WINED3DERR_INVALIDCALL;
3294 HRESULT WINAPI IWineD3DSurfaceImpl_GetFlipStatus(IWineD3DSurface *iface, DWORD Flags) {
3295 /* XXX: DDERR_INVALIDSURFACETYPE */
3297 TRACE("(%p)->(%08x)\n",iface,Flags);
3298 switch (Flags) {
3299 case WINEDDGFS_CANFLIP:
3300 case WINEDDGFS_ISFLIPDONE:
3301 return WINED3D_OK;
3303 default:
3304 return WINED3DERR_INVALIDCALL;
3308 HRESULT WINAPI IWineD3DSurfaceImpl_IsLost(IWineD3DSurface *iface) {
3309 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3310 TRACE("(%p)\n", This);
3312 /* D3D8 and 9 loose full devices, ddraw only surfaces */
3313 return This->Flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
3316 HRESULT WINAPI IWineD3DSurfaceImpl_Restore(IWineD3DSurface *iface) {
3317 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3318 TRACE("(%p)\n", This);
3320 /* So far we don't lose anything :) */
3321 This->Flags &= ~SFLAG_LOST;
3322 return WINED3D_OK;
3325 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3326 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3327 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3328 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3329 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3331 if(myDevice->inScene &&
3332 (iface == myDevice->stencilBufferTarget ||
3333 (Source && Source == myDevice->stencilBufferTarget))) {
3334 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3335 return WINED3DERR_INVALIDCALL;
3338 /* Special cases for RenderTargets */
3339 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3340 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3342 RECT SrcRect, DstRect;
3343 DWORD Flags=0;
3345 if(rsrc) {
3346 SrcRect.left = rsrc->left;
3347 SrcRect.top= rsrc->top;
3348 SrcRect.bottom = rsrc->bottom;
3349 SrcRect.right = rsrc->right;
3350 } else {
3351 SrcRect.left = 0;
3352 SrcRect.top = 0;
3353 SrcRect.right = srcImpl->currentDesc.Width;
3354 SrcRect.bottom = srcImpl->currentDesc.Height;
3357 DstRect.left = dstx;
3358 DstRect.top=dsty;
3359 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3360 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3362 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3363 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3364 Flags |= WINEDDBLT_KEYSRC;
3365 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3366 Flags |= WINEDDBLT_KEYDEST;
3367 if(trans & WINEDDBLTFAST_WAIT)
3368 Flags |= WINEDDBLT_WAIT;
3369 if(trans & WINEDDBLTFAST_DONOTWAIT)
3370 Flags |= WINEDDBLT_DONOTWAIT;
3372 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3376 return IWineGDISurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3379 HRESULT WINAPI IWineD3DSurfaceImpl_GetPalette(IWineD3DSurface *iface, IWineD3DPalette **Pal) {
3380 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3381 TRACE("(%p)->(%p)\n", This, Pal);
3383 *Pal = (IWineD3DPalette *) This->palette;
3384 return WINED3D_OK;
3387 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
3388 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3389 RGBQUAD col[256];
3390 IWineD3DPaletteImpl *pal = This->palette;
3391 unsigned int n;
3392 TRACE("(%p)\n", This);
3394 if(This->resource.format == WINED3DFMT_P8 ||
3395 This->resource.format == WINED3DFMT_A8P8)
3397 if(!This->Flags & SFLAG_INSYSMEM) {
3398 FIXME("Palette changed with surface that does not have an up to date system memory copy\n");
3400 TRACE("Dirtifying surface\n");
3401 This->Flags &= ~(SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3404 if(This->Flags & SFLAG_DIBSECTION) {
3405 TRACE("(%p): Updating the hdc's palette\n", This);
3406 for (n=0; n<256; n++) {
3407 if(pal) {
3408 col[n].rgbRed = pal->palents[n].peRed;
3409 col[n].rgbGreen = pal->palents[n].peGreen;
3410 col[n].rgbBlue = pal->palents[n].peBlue;
3411 } else {
3412 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3413 /* Use the default device palette */
3414 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
3415 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
3416 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
3418 col[n].rgbReserved = 0;
3420 SetDIBColorTable(This->hDC, 0, 256, col);
3423 return WINED3D_OK;
3426 HRESULT WINAPI IWineD3DSurfaceImpl_SetPalette(IWineD3DSurface *iface, IWineD3DPalette *Pal) {
3427 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3428 IWineD3DPaletteImpl *PalImpl = (IWineD3DPaletteImpl *) Pal;
3429 TRACE("(%p)->(%p)\n", This, Pal);
3431 if(This->palette != NULL)
3432 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3433 This->palette->Flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
3435 if(PalImpl != NULL) {
3436 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3437 /* Set the device's main palette if the palette
3438 * wasn't a primary palette before
3440 if(!(PalImpl->Flags & WINEDDPCAPS_PRIMARYSURFACE)) {
3441 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3442 unsigned int i;
3444 for(i=0; i < 256; i++) {
3445 device->palettes[device->currentPalette][i] = PalImpl->palents[i];
3449 (PalImpl)->Flags |= WINEDDPCAPS_PRIMARYSURFACE;
3452 This->palette = PalImpl;
3454 return IWineD3DSurface_RealizePalette(iface);
3457 HRESULT WINAPI IWineD3DSurfaceImpl_SetColorKey(IWineD3DSurface *iface, DWORD Flags, WINEDDCOLORKEY *CKey) {
3458 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3459 TRACE("(%p)->(%08x,%p)\n", This, Flags, CKey);
3461 if ((Flags & WINEDDCKEY_COLORSPACE) != 0) {
3462 FIXME(" colorkey value not supported (%08x) !\n", Flags);
3463 return WINED3DERR_INVALIDCALL;
3466 /* Dirtify the surface, but only if a key was changed */
3467 if(CKey) {
3468 switch (Flags & ~WINEDDCKEY_COLORSPACE) {
3469 case WINEDDCKEY_DESTBLT:
3470 This->DestBltCKey = *CKey;
3471 This->CKeyFlags |= WINEDDSD_CKDESTBLT;
3472 break;
3474 case WINEDDCKEY_DESTOVERLAY:
3475 This->DestOverlayCKey = *CKey;
3476 This->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3477 break;
3479 case WINEDDCKEY_SRCOVERLAY:
3480 This->SrcOverlayCKey = *CKey;
3481 This->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3482 break;
3484 case WINEDDCKEY_SRCBLT:
3485 This->SrcBltCKey = *CKey;
3486 This->CKeyFlags |= WINEDDSD_CKSRCBLT;
3487 break;
3490 else {
3491 switch (Flags & ~WINEDDCKEY_COLORSPACE) {
3492 case WINEDDCKEY_DESTBLT:
3493 This->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3494 break;
3496 case WINEDDCKEY_DESTOVERLAY:
3497 This->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3498 break;
3500 case WINEDDCKEY_SRCOVERLAY:
3501 This->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3502 break;
3504 case WINEDDCKEY_SRCBLT:
3505 This->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3506 break;
3510 return WINED3D_OK;
3513 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3514 /** Check against the maximum texture sizes supported by the video card **/
3515 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3516 unsigned int pow2Width, pow2Height;
3517 const GlPixelFormatDesc *glDesc;
3519 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3520 /* Setup some glformat defaults */
3521 This->glDescription.glFormat = glDesc->glFormat;
3522 This->glDescription.glFormatInternal = glDesc->glInternal;
3523 This->glDescription.glType = glDesc->glType;
3525 This->glDescription.textureName = 0;
3526 This->glDescription.target = GL_TEXTURE_2D;
3528 /* Non-power2 support */
3529 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO)) {
3530 pow2Width = This->currentDesc.Width;
3531 pow2Height = This->currentDesc.Height;
3532 } else {
3533 /* Find the nearest pow2 match */
3534 pow2Width = pow2Height = 1;
3535 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3536 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3538 This->pow2Width = pow2Width;
3539 This->pow2Height = pow2Height;
3541 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3542 WINED3DFORMAT Format = This->resource.format;
3543 /** TODO: add support for non power two compressed textures **/
3544 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3545 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5) {
3546 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3547 This, This->currentDesc.Width, This->currentDesc.Height);
3548 return WINED3DERR_NOTAVAILABLE;
3552 if(pow2Width != This->currentDesc.Width ||
3553 pow2Height != This->currentDesc.Height) {
3554 This->Flags |= SFLAG_NONPOW2;
3557 TRACE("%p\n", This);
3558 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3559 /* one of three options
3560 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)
3561 2: Set the texture to the maximum size (bad idea)
3562 3: WARN and return WINED3DERR_NOTAVAILABLE;
3563 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.
3565 WARN("(%p) Creating an oversized surface\n", This);
3566 This->Flags |= SFLAG_OVERSIZE;
3568 /* This will be initialized on the first blt */
3569 This->glRect.left = 0;
3570 This->glRect.top = 0;
3571 This->glRect.right = 0;
3572 This->glRect.bottom = 0;
3573 } else {
3574 /* No oversize, gl rect is the full texture size */
3575 This->Flags &= ~SFLAG_OVERSIZE;
3576 This->glRect.left = 0;
3577 This->glRect.top = 0;
3578 This->glRect.right = This->pow2Width;
3579 This->glRect.bottom = This->pow2Height;
3582 if(This->resource.allocatedMemory == NULL) {
3583 /* Make sure memory exists from the start, and it is initialized properly. D3D initializes surfaces,
3584 * gl does not, so we need to upload zeroes to init the gl texture.
3586 This->resource.allocatedMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->resource.size + 4);
3588 This->Flags |= SFLAG_INSYSMEM;
3590 return WINED3D_OK;
3593 DWORD WINAPI IWineD3DSurfaceImpl_GetPitch(IWineD3DSurface *iface) {
3594 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3595 DWORD ret;
3596 TRACE("(%p)\n", This);
3598 /* DXTn formats don't have exact pitches as they are to the new row of blocks,
3599 where each block is 4x4 pixels, 8 bytes (dxt1) and 16 bytes (dxt2/3/4/5)
3600 ie pitch = (width/4) * bytes per block */
3601 if (This->resource.format == WINED3DFMT_DXT1) /* DXT1 is 8 bytes per block */
3602 ret = ((This->currentDesc.Width + 3) >> 2) << 3;
3603 else if (This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
3604 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) /* DXT2/3/4/5 is 16 bytes per block */
3605 ret = ((This->currentDesc.Width + 3) >> 2) << 4;
3606 else {
3607 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
3608 ret = This->bytesPerPixel * This->currentDesc.Width; /* Bytes / row */
3609 ret = (ret + alignment - 1) & ~(alignment - 1);
3611 TRACE("(%p) Returning %d\n", This, ret);
3612 return ret;
3615 HRESULT WINAPI IWineD3DSurfaceImpl_SetOverlayPosition(IWineD3DSurface *iface, LONG X, LONG Y) {
3616 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3618 FIXME("(%p)->(%d,%d) Stub!\n", This, X, Y);
3620 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3622 TRACE("(%p): Not an overlay surface\n", This);
3623 return WINEDDERR_NOTAOVERLAYSURFACE;
3626 return WINED3D_OK;
3629 HRESULT WINAPI IWineD3DSurfaceImpl_GetOverlayPosition(IWineD3DSurface *iface, LONG *X, LONG *Y) {
3630 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3632 FIXME("(%p)->(%p,%p) Stub!\n", This, X, Y);
3634 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3636 TRACE("(%p): Not an overlay surface\n", This);
3637 return WINEDDERR_NOTAOVERLAYSURFACE;
3640 return WINED3D_OK;
3643 HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlayZOrder(IWineD3DSurface *iface, DWORD Flags, IWineD3DSurface *Ref) {
3644 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3645 IWineD3DSurfaceImpl *RefImpl = (IWineD3DSurfaceImpl *) Ref;
3647 FIXME("(%p)->(%08x,%p) Stub!\n", This, Flags, RefImpl);
3649 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3651 TRACE("(%p): Not an overlay surface\n", This);
3652 return WINEDDERR_NOTAOVERLAYSURFACE;
3655 return WINED3D_OK;
3658 HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlay(IWineD3DSurface *iface, RECT *SrcRect, IWineD3DSurface *DstSurface, RECT *DstRect, DWORD Flags, WINEDDOVERLAYFX *FX) {
3659 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3660 IWineD3DSurfaceImpl *Dst = (IWineD3DSurfaceImpl *) DstSurface;
3661 FIXME("(%p)->(%p, %p, %p, %08x, %p)\n", This, SrcRect, Dst, DstRect, Flags, FX);
3663 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3665 TRACE("(%p): Not an overlay surface\n", This);
3666 return WINEDDERR_NOTAOVERLAYSURFACE;
3669 return WINED3D_OK;
3672 HRESULT WINAPI IWineD3DSurfaceImpl_SetClipper(IWineD3DSurface *iface, IWineD3DClipper *clipper)
3674 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3675 TRACE("(%p)->(%p)\n", This, clipper);
3677 This->clipper = clipper;
3678 return WINED3D_OK;
3681 HRESULT WINAPI IWineD3DSurfaceImpl_GetClipper(IWineD3DSurface *iface, IWineD3DClipper **clipper)
3683 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3684 TRACE("(%p)->(%p)\n", This, clipper);
3686 *clipper = This->clipper;
3687 if(*clipper) {
3688 IWineD3DClipper_AddRef(*clipper);
3690 return WINED3D_OK;
3693 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
3695 /* IUnknown */
3696 IWineD3DSurfaceImpl_QueryInterface,
3697 IWineD3DSurfaceImpl_AddRef,
3698 IWineD3DSurfaceImpl_Release,
3699 /* IWineD3DResource */
3700 IWineD3DSurfaceImpl_GetParent,
3701 IWineD3DSurfaceImpl_GetDevice,
3702 IWineD3DSurfaceImpl_SetPrivateData,
3703 IWineD3DSurfaceImpl_GetPrivateData,
3704 IWineD3DSurfaceImpl_FreePrivateData,
3705 IWineD3DSurfaceImpl_SetPriority,
3706 IWineD3DSurfaceImpl_GetPriority,
3707 IWineD3DSurfaceImpl_PreLoad,
3708 IWineD3DSurfaceImpl_GetType,
3709 /* IWineD3DSurface */
3710 IWineD3DSurfaceImpl_GetContainer,
3711 IWineD3DSurfaceImpl_GetDesc,
3712 IWineD3DSurfaceImpl_LockRect,
3713 IWineD3DSurfaceImpl_UnlockRect,
3714 IWineD3DSurfaceImpl_GetDC,
3715 IWineD3DSurfaceImpl_ReleaseDC,
3716 IWineD3DSurfaceImpl_Flip,
3717 IWineD3DSurfaceImpl_Blt,
3718 IWineD3DSurfaceImpl_GetBltStatus,
3719 IWineD3DSurfaceImpl_GetFlipStatus,
3720 IWineD3DSurfaceImpl_IsLost,
3721 IWineD3DSurfaceImpl_Restore,
3722 IWineD3DSurfaceImpl_BltFast,
3723 IWineD3DSurfaceImpl_GetPalette,
3724 IWineD3DSurfaceImpl_SetPalette,
3725 IWineD3DSurfaceImpl_RealizePalette,
3726 IWineD3DSurfaceImpl_SetColorKey,
3727 IWineD3DSurfaceImpl_GetPitch,
3728 IWineD3DSurfaceImpl_SetMem,
3729 IWineD3DSurfaceImpl_SetOverlayPosition,
3730 IWineD3DSurfaceImpl_GetOverlayPosition,
3731 IWineD3DSurfaceImpl_UpdateOverlayZOrder,
3732 IWineD3DSurfaceImpl_UpdateOverlay,
3733 IWineD3DSurfaceImpl_SetClipper,
3734 IWineD3DSurfaceImpl_GetClipper,
3735 /* Internal use: */
3736 IWineD3DSurfaceImpl_AddDirtyRect,
3737 IWineD3DSurfaceImpl_LoadTexture,
3738 IWineD3DSurfaceImpl_SaveSnapshot,
3739 IWineD3DSurfaceImpl_SetContainer,
3740 IWineD3DSurfaceImpl_SetGlTextureDesc,
3741 IWineD3DSurfaceImpl_GetGlDesc,
3742 IWineD3DSurfaceImpl_GetData,
3743 IWineD3DSurfaceImpl_SetFormat,
3744 IWineD3DSurfaceImpl_PrivateSetup