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
29 #include "wine/port.h"
30 #include "wined3d_private.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
33 #define GLINFO_LOCATION ((IWineD3DImpl *)(((IWineD3DDeviceImpl *)This->resource.wineD3DDevice)->wineD3D))->gl_info
55 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf);
57 static void surface_download_data(IWineD3DSurfaceImpl *This) {
58 if (This->resource.format == WINED3DFMT_DXT1 ||
59 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
60 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
61 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
62 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
64 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
65 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
67 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
68 checkGLcall("glGetCompressedTexImageARB()");
75 if(This->Flags & SFLAG_CONVERTED) {
76 FIXME("Read back converted textures unsupported\n");
80 if (This->Flags & SFLAG_NONPOW2) {
81 src_pitch = This->bytesPerPixel * This->pow2Width;
82 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
83 src_pitch = (src_pitch + SURFACE_ALIGNMENT - 1) & ~(SURFACE_ALIGNMENT - 1);
84 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
86 mem = This->resource.allocatedMemory;
89 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
90 This->glDescription.glFormat, This->glDescription.glType, mem);
92 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
93 This->glDescription.glType, mem);
94 checkGLcall("glGetTexImage()");
96 if (This->Flags & SFLAG_NONPOW2) {
97 LPBYTE src_data, dst_data;
100 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
101 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
102 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
104 * We're doing this...
106 * instead of boxing the texture :
107 * |<-texture width ->| -->pow2width| /\
108 * |111111111111111111| | |
109 * |222 Texture 222222| boxed empty | texture height
110 * |3333 Data 33333333| | |
111 * |444444444444444444| | \/
112 * ----------------------------------- |
113 * | boxed empty | boxed empty | pow2height
115 * -----------------------------------
118 * we're repacking the data to the expected texture width
120 * |<-texture width ->| -->pow2width| /\
121 * |111111111111111111222222222222222| |
122 * |222333333333333333333444444444444| texture height
126 * | empty | pow2height
128 * -----------------------------------
132 * |<-texture width ->| /\
133 * |111111111111111111|
134 * |222222222222222222|texture height
135 * |333333333333333333|
136 * |444444444444444444| \/
137 * --------------------
139 * this also means that any references to allocatedMemory should work with the data as if were a
140 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
142 * internally the texture is still stored in a boxed format so any references to textureName will
143 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
145 * Performance should not be an issue, because applications normally do not lock the surfaces when
146 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
147 * and doesn't have to be re-read.
150 dst_data = This->resource.allocatedMemory;
151 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
152 for (y = 1 ; y < This->currentDesc.Height; y++) {
153 /* skip the first row */
154 src_data += src_pitch;
155 dst_data += dst_pitch;
156 memcpy(dst_data, src_data, dst_pitch);
159 HeapFree(GetProcessHeap(), 0, mem);
164 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
165 if (This->resource.format == WINED3DFMT_DXT1 ||
166 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
167 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
168 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
169 FIXME("Using DXT1/3/5 without advertized support\n");
171 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
172 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
173 This->Flags |= SFLAG_CLIENT;
176 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
178 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
179 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
180 * function uses glCompressedTexImage2D instead of the SubImage call
182 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, This->glDescription.glFormatInternal,
183 width, height, 0 /* border */, This->resource.size, data));
184 checkGLcall("glCompressedTexSubImage2D");
188 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
190 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
191 checkGLcall("glTexSubImage2D");
196 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
197 BOOL enable_client_storage = FALSE;
199 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,
200 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
202 if (This->resource.format == WINED3DFMT_DXT1 ||
203 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
204 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
205 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
206 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
212 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
213 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
214 /* In some cases we want to disable client storage.
215 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
216 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
217 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
218 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
219 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
221 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
222 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
223 This->Flags &= SFLAG_CLIENT;
224 enable_client_storage = TRUE;
226 This->Flags |= SFLAG_CLIENT;
227 /* Below point opengl to our allocated texture memory */
230 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type,
231 This->Flags & SFLAG_CLIENT ? This->resource.allocatedMemory : NULL);
232 checkGLcall("glTexImage2D");
234 if(enable_client_storage) {
235 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
236 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
241 /* In D3D the depth stencil dimensions have to be greater than or equal to the
242 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
243 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
244 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
245 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
246 renderbuffer_entry_t *entry;
247 GLuint renderbuffer = 0;
248 unsigned int src_width, src_height;
250 src_width = This->pow2Width;
251 src_height = This->pow2Height;
253 /* A depth stencil smaller than the render target is not valid */
254 if (width > src_width || height > src_height) return;
256 /* Remove any renderbuffer set if the sizes match */
257 if (width == src_width && height == src_height) {
258 This->current_renderbuffer = NULL;
262 /* Look if we've already got a renderbuffer of the correct dimensions */
263 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
264 if (entry->width == width && entry->height == height) {
265 renderbuffer = entry->id;
266 This->current_renderbuffer = entry;
272 const PixelFormatDesc *format_entry = getFormatDescEntry(This->resource.format);
274 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
275 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
276 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, format_entry->glFormat, width, height));
278 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
279 entry->width = width;
280 entry->height = height;
281 entry->id = renderbuffer;
282 list_add_head(&This->renderbuffers, &entry->entry);
284 This->current_renderbuffer = entry;
287 checkGLcall("set_compatible_renderbuffer");
290 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
291 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
292 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
294 TRACE("(%p) : swapchain %p\n", This, swapchain);
296 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
297 TRACE("Returning GL_BACK\n");
299 } else if (swapchain_impl->frontBuffer == iface) {
300 TRACE("Returning GL_FRONT\n");
304 FIXME("Higher back buffer, returning GL_BACK\n");
308 /* *******************************************
309 IWineD3DSurface IUnknown parts follow
310 ******************************************* */
311 HRESULT WINAPI IWineD3DSurfaceImpl_QueryInterface(IWineD3DSurface *iface, REFIID riid, LPVOID *ppobj)
313 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
314 /* Warn ,but be nice about things */
315 TRACE("(%p)->(%s,%p)\n", This,debugstr_guid(riid),ppobj);
317 if (IsEqualGUID(riid, &IID_IUnknown)
318 || IsEqualGUID(riid, &IID_IWineD3DBase)
319 || IsEqualGUID(riid, &IID_IWineD3DResource)
320 || IsEqualGUID(riid, &IID_IWineD3DSurface)) {
321 IUnknown_AddRef((IUnknown*)iface);
326 return E_NOINTERFACE;
329 ULONG WINAPI IWineD3DSurfaceImpl_AddRef(IWineD3DSurface *iface) {
330 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
331 ULONG ref = InterlockedIncrement(&This->resource.ref);
332 TRACE("(%p) : AddRef increasing from %d\n", This,ref - 1);
336 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
337 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
338 ULONG ref = InterlockedDecrement(&This->resource.ref);
339 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
341 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) This->resource.wineD3DDevice;
342 renderbuffer_entry_t *entry, *entry2;
343 TRACE("(%p) : cleaning up\n", This);
345 if(iface == device->lastActiveRenderTarget) {
346 IWineD3DSwapChainImpl *swapchain = device->swapchains ? (IWineD3DSwapChainImpl *) device->swapchains[0] : NULL;
348 TRACE("Last active render target destroyed\n");
349 /* Find a replacement surface for the currently active back buffer. The context manager does not do NULL
350 * checks, so switch to a valid target as long as the currently set surface is still valid. Use the
351 * surface of the implicit swpchain. If that is the same as the destroyed surface the device is destroyed
352 * and the lastActiveRenderTarget member shouldn't matter
355 ENTER_GL(); /* For ActivateContext */
356 if(swapchain->backBuffer && swapchain->backBuffer[0] != iface) {
357 TRACE("Activating primary back buffer\n");
358 ActivateContext(device, swapchain->backBuffer[0], CTXUSAGE_RESOURCELOAD);
359 } else if(!swapchain->backBuffer && swapchain->frontBuffer != iface) {
360 /* Single buffering environment */
361 TRACE("Activating primary front buffer\n");
362 ActivateContext(device, swapchain->frontBuffer, CTXUSAGE_RESOURCELOAD);
364 TRACE("Device is being destroyed, setting lastActiveRenderTarget = 0xdeadbabe\n");
365 /* Implicit render target destroyed, that means the device is being destroyed
366 * whatever we set here, it shouldn't matter
368 device->lastActiveRenderTarget = (IWineD3DSurface *) 0xdeadbabe;
372 /* May happen during ddraw uninitialization */
373 TRACE("Render target set, but swapchain does not exist!\n");
374 device->lastActiveRenderTarget = (IWineD3DSurface *) 0xdeadcafe;
378 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
381 /* Need a context to destroy the texture. Use the currently active render target, but only if
382 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
383 * When destroying the primary rt, Uninit3D will activate a context before doing anything
385 if(device->render_targets[0]) {
386 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
389 TRACE("Deleting texture %d\n", This->glDescription.textureName);
390 glDeleteTextures(1, &This->glDescription.textureName);
394 if(This->Flags & SFLAG_DIBSECTION) {
396 SelectObject(This->hDC, This->dib.holdbitmap);
398 /* Release the DIB section */
399 DeleteObject(This->dib.DIBsection);
400 This->dib.bitmap_data = NULL;
401 This->resource.allocatedMemory = NULL;
403 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
405 HeapFree(GetProcessHeap(), 0, This->palette9);
407 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
408 if(iface == device->ddraw_primary)
409 device->ddraw_primary = NULL;
411 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
412 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
413 HeapFree(GetProcessHeap(), 0, entry);
416 TRACE("(%p) Released\n", This);
417 HeapFree(GetProcessHeap(), 0, This);
423 /* ****************************************************
424 IWineD3DSurface IWineD3DResource parts follow
425 **************************************************** */
426 HRESULT WINAPI IWineD3DSurfaceImpl_GetDevice(IWineD3DSurface *iface, IWineD3DDevice** ppDevice) {
427 return IWineD3DResourceImpl_GetDevice((IWineD3DResource *)iface, ppDevice);
430 HRESULT WINAPI IWineD3DSurfaceImpl_SetPrivateData(IWineD3DSurface *iface, REFGUID refguid, CONST void* pData, DWORD SizeOfData, DWORD Flags) {
431 return IWineD3DResourceImpl_SetPrivateData((IWineD3DResource *)iface, refguid, pData, SizeOfData, Flags);
434 HRESULT WINAPI IWineD3DSurfaceImpl_GetPrivateData(IWineD3DSurface *iface, REFGUID refguid, void* pData, DWORD* pSizeOfData) {
435 return IWineD3DResourceImpl_GetPrivateData((IWineD3DResource *)iface, refguid, pData, pSizeOfData);
438 HRESULT WINAPI IWineD3DSurfaceImpl_FreePrivateData(IWineD3DSurface *iface, REFGUID refguid) {
439 return IWineD3DResourceImpl_FreePrivateData((IWineD3DResource *)iface, refguid);
442 DWORD WINAPI IWineD3DSurfaceImpl_SetPriority(IWineD3DSurface *iface, DWORD PriorityNew) {
443 return IWineD3DResourceImpl_SetPriority((IWineD3DResource *)iface, PriorityNew);
446 DWORD WINAPI IWineD3DSurfaceImpl_GetPriority(IWineD3DSurface *iface) {
447 return IWineD3DResourceImpl_GetPriority((IWineD3DResource *)iface);
450 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
451 /* TODO: check for locks */
452 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
453 IWineD3DBaseTexture *baseTexture = NULL;
454 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
456 TRACE("(%p)Checking to see if the container is a base texture\n", This);
457 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
458 TRACE("Passing to conatiner\n");
459 IWineD3DBaseTexture_PreLoad(baseTexture);
460 IWineD3DBaseTexture_Release(baseTexture);
462 TRACE("(%p) : About to load surface\n", This);
465 if(!device->isInDraw) {
466 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
469 glEnable(This->glDescription.target);/* make sure texture support is enabled in this context */
470 if (!This->glDescription.level) {
471 if (!This->glDescription.textureName) {
472 glGenTextures(1, &This->glDescription.textureName);
473 checkGLcall("glGenTextures");
474 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
476 glBindTexture(This->glDescription.target, This->glDescription.textureName);
477 checkGLcall("glBindTexture");
478 IWineD3DSurface_LoadTexture(iface);
479 /* This is where we should be reducing the amount of GLMemoryUsed */
480 } else if (This->glDescription.textureName) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
481 /* assume this is a coding error not a real error for now */
482 FIXME("Mipmap surface has a glTexture bound to it!\n");
484 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
485 /* Tell opengl to try and keep this texture in video ram (well mostly) */
488 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
495 WINED3DRESOURCETYPE WINAPI IWineD3DSurfaceImpl_GetType(IWineD3DSurface *iface) {
496 TRACE("(%p) : calling resourceimpl_GetType\n", iface);
497 return IWineD3DResourceImpl_GetType((IWineD3DResource *)iface);
500 HRESULT WINAPI IWineD3DSurfaceImpl_GetParent(IWineD3DSurface *iface, IUnknown **pParent) {
501 TRACE("(%p) : calling resourceimpl_GetParent\n", iface);
502 return IWineD3DResourceImpl_GetParent((IWineD3DResource *)iface, pParent);
505 /* ******************************************************
506 IWineD3DSurface IWineD3DSurface parts follow
507 ****************************************************** */
509 HRESULT WINAPI IWineD3DSurfaceImpl_GetContainer(IWineD3DSurface* iface, REFIID riid, void** ppContainer) {
510 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
511 IWineD3DBase *container = 0;
513 TRACE("(This %p, riid %s, ppContainer %p)\n", This, debugstr_guid(riid), ppContainer);
516 ERR("Called without a valid ppContainer.\n");
520 * If the surface is created using CreateImageSurface/CreateOffscreenPlainSurface, CreateRenderTarget,
521 * or CreateDepthStencilSurface, the surface is considered stand alone. In this case,
522 * GetContainer will return the Direct3D device used to create the surface.
524 if (This->container) {
525 container = This->container;
527 container = (IWineD3DBase *)This->resource.wineD3DDevice;
530 TRACE("Relaying to QueryInterface\n");
531 return IUnknown_QueryInterface(container, riid, ppContainer);
534 HRESULT WINAPI IWineD3DSurfaceImpl_GetDesc(IWineD3DSurface *iface, WINED3DSURFACE_DESC *pDesc) {
535 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
537 TRACE("(%p) : copying into %p\n", This, pDesc);
538 if(pDesc->Format != NULL) *(pDesc->Format) = This->resource.format;
539 if(pDesc->Type != NULL) *(pDesc->Type) = This->resource.resourceType;
540 if(pDesc->Usage != NULL) *(pDesc->Usage) = This->resource.usage;
541 if(pDesc->Pool != NULL) *(pDesc->Pool) = This->resource.pool;
542 if(pDesc->Size != NULL) *(pDesc->Size) = This->resource.size; /* dx8 only */
543 if(pDesc->MultiSampleType != NULL) *(pDesc->MultiSampleType) = This->currentDesc.MultiSampleType;
544 if(pDesc->MultiSampleQuality != NULL) *(pDesc->MultiSampleQuality) = This->currentDesc.MultiSampleQuality;
545 if(pDesc->Width != NULL) *(pDesc->Width) = This->currentDesc.Width;
546 if(pDesc->Height != NULL) *(pDesc->Height) = This->currentDesc.Height;
550 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
551 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
552 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
553 if (This->glDescription.textureName == 0 && textureName != 0) {
554 This->Flags &= ~SFLAG_INTEXTURE;
555 IWineD3DSurface_AddDirtyRect(iface, NULL);
557 This->glDescription.textureName = textureName;
558 This->glDescription.target = target;
561 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
562 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
563 TRACE("(%p) : returning %p\n", This, &This->glDescription);
564 *glDescription = &This->glDescription;
567 /* TODO: think about moving this down to resource? */
568 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
569 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
570 /* This should only be called for sysmem textures, it may be a good idea to extend this to all pools at some point in the futture */
571 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
572 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
574 return (CONST void*)(This->resource.allocatedMemory);
577 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch, BOOL srcUpsideDown) {
581 BYTE *row, *top, *bottom;
585 switch(This->resource.format)
589 /* GL can't return palettized data, so read ARGB pixels into a
590 * separate block of memory and convert them into palettized format
591 * in software. Slow, but if the app means to use palettized render
592 * targets and locks it...
594 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
595 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
596 * for the color channels when palettizing the colors.
599 type = GL_UNSIGNED_BYTE;
601 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
603 ERR("Out of memory\n");
606 bpp = This->bytesPerPixel * 3;
612 fmt = This->glDescription.glFormat;
613 type = This->glDescription.glType;
614 bpp = This->bytesPerPixel;
617 glReadPixels(rect->left, rect->top,
618 rect->right - rect->left,
619 rect->bottom - rect->top,
621 vcheckGLcall("glReadPixels");
623 /* TODO: Merge this with the palettization loop below for P8 targets */
627 /* glReadPixels returns the image upside down, and there is no way to prevent this.
628 Flip the lines in software */
629 len = (rect->right - rect->left) * bpp;
630 off = rect->left * bpp;
632 row = HeapAlloc(GetProcessHeap(), 0, len);
634 ERR("Out of memory\n");
635 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
639 top = mem + pitch * rect->top;
640 bottom = ((BYTE *) mem) + pitch * ( rect->bottom - rect->top - 1);
641 for(i = 0; i < (rect->bottom - rect->top) / 2; i++) {
642 memcpy(row, top + off, len);
643 memcpy(top + off, bottom + off, len);
644 memcpy(bottom + off, row, len);
648 HeapFree(GetProcessHeap(), 0, row);
651 if(This->resource.format == WINED3DFMT_P8) {
653 DWORD width = pitch / 3;
656 pal = This->palette->palents;
658 pal = This->resource.wineD3DDevice->palettes[This->resource.wineD3DDevice->currentPalette];
661 for(y = rect->top; y < rect->bottom; y++) {
662 for(x = rect->left; x < rect->right; x++) {
663 /* start lines pixels */
664 BYTE *blue = (BYTE *) ((BYTE *) mem) + y * pitch + x * (sizeof(BYTE) * 3);
665 BYTE *green = blue + 1;
666 BYTE *red = green + 1;
668 for(c = 0; c < 256; c++) {
669 if(*red == pal[c].peRed &&
670 *green == pal[c].peGreen &&
671 *blue == pal[c].peBlue)
673 *((BYTE *) dest + y * width + x) = c;
679 HeapFree(GetProcessHeap(), 0, mem);
683 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
684 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
685 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
686 IWineD3DSwapChainImpl *swapchain = NULL;
688 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
690 if (!(This->Flags & SFLAG_LOCKABLE)) {
691 /* Note: UpdateTextures calls CopyRects which calls this routine to populate the
692 texture regions, and since the destination is an unlockable region we need
694 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
695 /*return WINED3DERR_INVALIDCALL; */
698 pLockedRect->Pitch = IWineD3DSurface_GetPitch(iface);
700 /* Mark the surface locked */
701 This->Flags |= SFLAG_LOCKED;
703 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy */
704 if(!This->resource.allocatedMemory) {
705 This->resource.allocatedMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + 4);
706 This->Flags &= ~SFLAG_INSYSMEM; /* This is the marker that surface data has to be downloaded */
709 /* Calculate the correct start address to report */
711 pLockedRect->pBits = This->resource.allocatedMemory;
712 This->lockedRect.left = 0;
713 This->lockedRect.top = 0;
714 This->lockedRect.right = This->currentDesc.Width;
715 This->lockedRect.bottom = This->currentDesc.Height;
716 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);
718 TRACE("Lock Rect (%p) = l %d, t %d, r %d, b %d\n", pRect, pRect->left, pRect->top, pRect->right, pRect->bottom);
720 if ((pRect->top < 0) ||
722 (pRect->left >= pRect->right) ||
723 (pRect->top >= pRect->bottom) ||
724 (pRect->right > This->currentDesc.Width) ||
725 (pRect->bottom > This->currentDesc.Height))
727 WARN(" Invalid values in pRect !!!\n");
728 return WINED3DERR_INVALIDCALL;
731 /* DXTn textures are based on compressed blocks of 4x4 pixels, each
732 * 16 bytes large (8 bytes in case of DXT1). Because of that Pitch has
733 * slightly different meaning compared to regular textures. For DXTn
734 * textures Pitch is the size of a row of blocks, 4 high and "width"
735 * long. The x offset is calculated differently as well, since moving 4
736 * pixels to the right actually moves an entire 4x4 block to right, ie
737 * 16 bytes (8 in case of DXT1). */
738 if (This->resource.format == WINED3DFMT_DXT1) {
739 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top / 4) + (pRect->left * 2);
740 } else if (This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3
741 || This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
742 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top / 4) + (pRect->left * 4);
744 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top) + (pRect->left * This->bytesPerPixel);
746 This->lockedRect.left = pRect->left;
747 This->lockedRect.top = pRect->top;
748 This->lockedRect.right = pRect->right;
749 This->lockedRect.bottom = pRect->bottom;
752 if (This->Flags & SFLAG_NONPOW2) {
753 TRACE("Locking non-power 2 texture\n");
756 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
757 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
760 if(!(This->Flags & SFLAG_DYNLOCK)) {
762 /* MAXLOCKCOUNT is defined in wined3d_private.h */
763 if(This->lockCount > MAXLOCKCOUNT) {
764 TRACE("Surface is locked regularily, not freeing the system memory copy any more\n");
765 This->Flags |= SFLAG_DYNLOCK;
769 if((Flags & WINED3DLOCK_DISCARD) || (This->Flags & SFLAG_INSYSMEM)) {
770 TRACE("WINED3DLOCK_DISCARD flag passed, or local copy is up to date, not downloading data\n");
774 /* Now download the surface content from opengl
775 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
776 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
778 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
779 if(swapchain || iface == myDevice->render_targets[0]) {
780 BOOL srcIsUpsideDown;
782 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
783 static BOOL warned = FALSE;
785 ERR("The application tries to lock the render target, but render target locking is disabled\n");
788 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
792 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
793 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
794 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
795 * context->last_was_blit set on the unlock.
798 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
800 /* Select the correct read buffer, and give some debug output.
801 * There is no need to keep track of the current read buffer or reset it, every part of the code
802 * that reads sets the read buffer as desired.
805 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
806 * Read from the back buffer
808 TRACE("Locking offscreen render target\n");
809 glReadBuffer(myDevice->offscreenBuffer);
810 srcIsUpsideDown = TRUE;
812 GLenum buffer = surface_get_gl_buffer(iface, (IWineD3DSwapChain *)swapchain);
813 TRACE("Locking %#x buffer\n", buffer);
814 glReadBuffer(buffer);
815 checkGLcall("glReadBuffer");
817 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
818 srcIsUpsideDown = FALSE;
821 switch(wined3d_settings.rendertargetlock_mode) {
825 read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
830 read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
831 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
836 /* Mark the local copy up to date if a full download was done */
837 if(This->lockedRect.left == 0 &&
838 This->lockedRect.top == 0 &&
839 This->lockedRect.right == This->currentDesc.Width &&
840 This->lockedRect.bottom == This->currentDesc.Height) {
841 This->Flags |= SFLAG_INSYSMEM;
843 } else if(iface == myDevice->stencilBufferTarget) {
844 /** the depth stencil in openGL has a format of GL_FLOAT
845 * which should be good for WINED3DFMT_D16_LOCKABLE
847 * it is unclear what format the stencil buffer is in except.
848 * 'Each index is converted to fixed point...
849 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
850 * mappings in the table GL_PIXEL_MAP_S_TO_S.
851 * glReadPixels(This->lockedRect.left,
852 * This->lockedRect.bottom - j - 1,
853 * This->lockedRect.right - This->lockedRect.left,
855 * GL_DEPTH_COMPONENT,
857 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
859 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
860 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
861 * none of that is the case the problem is not in this function :-)
862 ********************************************/
863 FIXME("Depth stencil locking not supported yet\n");
865 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
866 TRACE("locking an ordinarary surface\n");
868 if (0 != This->glDescription.textureName) {
869 /* Now I have to copy thing bits back */
873 if(myDevice->createParms.BehaviorFlags & WINED3DCREATE_MULTITHREADED) {
874 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
877 /* Make sure that a proper texture unit is selected, bind the texture and dirtify the sampler to restore the texture on the next draw */
878 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
879 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
880 checkGLcall("glActiveTextureARB");
882 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(0));
883 IWineD3DSurface_PreLoad(iface);
885 surface_download_data(This);
889 /* The local copy is now up to date to the opengl one because a full download was done */
890 This->Flags |= SFLAG_INSYSMEM;
894 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
897 IWineD3DBaseTexture *pBaseTexture;
900 * as seen in msdn docs
902 IWineD3DSurface_AddDirtyRect(iface, &This->lockedRect);
904 /** Dirtify Container if needed */
905 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
906 TRACE("Making container dirty\n");
907 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
908 IWineD3DBaseTexture_Release(pBaseTexture);
910 TRACE("Surface is standalone, no need to dirty the container\n");
914 TRACE("returning memory@%p, pitch(%d) dirtyfied(%d)\n", pLockedRect->pBits, pLockedRect->Pitch,
915 This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
919 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This) {
921 GLint prev_rasterpos[4];
923 BOOL storechanged = FALSE, memory_allocated = FALSE;
927 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
929 glDisable(GL_TEXTURE_2D);
930 vcheckGLcall("glDisable(GL_TEXTURE_2D)");
933 vcheckGLcall("glFlush");
934 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
935 vcheckGLcall("glIntegerv");
936 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
937 vcheckGLcall("glIntegerv");
938 glPixelZoom(1.0, -1.0);
939 vcheckGLcall("glPixelZoom");
941 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
942 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
943 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
945 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
946 vcheckGLcall("glRasterPos2f");
948 /* Some drivers(radeon dri, others?) don't like exceptions during
949 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
950 * after ReleaseDC. Reading it will cause an exception, which x11drv will
951 * catch to put the dib section in InSync mode, which leads to a crash
952 * and a blocked x server on my radeon card.
954 * The following lines read the dib section so it is put in inSync mode
955 * before glDrawPixels is called and the crash is prevented. There won't
956 * be any interfering gdi accesses, because UnlockRect is called from
957 * ReleaseDC, and the app won't use the dc any more afterwards.
959 if(This->Flags & SFLAG_DIBSECTION) {
961 read = This->resource.allocatedMemory[0];
964 switch (This->resource.format) {
965 /* No special care needed */
966 case WINED3DFMT_A4R4G4B4:
967 case WINED3DFMT_R5G6B5:
968 case WINED3DFMT_A1R5G5B5:
969 case WINED3DFMT_R8G8B8:
970 type = This->glDescription.glType;
971 fmt = This->glDescription.glFormat;
972 mem = This->resource.allocatedMemory;
973 bpp = This->bytesPerPixel;
976 case WINED3DFMT_X4R4G4B4:
979 unsigned short *data;
980 data = (unsigned short *)This->resource.allocatedMemory;
981 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
987 type = This->glDescription.glType;
988 fmt = This->glDescription.glFormat;
989 mem = This->resource.allocatedMemory;
990 bpp = This->bytesPerPixel;
994 case WINED3DFMT_X1R5G5B5:
997 unsigned short *data;
998 data = (unsigned short *)This->resource.allocatedMemory;
999 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
1005 type = This->glDescription.glType;
1006 fmt = This->glDescription.glFormat;
1007 mem = This->resource.allocatedMemory;
1008 bpp = This->bytesPerPixel;
1012 case WINED3DFMT_X8R8G8B8:
1014 /* make sure the X byte is set to alpha on, since it
1015 could be any random value. This fixes the intro movie in Pirates! */
1018 data = (unsigned int *)This->resource.allocatedMemory;
1019 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
1021 *data |= 0xFF000000;
1028 case WINED3DFMT_A8R8G8B8:
1030 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
1031 vcheckGLcall("glPixelStorei");
1032 storechanged = TRUE;
1033 type = This->glDescription.glType;
1034 fmt = This->glDescription.glFormat;
1035 mem = This->resource.allocatedMemory;
1036 bpp = This->bytesPerPixel;
1040 case WINED3DFMT_A2R10G10B10:
1042 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
1043 vcheckGLcall("glPixelStorei");
1044 storechanged = TRUE;
1045 type = This->glDescription.glType;
1046 fmt = This->glDescription.glFormat;
1047 mem = This->resource.allocatedMemory;
1048 bpp = This->bytesPerPixel;
1054 int height = This->glRect.bottom - This->glRect.top;
1055 type = GL_UNSIGNED_BYTE;
1058 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * sizeof(DWORD));
1060 ERR("Out of memory\n");
1063 memory_allocated = TRUE;
1064 d3dfmt_convert_surface(This->resource.allocatedMemory,
1072 bpp = This->bytesPerPixel * 4;
1078 FIXME("Unsupported Format %u in locking func\n", This->resource.format);
1081 type = This->glDescription.glType;
1082 fmt = This->glDescription.glFormat;
1083 mem = This->resource.allocatedMemory;
1084 bpp = This->bytesPerPixel;
1087 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1088 (This->lockedRect.bottom - This->lockedRect.top)-1,
1090 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1091 checkGLcall("glDrawPixels");
1092 glPixelZoom(1.0,1.0);
1093 vcheckGLcall("glPixelZoom");
1095 glRasterPos3iv(&prev_rasterpos[0]);
1096 vcheckGLcall("glRasterPos3iv");
1098 /* Reset to previous pack row length */
1099 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1100 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1102 glPixelStorei(GL_PACK_SWAP_BYTES, prev_store);
1103 vcheckGLcall("glPixelStorei GL_PACK_SWAP_BYTES");
1106 /* Blitting environment requires that 2D texturing is enabled. It was turned off before,
1109 glEnable(GL_TEXTURE_2D);
1110 checkGLcall("glEnable(GL_TEXTURE_2D)");
1112 if(memory_allocated) HeapFree(GetProcessHeap(), 0, mem);
1116 static void flush_to_framebuffer_texture(IWineD3DSurfaceImpl *This) {
1117 float glTexCoord[4];
1119 glTexCoord[0] = (float) This->lockedRect.left / (float) This->pow2Width; /* left */
1120 glTexCoord[1] = (float) This->lockedRect.right / (float) This->pow2Width; /* right */
1121 glTexCoord[2] = (float) This->lockedRect.top / (float) This->pow2Height; /* top */
1122 glTexCoord[3] = (float) This->lockedRect.bottom / (float) This->pow2Height; /* bottom */
1124 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
1128 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
1129 checkGLcall("glEnable glBindTexture");
1131 /* No filtering for blts */
1132 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1133 checkGLcall("glTexParameteri");
1134 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1135 checkGLcall("glTexParameteri");
1137 /* Start drawing a quad */
1140 glColor3d(1.0f, 1.0f, 1.0f);
1141 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
1142 glVertex3f(This->lockedRect.left, This->lockedRect.top, 0.0);
1144 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
1145 glVertex3f(This->lockedRect.left, This->lockedRect.bottom, 0.0);
1147 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
1148 glVertex3d(This->lockedRect.right, This->lockedRect.bottom, 0.0);
1150 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
1151 glVertex3f(This->lockedRect.right, This->lockedRect.top, 0.0);
1154 checkGLcall("glEnd");
1156 /* Unbind the texture */
1157 glBindTexture(GL_TEXTURE_2D, 0);
1158 checkGLcall("glEnable glBindTexture");
1163 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1164 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1165 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1166 IWineD3DSwapChainImpl *swapchain = NULL;
1168 if (!(This->Flags & SFLAG_LOCKED)) {
1169 WARN("trying to Unlock an unlocked surf@%p\n", This);
1170 return WINED3DERR_INVALIDCALL;
1173 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1175 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1176 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1180 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1181 if(swapchain || iface == myDevice->render_targets[0]) {
1182 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1183 static BOOL warned = FALSE;
1185 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1188 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1192 /* Activate the correct context for the render target */
1194 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1197 /* Primary offscreen render target */
1198 TRACE("Offscreen render target\n");
1199 glDrawBuffer(myDevice->offscreenBuffer);
1200 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1202 GLenum buffer = surface_get_gl_buffer(iface, (IWineD3DSwapChain *)swapchain);
1203 TRACE("Unlocking %#x buffer\n", buffer);
1204 glDrawBuffer(buffer);
1205 checkGLcall("glDrawBuffer");
1207 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1210 switch(wined3d_settings.rendertargetlock_mode) {
1214 flush_to_framebuffer_drawpixels(This);
1219 flush_to_framebuffer_texture(This);
1223 glDrawBuffer(myDevice->offscreenBuffer);
1224 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1225 } else if(swapchain->backBuffer) {
1226 glDrawBuffer(GL_BACK);
1227 checkGLcall("glDrawBuffer(GL_BACK)");
1229 glDrawBuffer(GL_FRONT);
1230 checkGLcall("glDrawBuffer(GL_FRONT)");
1234 This->dirtyRect.left = This->currentDesc.Width;
1235 This->dirtyRect.top = This->currentDesc.Height;
1236 This->dirtyRect.right = 0;
1237 This->dirtyRect.bottom = 0;
1238 This->Flags |= SFLAG_INDRAWABLE;
1239 } else if(iface == myDevice->stencilBufferTarget) {
1240 FIXME("Depth Stencil buffer locking is not implemented\n");
1242 /* The rest should be a normal texture */
1243 IWineD3DBaseTextureImpl *impl;
1244 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1245 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1246 * states need resetting
1248 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1249 if(impl->baseTexture.bindCount) {
1250 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1252 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1257 This->Flags &= ~SFLAG_LOCKED;
1258 memset(&This->lockedRect, 0, sizeof(RECT));
1262 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1263 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1264 WINED3DLOCKED_RECT lock;
1271 const PixelFormatDesc *formatEntry = getFormatDescEntry(This->resource.format);
1273 TRACE("(%p)->(%p)\n",This,pHDC);
1275 if(This->Flags & SFLAG_USERPTR) {
1276 ERR("Not supported on surfaces with an application-provided surfaces\n");
1277 return WINEDDERR_NODC;
1280 /* Give more detailed info for ddraw */
1281 if (This->Flags & SFLAG_DCINUSE)
1282 return WINEDDERR_DCALREADYCREATED;
1284 /* Can't GetDC if the surface is locked */
1285 if (This->Flags & SFLAG_LOCKED)
1286 return WINED3DERR_INVALIDCALL;
1288 memset(&lock, 0, sizeof(lock)); /* To be sure */
1290 /* Create a DIB section if there isn't a hdc yet */
1293 SYSTEM_INFO sysInfo;
1294 void *oldmem = This->resource.allocatedMemory;
1296 switch (This->bytesPerPixel) {
1299 /* Allocate extra space to store the RGB bit masks. */
1300 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
1304 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
1308 /* Allocate extra space for a palette. */
1309 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1310 sizeof(BITMAPINFOHEADER)
1312 * (1 << (This->bytesPerPixel * 8)));
1317 return E_OUTOFMEMORY;
1319 /* Some apps access the surface in via DWORDs, and do not take the necessary care at the end of the
1320 * surface. So we need at least extra 4 bytes at the end of the surface. Check against the page size,
1321 * if the last page used for the surface has at least 4 spare bytes we're safe, otherwise
1322 * add an extra line to the dib section
1324 GetSystemInfo(&sysInfo);
1325 if( ((This->resource.size + 3) % sysInfo.dwPageSize) < 4) {
1327 TRACE("Adding an extra line to the dib section\n");
1330 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1331 b_info->bmiHeader.biWidth = This->currentDesc.Width;
1332 b_info->bmiHeader.biHeight = -This->currentDesc.Height -extraline;
1333 b_info->bmiHeader.biSizeImage = ( This->currentDesc.Height + extraline) * IWineD3DSurface_GetPitch(iface);
1334 b_info->bmiHeader.biPlanes = 1;
1335 b_info->bmiHeader.biBitCount = This->bytesPerPixel * 8;
1337 b_info->bmiHeader.biXPelsPerMeter = 0;
1338 b_info->bmiHeader.biYPelsPerMeter = 0;
1339 b_info->bmiHeader.biClrUsed = 0;
1340 b_info->bmiHeader.biClrImportant = 0;
1342 /* Get the bit masks */
1343 masks = (DWORD *) &(b_info->bmiColors);
1344 switch (This->resource.format) {
1345 case WINED3DFMT_R8G8B8:
1346 usage = DIB_RGB_COLORS;
1347 b_info->bmiHeader.biCompression = BI_RGB;
1350 case WINED3DFMT_X1R5G5B5:
1351 case WINED3DFMT_A1R5G5B5:
1352 case WINED3DFMT_A4R4G4B4:
1353 case WINED3DFMT_X4R4G4B4:
1354 case WINED3DFMT_R3G3B2:
1355 case WINED3DFMT_A8R3G3B2:
1356 case WINED3DFMT_A2B10G10R10:
1357 case WINED3DFMT_A8B8G8R8:
1358 case WINED3DFMT_X8B8G8R8:
1359 case WINED3DFMT_A2R10G10B10:
1360 case WINED3DFMT_R5G6B5:
1361 case WINED3DFMT_A16B16G16R16:
1363 b_info->bmiHeader.biCompression = BI_BITFIELDS;
1364 masks[0] = formatEntry->redMask;
1365 masks[1] = formatEntry->greenMask;
1366 masks[2] = formatEntry->blueMask;
1370 /* Don't know palette */
1371 b_info->bmiHeader.biCompression = BI_RGB;
1378 HeapFree(GetProcessHeap(), 0, b_info);
1379 return HRESULT_FROM_WIN32(GetLastError());
1382 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);
1383 This->dib.DIBsection = CreateDIBSection(ddc, b_info, usage, &This->dib.bitmap_data, 0 /* Handle */, 0 /* Offset */);
1386 if (!This->dib.DIBsection) {
1387 ERR("CreateDIBSection failed!\n");
1388 HeapFree(GetProcessHeap(), 0, b_info);
1389 return HRESULT_FROM_WIN32(GetLastError());
1392 TRACE("DIBSection at : %p\n", This->dib.bitmap_data);
1394 /* copy the existing surface to the dib section */
1395 if(This->resource.allocatedMemory) {
1396 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, b_info->bmiHeader.biSizeImage);
1397 /* We won't need that any more */
1399 /* This is to make LockRect read the gl Texture although memory is allocated */
1400 This->Flags &= ~SFLAG_INSYSMEM;
1403 HeapFree(GetProcessHeap(), 0, b_info);
1405 /* Use the dib section from now on */
1406 This->resource.allocatedMemory = This->dib.bitmap_data;
1408 /* Now allocate a HDC */
1409 This->hDC = CreateCompatibleDC(0);
1410 This->dib.holdbitmap = SelectObject(This->hDC, This->dib.DIBsection);
1411 TRACE("using wined3d palette %p\n", This->palette);
1412 SelectPalette(This->hDC,
1413 This->palette ? This->palette->hpal : 0,
1416 This->Flags |= SFLAG_DIBSECTION;
1418 if(This->Flags & SFLAG_CLIENT) {
1419 IWineD3DSurface_PreLoad(iface);
1421 HeapFree(GetProcessHeap(), 0, oldmem);
1424 /* Lock the surface */
1425 hr = IWineD3DSurface_LockRect(iface,
1430 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1431 /* keep the dib section */
1435 if(This->resource.format == WINED3DFMT_P8 ||
1436 This->resource.format == WINED3DFMT_A8P8) {
1439 PALETTEENTRY ent[256];
1441 GetPaletteEntries(This->palette->hpal, 0, 256, ent);
1442 for (n=0; n<256; n++) {
1443 col[n].rgbRed = ent[n].peRed;
1444 col[n].rgbGreen = ent[n].peGreen;
1445 col[n].rgbBlue = ent[n].peBlue;
1446 col[n].rgbReserved = 0;
1449 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1451 for (n=0; n<256; n++) {
1452 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
1453 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
1454 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
1455 col[n].rgbReserved = 0;
1459 SetDIBColorTable(This->hDC, 0, 256, col);
1463 TRACE("returning %p\n",*pHDC);
1464 This->Flags |= SFLAG_DCINUSE;
1469 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1470 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1472 TRACE("(%p)->(%p)\n",This,hDC);
1474 if (!(This->Flags & SFLAG_DCINUSE))
1475 return WINED3DERR_INVALIDCALL;
1477 /* we locked first, so unlock now */
1478 IWineD3DSurface_UnlockRect(iface);
1480 This->Flags &= ~SFLAG_DCINUSE;
1485 /* ******************************************************
1486 IWineD3DSurface Internal (No mapping to directx api) parts follow
1487 ****************************************************** */
1489 static HRESULT d3dfmt_get_conv(IWineD3DSurfaceImpl *This, BOOL need_alpha_ck, BOOL use_texturing, GLenum *format, GLenum *internal, GLenum *type, CONVERT_TYPES *convert, int *target_bpp) {
1490 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1491 const PixelFormatDesc *formatEntry = getFormatDescEntry(This->resource.format);
1493 /* Default values: From the surface */
1494 *format = formatEntry->glFormat;
1495 *internal = formatEntry->glInternal;
1496 *type = formatEntry->glType;
1497 *convert = NO_CONVERSION;
1498 *target_bpp = This->bytesPerPixel;
1500 /* Ok, now look if we have to do any conversion */
1501 switch(This->resource.format) {
1506 /* Use conversion when the paletted texture extension is not available, or when it is available make sure it is used
1507 * for texturing as it won't work for calls like glDraw-/glReadPixels and further also use conversion in case of color keying.
1509 if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) || colorkey_active || (!use_texturing && GL_SUPPORT(EXT_PALETTED_TEXTURE)) ) {
1511 *internal = GL_RGBA;
1512 *type = GL_UNSIGNED_BYTE;
1514 if(colorkey_active) {
1515 *convert = CONVERT_PALETTED_CK;
1517 *convert = CONVERT_PALETTED;
1523 case WINED3DFMT_R3G3B2:
1524 /* **********************
1525 GL_UNSIGNED_BYTE_3_3_2
1526 ********************** */
1527 if (colorkey_active) {
1528 /* This texture format will never be used.. So do not care about color keying
1529 up until the point in time it will be needed :-) */
1530 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1534 case WINED3DFMT_R5G6B5:
1535 if (colorkey_active) {
1536 *convert = CONVERT_CK_565;
1538 *internal = GL_RGBA;
1539 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1543 case WINED3DFMT_R8G8B8:
1544 if (colorkey_active) {
1545 *convert = CONVERT_CK_RGB24;
1547 *internal = GL_RGBA;
1548 *type = GL_UNSIGNED_INT_8_8_8_8;
1553 case WINED3DFMT_X8R8G8B8:
1554 if (colorkey_active) {
1555 *convert = CONVERT_RGB32_888;
1557 *internal = GL_RGBA;
1558 *type = GL_UNSIGNED_INT_8_8_8_8;
1562 case WINED3DFMT_V8U8:
1563 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1564 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1565 *format = GL_DUDV_ATI;
1566 *internal = GL_DU8DV8_ATI;
1568 /* No conversion - Just change the gl type */
1571 *convert = CONVERT_V8U8;
1573 *internal = GL_RGB8;
1574 *type = GL_UNSIGNED_BYTE;
1578 case WINED3DFMT_X8L8V8U8:
1579 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1580 *convert = CONVERT_X8L8V8U8;
1582 *internal = GL_RGBA8;
1583 *type = GL_UNSIGNED_BYTE;
1585 /* Not supported by GL_ATI_envmap_bumpmap */
1588 case WINED3DFMT_Q8W8V8U8:
1589 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1590 *convert = CONVERT_Q8W8V8U8;
1592 *internal = GL_RGBA8;
1593 *type = GL_UNSIGNED_BYTE;
1595 /* Not supported by GL_ATI_envmap_bumpmap */
1598 case WINED3DFMT_V16U16:
1599 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1600 *convert = CONVERT_V16U16;
1602 *internal = GL_RGB16;
1605 /* What should I do here about GL_ATI_envmap_bumpmap?
1606 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1617 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf) {
1618 BYTE *source, *dest;
1619 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surf);
1624 memcpy(dst, src, pitch * height);
1627 case CONVERT_PALETTED:
1628 case CONVERT_PALETTED_CK:
1630 IWineD3DPaletteImpl* pal = surf->palette;
1636 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1640 /* Still no palette? Use the device's palette */
1641 /* Get the surface's palette */
1642 for (i = 0; i < 256; i++) {
1643 IWineD3DDeviceImpl *device = surf->resource.wineD3DDevice;
1645 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1646 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1647 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1648 if ((convert == CONVERT_PALETTED_CK) &&
1649 (i >= surf->SrcBltCKey.dwColorSpaceLowValue) &&
1650 (i <= surf->SrcBltCKey.dwColorSpaceHighValue)) {
1651 /* We should maybe here put a more 'neutral' color than the standard bright purple
1652 one often used by application to prevent the nice purple borders when bi-linear
1660 TRACE("Using surface palette %p\n", pal);
1661 /* Get the surface's palette */
1662 for (i = 0; i < 256; i++) {
1663 table[i][0] = pal->palents[i].peRed;
1664 table[i][1] = pal->palents[i].peGreen;
1665 table[i][2] = pal->palents[i].peBlue;
1666 if ((convert == CONVERT_PALETTED_CK) &&
1667 (i >= surf->SrcBltCKey.dwColorSpaceLowValue) &&
1668 (i <= surf->SrcBltCKey.dwColorSpaceHighValue)) {
1669 /* We should maybe here put a more 'neutral' color than the standard bright purple
1670 one often used by application to prevent the nice purple borders when bi-linear
1679 for (y = 0; y < height; y++)
1681 source = src + pitch * y;
1682 dest = dst + outpitch * y;
1683 /* This is an 1 bpp format, using the width here is fine */
1684 for (x = 0; x < width; x++) {
1685 BYTE color = *source++;
1686 *dest++ = table[color][0];
1687 *dest++ = table[color][1];
1688 *dest++ = table[color][2];
1689 *dest++ = table[color][3];
1695 case CONVERT_CK_565:
1697 /* Converting the 565 format in 5551 packed to emulate color-keying.
1699 Note : in all these conversion, it would be best to average the averaging
1700 pixels to get the color of the pixel that will be color-keyed to
1701 prevent 'color bleeding'. This will be done later on if ever it is
1704 Note2: Nvidia documents say that their driver does not support alpha + color keying
1705 on the same surface and disables color keying in such a case
1711 TRACE("Color keyed 565\n");
1713 for (y = 0; y < height; y++) {
1714 Source = (WORD *) (src + y * pitch);
1715 Dest = (WORD *) (dst + y * outpitch);
1716 for (x = 0; x < width; x++ ) {
1717 WORD color = *Source++;
1718 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1719 if ((color < surf->SrcBltCKey.dwColorSpaceLowValue) ||
1720 (color > surf->SrcBltCKey.dwColorSpaceHighValue)) {
1733 unsigned char *Dest;
1734 for(y = 0; y < height; y++) {
1735 Source = (short *) (src + y * pitch);
1736 Dest = (unsigned char *) (dst + y * outpitch);
1737 for (x = 0; x < width; x++ ) {
1738 long color = (*Source++);
1739 /* B */ Dest[0] = 0xff;
1740 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1741 /* R */ Dest[2] = (color) + 128; /* U */
1748 case CONVERT_Q8W8V8U8:
1752 unsigned char *Dest;
1753 for(y = 0; y < height; y++) {
1754 Source = (DWORD *) (src + y * pitch);
1755 Dest = (unsigned char *) (dst + y * outpitch);
1756 for (x = 0; x < width; x++ ) {
1757 long color = (*Source++);
1758 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1759 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1760 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1761 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1769 ERR("Unsupported conversation type %d\n", convert);
1774 /* This function is used in case of 8bit paletted textures to upload the palette.
1775 For now it only supports GL_EXT_paletted_texture extension but support for other
1776 extensions like ARB_fragment_program and ATI_fragment_shaders will be added as well.
1778 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
1779 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1780 IWineD3DPaletteImpl* pal = This->palette;
1785 /* Still no palette? Use the device's palette */
1786 /* Get the surface's palette */
1787 for (i = 0; i < 256; i++) {
1788 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1790 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1791 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1792 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1793 if ((convert == CONVERT_PALETTED_CK) &&
1794 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1795 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1796 /* We should maybe here put a more 'neutral' color than the standard bright purple
1797 one often used by application to prevent the nice purple borders when bi-linear
1805 TRACE("Using surface palette %p\n", pal);
1806 /* Get the surface's palette */
1807 for (i = 0; i < 256; i++) {
1808 table[i][0] = pal->palents[i].peRed;
1809 table[i][1] = pal->palents[i].peGreen;
1810 table[i][2] = pal->palents[i].peBlue;
1811 if ((convert == CONVERT_PALETTED_CK) &&
1812 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1813 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1814 /* We should maybe here put a more 'neutral' color than the standard bright purple
1815 one often used by application to prevent the nice purple borders when bi-linear
1823 GL_EXTCALL(glColorTableEXT(GL_TEXTURE_2D,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
1826 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
1827 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1829 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
1830 /* If a ddraw-style palette is attached assume no d3d9 palette change.
1831 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
1836 if(This->palette9) {
1837 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
1841 This->palette9 = (PALETTEENTRY *) HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
1843 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
1847 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface) {
1848 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1849 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1850 GLenum format, internal, type;
1851 CONVERT_TYPES convert;
1853 int width, pitch, outpitch;
1856 if (!(This->Flags & SFLAG_INTEXTURE)) {
1857 TRACE("Reloading because surface is dirty\n");
1858 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
1859 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
1860 /* Reload: vice versa OR */
1861 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
1862 /* Also reload: Color key is active AND the color key has changed */
1863 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
1864 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
1865 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
1866 TRACE("Reloading because of color keying\n");
1867 } else if(palette9_changed(This)) {
1868 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
1870 TRACE("surface is already in texture\n");
1874 This->Flags |= SFLAG_INTEXTURE;
1876 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
1877 * These resources are not bound by device size or format restrictions. Because of this,
1878 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
1879 * However, these resources can always be created, locked, and copied.
1881 if (This->resource.pool == WINED3DPOOL_SCRATCH )
1883 FIXME("(%p) Operation not supported for scratch textures\n",This);
1884 return WINED3DERR_INVALIDCALL;
1887 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp);
1889 if (This->Flags & SFLAG_INDRAWABLE) {
1890 if (This->glDescription.level != 0)
1891 FIXME("Surface in texture is only supported for level 0\n");
1892 else if (This->resource.format == WINED3DFMT_P8 || This->resource.format == WINED3DFMT_A8P8 ||
1893 This->resource.format == WINED3DFMT_DXT1 || This->resource.format == WINED3DFMT_DXT2 ||
1894 This->resource.format == WINED3DFMT_DXT3 || This->resource.format == WINED3DFMT_DXT4 ||
1895 This->resource.format == WINED3DFMT_DXT5)
1896 FIXME("Format %d not supported\n", This->resource.format);
1901 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1902 vcheckGLcall("glGetIntegerv");
1903 glReadBuffer(This->resource.wineD3DDevice->offscreenBuffer);
1904 vcheckGLcall("glReadBuffer");
1906 surface_allocate_surface(This, internal, This->pow2Width,
1907 This->pow2Height, format, type);
1909 glCopyTexSubImage2D(This->glDescription.target,
1910 This->glDescription.level,
1912 This->currentDesc.Width,
1913 This->currentDesc.Height);
1914 checkGLcall("glCopyTexSubImage2D");
1916 glReadBuffer(prevRead);
1917 vcheckGLcall("glReadBuffer");
1921 TRACE("Updated target %d\n", This->glDescription.target);
1925 /* The only place where LoadTexture() might get called when isInDraw=1
1926 * is ActivateContext where lastActiveRenderTarget is preloaded.
1928 if(iface == device->lastActiveRenderTarget && device->isInDraw)
1929 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
1931 /* Otherwise: System memory copy must be most up to date */
1933 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
1934 This->Flags |= SFLAG_GLCKEY;
1935 This->glCKey = This->SrcBltCKey;
1937 else This->Flags &= ~SFLAG_GLCKEY;
1939 /* The width is in 'length' not in bytes */
1940 width = This->currentDesc.Width;
1941 pitch = IWineD3DSurface_GetPitch(iface);
1943 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
1944 int height = This->currentDesc.Height;
1946 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
1947 outpitch = width * bpp;
1948 outpitch = (outpitch + SURFACE_ALIGNMENT - 1) & ~(SURFACE_ALIGNMENT - 1);
1950 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
1952 ERR("Out of memory %d, %d!\n", outpitch, height);
1953 return WINED3DERR_OUTOFVIDEOMEMORY;
1955 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
1957 This->Flags |= SFLAG_CONVERTED;
1958 } else if (This->resource.format == WINED3DFMT_P8 && GL_SUPPORT(EXT_PALETTED_TEXTURE)) {
1959 d3dfmt_p8_upload_palette(iface, convert);
1960 This->Flags &= ~SFLAG_CONVERTED;
1961 mem = This->resource.allocatedMemory;
1963 This->Flags &= ~SFLAG_CONVERTED;
1964 mem = This->resource.allocatedMemory;
1967 /* Make sure the correct pitch is used */
1968 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
1970 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
1971 TRACE("non power of two support\n");
1972 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
1974 surface_upload_data(This, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
1977 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
1978 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
1980 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
1982 surface_upload_data(This, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
1986 /* Restore the default pitch */
1987 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1989 if (mem != This->resource.allocatedMemory)
1990 HeapFree(GetProcessHeap(), 0, mem);
1994 static unsigned int gen = 0;
1997 if ((gen % 10) == 0) {
1998 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
1999 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2002 * debugging crash code
2011 if (!(This->Flags & SFLAG_DONOTFREE)) {
2012 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
2013 This->resource.allocatedMemory = NULL;
2014 This->Flags &= ~SFLAG_INSYSMEM;
2022 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2025 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2026 char *allocatedMemory;
2028 IWineD3DSwapChain *swapChain = NULL;
2030 GLuint tmpTexture = 0;
2033 Textures my not be stored in ->allocatedgMemory and a GlTexture
2034 so we should lock the surface before saving a snapshot, or at least check that
2036 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2037 by calling GetTexImage and in compressed form by calling
2038 GetCompressedTexImageARB. Queried compressed images can be saved and
2039 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2040 texture images do not need to be processed by the GL and should
2041 significantly improve texture loading performance relative to uncompressed
2044 /* Setup the width and height to be the internal texture width and height. */
2045 width = This->pow2Width;
2046 height = This->pow2Height;
2047 /* check to see if were a 'virtual' texture e.g. were not a pbuffer of texture were a back buffer*/
2048 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2050 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2051 /* if were not a real texture then read the back buffer into a real texture */
2052 /* we don't want to interfere with the back buffer so read the data into a temporary
2053 * texture and then save the data out of the temporary texture
2057 TRACE("(%p) Reading render target into texture\n", This);
2058 glEnable(GL_TEXTURE_2D);
2060 glGenTextures(1, &tmpTexture);
2061 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2063 glTexImage2D(GL_TEXTURE_2D,
2070 GL_UNSIGNED_INT_8_8_8_8_REV,
2073 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2074 vcheckGLcall("glGetIntegerv");
2075 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2076 vcheckGLcall("glReadBuffer");
2077 glCopyTexImage2D(GL_TEXTURE_2D,
2086 checkGLcall("glCopyTexImage2D");
2087 glReadBuffer(prevRead);
2090 } else { /* bind the real texture, and make sure it up to date */
2091 IWineD3DSurface_PreLoad(iface);
2093 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2095 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2096 glGetTexImage(GL_TEXTURE_2D,
2097 This->glDescription.level,
2099 GL_UNSIGNED_INT_8_8_8_8_REV,
2101 checkGLcall("glTexImage2D");
2103 glBindTexture(GL_TEXTURE_2D, 0);
2104 glDeleteTextures(1, &tmpTexture);
2108 f = fopen(filename, "w+");
2110 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2111 return WINED3DERR_INVALIDCALL;
2113 /* Save the dat out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha chanel*/
2114 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2129 fwrite(&width,2,1,f);
2131 fwrite(&height,2,1,f);
2136 /* 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*/
2138 textureRow = allocatedMemory + (width * (height - 1) *4);
2140 textureRow = allocatedMemory;
2141 for (y = 0 ; y < height; y++) {
2142 for (i = 0; i < width; i++) {
2143 color = *((DWORD*)textureRow);
2144 fputc((color >> 16) & 0xFF, f); /* B */
2145 fputc((color >> 8) & 0xFF, f); /* G */
2146 fputc((color >> 0) & 0xFF, f); /* R */
2147 fputc((color >> 24) & 0xFF, f); /* A */
2150 /* take two rows of the pointer to the texture memory */
2152 (textureRow-= width << 3);
2155 TRACE("Closing file\n");
2159 IWineD3DSwapChain_Release(swapChain);
2161 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2166 * Slightly inefficient way to handle multiple dirty rects but it works :)
2168 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2169 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2170 IWineD3DBaseTexture *baseTexture = NULL;
2171 This->Flags &= ~(SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
2172 if (NULL != pDirtyRect) {
2173 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2174 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2175 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2176 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2178 This->dirtyRect.left = 0;
2179 This->dirtyRect.top = 0;
2180 This->dirtyRect.right = This->currentDesc.Width;
2181 This->dirtyRect.bottom = This->currentDesc.Height;
2183 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2184 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2185 /* if the container is a basetexture then mark it dirty. */
2186 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2187 TRACE("Passing to conatiner\n");
2188 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2189 IWineD3DBaseTexture_Release(baseTexture);
2194 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
2195 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2197 TRACE("This %p, container %p\n", This, container);
2199 /* We can't keep a reference to the container, since the container already keeps a reference to us. */
2201 TRACE("Setting container to %p from %p\n", container, This->container);
2202 This->container = container;
2207 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2208 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2209 const PixelFormatDesc *formatEntry = getFormatDescEntry(format);
2211 if (This->resource.format != WINED3DFMT_UNKNOWN) {
2212 FIXME("(%p) : The foramt of the surface must be WINED3DFORMAT_UNKNOWN\n", This);
2213 return WINED3DERR_INVALIDCALL;
2216 TRACE("(%p) : Setting texture foramt to (%d,%s)\n", This, format, debug_d3dformat(format));
2217 if (format == WINED3DFMT_UNKNOWN) {
2218 This->resource.size = 0;
2219 } else if (format == WINED3DFMT_DXT1) {
2220 /* DXT1 is half byte per pixel */
2221 This->resource.size = ((max(This->pow2Width, 4) * formatEntry->bpp) * max(This->pow2Height, 4)) >> 1;
2223 } else if (format == WINED3DFMT_DXT2 || format == WINED3DFMT_DXT3 ||
2224 format == WINED3DFMT_DXT4 || format == WINED3DFMT_DXT5) {
2225 This->resource.size = ((max(This->pow2Width, 4) * formatEntry->bpp) * max(This->pow2Height, 4));
2227 This->resource.size = ((This->pow2Width * formatEntry->bpp) + SURFACE_ALIGNMENT - 1) & ~(SURFACE_ALIGNMENT - 1);
2228 This->resource.size *= This->pow2Height;
2232 /* Setup some glformat defaults */
2233 This->glDescription.glFormat = formatEntry->glFormat;
2234 This->glDescription.glFormatInternal = formatEntry->glInternal;
2235 This->glDescription.glType = formatEntry->glType;
2237 if (format != WINED3DFMT_UNKNOWN) {
2238 This->bytesPerPixel = formatEntry->bpp;
2240 This->bytesPerPixel = 0;
2243 This->Flags |= (WINED3DFMT_D16_LOCKABLE == format) ? SFLAG_LOCKABLE : 0;
2245 This->resource.format = format;
2247 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);
2252 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2253 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2255 /* Render targets depend on their hdc, and we can't create a hdc on a user pointer */
2256 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
2257 ERR("Not supported on render targets\n");
2258 return WINED3DERR_INVALIDCALL;
2261 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2262 WARN("Surface is locked or the HDC is in use\n");
2263 return WINED3DERR_INVALIDCALL;
2266 if(Mem && Mem != This->resource.allocatedMemory) {
2267 void *release = NULL;
2269 /* Do I have to copy the old surface content? */
2270 if(This->Flags & SFLAG_DIBSECTION) {
2271 /* Release the DC. No need to hold the critical section for the update
2272 * Thread because this thread runs only on front buffers, but this method
2273 * fails for render targets in the check above.
2275 SelectObject(This->hDC, This->dib.holdbitmap);
2276 DeleteDC(This->hDC);
2277 /* Release the DIB section */
2278 DeleteObject(This->dib.DIBsection);
2279 This->dib.bitmap_data = NULL;
2280 This->resource.allocatedMemory = NULL;
2282 This->Flags &= ~SFLAG_DIBSECTION;
2283 } else if(!(This->Flags & SFLAG_USERPTR)) {
2284 release = This->resource.allocatedMemory;
2286 This->resource.allocatedMemory = Mem;
2287 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2289 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2290 This->Flags &= ~(SFLAG_INDRAWABLE | SFLAG_INTEXTURE);
2292 /* For client textures opengl has to be notified */
2293 if(This->Flags & SFLAG_CLIENT) {
2294 IWineD3DSurface_PreLoad(iface);
2295 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2298 /* Now free the old memory if any */
2299 HeapFree(GetProcessHeap(), 0, release);
2300 } else if(This->Flags & SFLAG_USERPTR) {
2301 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2302 This->resource.allocatedMemory = NULL;
2303 This->Flags &= ~SFLAG_USERPTR;
2305 if(This->Flags & SFLAG_CLIENT) {
2306 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2307 IWineD3DSurface_PreLoad(iface);
2313 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2314 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2315 IWineD3DDevice *D3D = (IWineD3DDevice *) This->resource.wineD3DDevice;
2316 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2318 /* Flipping is only supported on RenderTargets */
2319 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2322 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2323 * FIXME("(%p) Target override is not supported by now\n", This);
2324 * Additionally, it isn't really possible to support triple-buffering
2325 * properly on opengl at all
2329 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2330 return IWineD3DDevice_Present(D3D, NULL, NULL, 0, NULL);
2333 /* Does a direct frame buffer -> texture copy. Stretching is done
2334 * with single pixel copy calls
2336 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2337 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2340 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2344 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2346 /* Bind the target texture */
2347 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
2348 checkGLcall("glBindTexture");
2350 glReadBuffer(myDevice->offscreenBuffer);
2352 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2353 glReadBuffer(buffer);
2355 checkGLcall("glReadBuffer");
2357 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2358 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2360 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2361 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2363 if(Filter != WINED3DTEXF_NONE) {
2364 ERR("Texture filtering not supported in direct blit\n");
2366 } else if((Filter != WINED3DTEXF_NONE) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2367 ERR("Texture filtering not supported in direct blit\n");
2371 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2372 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2373 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2375 glCopyTexSubImage2D(This->glDescription.target,
2376 This->glDescription.level,
2377 drect->x1, drect->y1, /* xoffset, yoffset */
2378 srect->x1, Src->currentDesc.Height - srect->y2,
2379 drect->x2 - drect->x1, drect->y2 - drect->y1);
2381 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2382 /* I have to process this row by row to swap the image,
2383 * otherwise it would be upside down, so streching in y direction
2384 * doesn't cost extra time
2386 * However, streching in x direction can be avoided if not necessary
2388 for(row = drect->y1; row < drect->y2; row++) {
2389 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2390 /* Well, that stuff works, but it's very slow.
2391 * find a better way instead
2395 for(col = drect->x1; col < drect->x2; col++) {
2396 glCopyTexSubImage2D(This->glDescription.target,
2397 This->glDescription.level,
2398 drect->x1 + col, row, /* xoffset, yoffset */
2399 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2403 glCopyTexSubImage2D(This->glDescription.target,
2404 This->glDescription.level,
2405 drect->x1, row, /* xoffset, yoffset */
2406 srect->x1, yoffset - (int) (row * yrel),
2407 drect->x2-drect->x1, 1);
2412 vcheckGLcall("glCopyTexSubImage2D");
2416 /* Uses the hardware to stretch and flip the image */
2417 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2418 GLuint src, backup = 0;
2419 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2420 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2421 float left, right, top, bottom; /* Texture coordinates */
2422 UINT fbwidth = Src->currentDesc.Width;
2423 UINT fbheight = Src->currentDesc.Height;
2424 GLenum drawBuffer = GL_BACK;
2426 TRACE("Using hwstretch blit\n");
2427 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2429 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2431 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2432 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2434 if(GL_LIMITS(aux_buffers) >= 2) {
2435 /* Got more than one aux buffer? Use the 2nd aux buffer */
2436 drawBuffer = GL_AUX1;
2437 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2438 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2439 drawBuffer = GL_AUX0;
2442 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2443 glGenTextures(1, &backup);
2444 checkGLcall("glGenTextures\n");
2445 glBindTexture(GL_TEXTURE_2D, backup);
2446 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2448 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2449 * we are reading from the back buffer, the backup can be used as source texture
2451 if(Src->glDescription.textureName == 0) {
2452 /* Get it a description */
2453 IWineD3DSurface_PreLoad(SrcSurface);
2455 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
2456 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2458 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2459 Src->Flags &= ~SFLAG_INTEXTURE;
2462 glReadBuffer(GL_BACK);
2463 checkGLcall("glReadBuffer(GL_BACK)");
2465 /* TODO: Only back up the part that will be overwritten */
2466 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2467 0, 0 /* read offsets */,
2472 checkGLcall("glCopyTexSubImage2D");
2474 /* No issue with overriding these - the sampler is dirty due to blit usage */
2475 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
2476 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2477 checkGLcall("glTexParameteri");
2478 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
2479 minMipLookup[Filter][WINED3DTEXF_NONE]);
2480 checkGLcall("glTexParameteri");
2482 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2483 src = backup ? backup : Src->glDescription.textureName;
2485 glReadBuffer(GL_FRONT);
2486 checkGLcall("glReadBuffer(GL_FRONT)");
2488 glGenTextures(1, &src);
2489 checkGLcall("glGenTextures(1, &src)");
2490 glBindTexture(GL_TEXTURE_2D, src);
2491 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2493 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2494 * out for power of 2 sizes
2496 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2497 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2498 checkGLcall("glTexImage2D");
2499 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2500 0, 0 /* read offsets */,
2505 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2506 checkGLcall("glTexParameteri");
2507 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2508 checkGLcall("glTexParameteri");
2510 glReadBuffer(GL_BACK);
2511 checkGLcall("glReadBuffer(GL_BACK)");
2513 checkGLcall("glEnd and previous");
2515 left = (float) srect->x1 / (float) Src->pow2Width;
2516 right = (float) srect->x2 / (float) Src->pow2Width;
2519 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2520 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2522 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2523 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2526 /* draw the source texture stretched and upside down. The correct surface is bound already */
2527 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
2528 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
2530 glDrawBuffer(drawBuffer);
2531 glReadBuffer(drawBuffer);
2535 glTexCoord2f(left, bottom);
2536 glVertex2i(0, fbheight);
2539 glTexCoord2f(left, top);
2540 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2543 glTexCoord2f(right, top);
2544 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2547 glTexCoord2f(right, bottom);
2548 glVertex2i(drect->x2 - drect->x1, fbheight);
2550 checkGLcall("glEnd and previous");
2552 /* Now read the stretched and upside down image into the destination texture */
2553 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2554 checkGLcall("glBindTexture");
2555 glCopyTexSubImage2D(This->glDescription.target,
2557 drect->x1, drect->y1, /* xoffset, yoffset */
2558 0, 0, /* We blitted the image to the origin */
2559 drect->x2 - drect->x1, drect->y2 - drect->y1);
2560 checkGLcall("glCopyTexSubImage2D");
2562 /* Write the back buffer backup back */
2563 glBindTexture(GL_TEXTURE_2D, backup ? backup : Src->glDescription.textureName);
2564 checkGLcall("glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName)");
2566 if(drawBuffer == GL_BACK) {
2569 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2573 glTexCoord2f(0.0, 0.0);
2574 glVertex2i(0, fbheight);
2577 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2578 glVertex2i(fbwidth, Src->currentDesc.Height);
2581 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2582 glVertex2i(fbwidth, 0);
2585 /* Restore the old draw buffer */
2586 glDrawBuffer(GL_BACK);
2590 if(src != Src->glDescription.textureName && src != backup) {
2591 glDeleteTextures(1, &src);
2592 checkGLcall("glDeleteTextures(1, &src)");
2595 glDeleteTextures(1, &backup);
2596 checkGLcall("glDeleteTextures(1, &backup)");
2601 /* Not called from the VTable */
2602 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2604 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2605 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2606 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2609 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2611 /* Get the swapchain. One of the surfaces has to be a primary surface */
2612 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2613 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2615 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2616 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2619 /* Early sort out of cases where no render target is used */
2620 if(!dstSwapchain && !srcSwapchain &&
2621 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2622 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2623 return WINED3DERR_INVALIDCALL;
2626 /* No destination color keying supported */
2627 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
2628 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2629 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2630 return WINED3DERR_INVALIDCALL;
2634 rect.x1 = DestRect->left;
2635 rect.y1 = DestRect->top;
2636 rect.x2 = DestRect->right;
2637 rect.y2 = DestRect->bottom;
2641 rect.x2 = This->currentDesc.Width;
2642 rect.y2 = This->currentDesc.Height;
2645 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2646 if(dstSwapchain && dstSwapchain == srcSwapchain) {
2647 /* Half-life does a Blt from the back buffer to the front buffer,
2648 * Full surface size, no flags... Use present instead
2651 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2653 if( (SrcRect->left == 0) && (SrcRect->top == 0) &&
2654 (SrcRect->right == Src->currentDesc.Width) && (SrcRect->bottom == Src->currentDesc.Height) ) {
2661 /* Check the Destination rect and the surface sizes */
2663 (rect.x1 == 0) && (rect.y1 == 0) &&
2664 (rect.x2 == This->currentDesc.Width) && (rect.y2 == This->currentDesc.Height) &&
2665 (This->currentDesc.Width == Src->currentDesc.Width) &&
2666 (This->currentDesc.Height == Src->currentDesc.Height)) {
2667 /* These flags are unimportant for the flag check, remove them */
2669 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
2670 if( dstSwapchain->backBuffer && ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) &&
2671 SrcSurface == dstSwapchain->backBuffer[0] ) {
2673 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
2675 /* The idea behind this is that a glReadPixels and a glDrawPixels call
2676 * take very long, while a flip is fast.
2677 * This applies to Half-Life, which does such Blts every time it finished
2678 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
2679 * menu. This is also used by all apps when they do windowed rendering
2681 * The problem is that flipping is not really the same as copying. After a
2682 * Blt the front buffer is a copy of the back buffer, and the back buffer is
2683 * untouched. Therefore it's necessary to override the swap effect
2684 * and to set it back after the flip.
2687 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
2689 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
2690 IWineD3DDevice_Present((IWineD3DDevice *) This->resource.wineD3DDevice,
2691 NULL, NULL, 0, NULL);
2693 dstSwapchain->presentParms.SwapEffect = orig_swap;
2700 TRACE("Unsupported blit between buffers on the same swapchain\n");
2701 return WINED3DERR_INVALIDCALL;
2702 } else if((dstSwapchain || This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) &&
2703 (srcSwapchain || SrcSurface == myDevice->render_targets[0]) ) {
2704 ERR("Can't perform hardware blit between 2 different swapchains, falling back to software\n");
2705 return WINED3DERR_INVALIDCALL;
2708 if(srcSwapchain || SrcSurface == myDevice->render_targets[0]) {
2709 /* Blit from render target to texture */
2711 BOOL upsideDown, stretchx;
2713 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
2714 TRACE("Color keying not supported by frame buffer to texture blit\n");
2715 return WINED3DERR_INVALIDCALL;
2716 /* Destination color key is checked above */
2719 /* Call preload for the surface to make sure it isn't dirty */
2720 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
2721 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
2722 checkGLcall("glActiveTextureARB");
2724 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(0));
2725 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2727 /* Make sure that the top pixel is always above the bottom pixel, and keep a seperate upside down flag
2728 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2731 if(SrcRect->top < SrcRect->bottom) {
2732 srect.y1 = SrcRect->top;
2733 srect.y2 = SrcRect->bottom;
2736 srect.y1 = SrcRect->bottom;
2737 srect.y2 = SrcRect->top;
2740 srect.x1 = SrcRect->left;
2741 srect.x2 = SrcRect->right;
2745 srect.x2 = Src->currentDesc.Width;
2746 srect.y2 = Src->currentDesc.Height;
2749 if(rect.x1 > rect.x2) {
2753 upsideDown = !upsideDown;
2756 TRACE("Reading from an offscreen target\n");
2757 upsideDown = !upsideDown;
2760 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
2766 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2767 * flip the image nor scale it.
2769 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2770 * -> If the app wants a image width an unscaled width, copy it line per line
2771 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
2772 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2773 * back buffer. This is slower than reading line per line, thus not used for flipping
2774 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2777 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
2778 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
2781 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
2782 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
2783 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
2784 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
2785 rect.y2 - rect.y1 > Src->currentDesc.Height) {
2786 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
2787 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
2789 TRACE("Using hardware stretching to flip / stretch the texture\n");
2790 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
2793 if(!(This->Flags & SFLAG_DONOTFREE)) {
2794 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
2795 This->resource.allocatedMemory = NULL;
2797 This->Flags &= ~SFLAG_INSYSMEM;
2799 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
2800 * path is never entered
2802 This->Flags |= SFLAG_INTEXTURE;
2806 /* Blit from offscreen surface to render target */
2807 float glTexCoord[4];
2808 DWORD oldCKeyFlags = Src->CKeyFlags;
2809 WINEDDCOLORKEY oldBltCKey = This->SrcBltCKey;
2810 RECT SourceRectangle;
2812 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
2815 SourceRectangle.left = SrcRect->left;
2816 SourceRectangle.right = SrcRect->right;
2817 SourceRectangle.top = SrcRect->top;
2818 SourceRectangle.bottom = SrcRect->bottom;
2820 SourceRectangle.left = 0;
2821 SourceRectangle.right = Src->currentDesc.Width;
2822 SourceRectangle.top = 0;
2823 SourceRectangle.bottom = Src->currentDesc.Height;
2826 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
2827 /* Fall back to software */
2828 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
2829 SourceRectangle.left, SourceRectangle.top,
2830 SourceRectangle.right, SourceRectangle.bottom);
2831 return WINED3DERR_INVALIDCALL;
2834 /* Color keying: Check if we have to do a color keyed blt,
2835 * and if not check if a color key is activated.
2837 * Just modify the color keying parameters in the surface and restore them afterwards
2838 * The surface keeps track of the color key last used to load the opengl surface.
2839 * PreLoad will catch the change to the flags and color key and reload if neccessary.
2841 if(Flags & WINEDDBLT_KEYSRC) {
2842 /* Use color key from surface */
2843 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
2844 /* Use color key from DDBltFx */
2845 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
2846 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
2848 /* Do not use color key */
2849 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
2852 /* Now load the surface */
2853 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
2857 /* Activate the destination context, set it up for blitting */
2858 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
2861 TRACE("Drawing to offscreen buffer\n");
2862 glDrawBuffer(myDevice->offscreenBuffer);
2864 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
2865 TRACE("Drawing to %#x buffer\n", buffer);
2866 glDrawBuffer(buffer);
2867 checkGLcall("glDrawBuffer");
2870 /* Bind the texture */
2871 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
2872 checkGLcall("glBindTexture");
2874 /* Filtering for StretchRect */
2875 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
2876 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2877 checkGLcall("glTexParameteri");
2878 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
2879 minMipLookup[Filter][WINED3DTEXF_NONE]);
2880 checkGLcall("glTexParameteri");
2881 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
2882 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
2883 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2884 checkGLcall("glTexEnvi");
2886 /* This is for color keying */
2887 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
2888 glEnable(GL_ALPHA_TEST);
2889 checkGLcall("glEnable GL_ALPHA_TEST");
2890 glAlphaFunc(GL_NOTEQUAL, 0.0);
2891 checkGLcall("glAlphaFunc\n");
2893 glDisable(GL_ALPHA_TEST);
2894 checkGLcall("glDisable GL_ALPHA_TEST");
2897 /* Draw a textured quad
2901 glColor3d(1.0f, 1.0f, 1.0f);
2902 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
2907 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
2908 glVertex3f(rect.x1, rect.y2, 0.0);
2910 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
2915 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
2920 checkGLcall("glEnd");
2922 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
2923 glDisable(GL_ALPHA_TEST);
2924 checkGLcall("glDisable(GL_ALPHA_TEST)");
2927 /* Unbind the texture */
2928 glBindTexture(GL_TEXTURE_2D, 0);
2929 checkGLcall("glEnable glBindTexture");
2931 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
2932 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
2934 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
2935 glDrawBuffer(GL_BACK);
2937 /* Restore the color key parameters */
2938 Src->CKeyFlags = oldCKeyFlags;
2939 This->SrcBltCKey = oldBltCKey;
2943 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
2944 This->Flags &= ~SFLAG_INSYSMEM;
2945 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
2948 if(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO) {
2949 This->Flags |= SFLAG_INDRAWABLE;
2950 This->Flags &= ~SFLAG_INTEXTURE;
2952 This->Flags |= SFLAG_INTEXTURE;
2957 /* Source-Less Blit to render target */
2958 if (Flags & WINEDDBLT_COLORFILL) {
2959 /* This is easy to handle for the D3D Device... */
2962 TRACE("Colorfill\n");
2964 /* The color as given in the Blt function is in the format of the frame-buffer...
2965 * 'clear' expect it in ARGB format => we need to do some conversion :-)
2967 if (This->resource.format == WINED3DFMT_P8) {
2968 if (This->palette) {
2969 color = ((0xFF000000) |
2970 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
2971 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
2972 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
2977 else if (This->resource.format == WINED3DFMT_R5G6B5) {
2978 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
2981 color = ((0xFF000000) |
2982 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
2983 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
2984 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
2987 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
2988 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
2989 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
2991 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
2992 color = DDBltFx->u5.dwFillColor;
2995 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
2996 return WINED3DERR_INVALIDCALL;
2999 TRACE("Calling GetSwapChain with mydevice = %p\n", myDevice);
3000 if(dstSwapchain && dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]) {
3001 glDrawBuffer(GL_BACK);
3002 checkGLcall("glDrawBuffer(GL_BACK)");
3003 } else if (dstSwapchain && This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer) {
3004 glDrawBuffer(GL_FRONT);
3005 checkGLcall("glDrawBuffer(GL_FRONT)");
3006 } else if(This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3007 glDrawBuffer(myDevice->offscreenBuffer);
3008 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer3)");
3010 TRACE("Surface is higher back buffer, falling back to software\n");
3011 return WINED3DERR_INVALIDCALL;
3014 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3016 IWineD3DDevice_Clear( (IWineD3DDevice *) myDevice,
3017 1 /* Number of rectangles */,
3019 WINED3DCLEAR_TARGET,
3024 /* Restore the original draw buffer */
3026 glDrawBuffer(myDevice->offscreenBuffer);
3027 } else if(dstSwapchain->backBuffer && dstSwapchain->backBuffer[0]) {
3028 glDrawBuffer(GL_BACK);
3030 vcheckGLcall("glDrawBuffer");
3036 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3037 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3038 return WINED3DERR_INVALIDCALL;
3041 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3042 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3043 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3044 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3045 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3046 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3048 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair */
3049 if(myDevice->inScene &&
3050 (iface == myDevice->stencilBufferTarget ||
3051 (SrcSurface && SrcSurface == myDevice->stencilBufferTarget))) {
3052 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3053 return WINED3DERR_INVALIDCALL;
3056 /* Special cases for RenderTargets */
3057 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3058 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3059 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3062 /* For the rest call the X11 surface implementation.
3063 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3064 * other Blts are rather rare
3066 return IWineGDISurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3069 HRESULT WINAPI IWineD3DSurfaceImpl_GetBltStatus(IWineD3DSurface *iface, DWORD Flags) {
3070 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3071 TRACE("(%p)->(%x)\n", This, Flags);
3075 case WINEDDGBS_CANBLT:
3076 case WINEDDGBS_ISBLTDONE:
3080 return WINED3DERR_INVALIDCALL;
3084 HRESULT WINAPI IWineD3DSurfaceImpl_GetFlipStatus(IWineD3DSurface *iface, DWORD Flags) {
3085 /* XXX: DDERR_INVALIDSURFACETYPE */
3087 TRACE("(%p)->(%08x)\n",iface,Flags);
3089 case WINEDDGFS_CANFLIP:
3090 case WINEDDGFS_ISFLIPDONE:
3094 return WINED3DERR_INVALIDCALL;
3098 HRESULT WINAPI IWineD3DSurfaceImpl_IsLost(IWineD3DSurface *iface) {
3099 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3100 TRACE("(%p)\n", This);
3102 /* D3D8 and 9 loose full devices, ddraw only surfaces */
3103 return This->Flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
3106 HRESULT WINAPI IWineD3DSurfaceImpl_Restore(IWineD3DSurface *iface) {
3107 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3108 TRACE("(%p)\n", This);
3110 /* So far we don't lose anything :) */
3111 This->Flags &= ~SFLAG_LOST;
3115 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3116 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3117 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3118 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3119 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3121 if(myDevice->inScene &&
3122 (iface == myDevice->stencilBufferTarget ||
3123 (Source && Source == myDevice->stencilBufferTarget))) {
3124 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3125 return WINED3DERR_INVALIDCALL;
3128 /* Special cases for RenderTargets */
3129 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3130 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3132 RECT SrcRect, DstRect;
3136 SrcRect.left = rsrc->left;
3137 SrcRect.top= rsrc->top;
3138 SrcRect.bottom = rsrc->bottom;
3139 SrcRect.right = rsrc->right;
3143 SrcRect.right = srcImpl->currentDesc.Width;
3144 SrcRect.bottom = srcImpl->currentDesc.Height;
3147 DstRect.left = dstx;
3149 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3150 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3152 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3153 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3154 Flags |= WINEDDBLT_KEYSRC;
3155 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3156 Flags |= WINEDDBLT_KEYDEST;
3157 if(trans & WINEDDBLTFAST_WAIT)
3158 Flags |= WINEDDBLT_WAIT;
3159 if(trans & WINEDDBLTFAST_DONOTWAIT)
3160 Flags |= WINEDDBLT_DONOTWAIT;
3162 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_NONE) == WINED3D_OK) return WINED3D_OK;
3166 return IWineGDISurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3169 HRESULT WINAPI IWineD3DSurfaceImpl_GetPalette(IWineD3DSurface *iface, IWineD3DPalette **Pal) {
3170 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3171 TRACE("(%p)->(%p)\n", This, Pal);
3173 *Pal = (IWineD3DPalette *) This->palette;
3177 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
3178 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3180 IWineD3DPaletteImpl *pal = This->palette;
3182 TRACE("(%p)\n", This);
3184 if(This->resource.format == WINED3DFMT_P8 ||
3185 This->resource.format == WINED3DFMT_A8P8)
3187 if(!This->Flags & SFLAG_INSYSMEM) {
3188 FIXME("Palette changed with surface that does not have an up to date system memory copy\n");
3190 TRACE("Dirtifying surface\n");
3191 This->Flags &= ~(SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3194 if(This->Flags & SFLAG_DIBSECTION) {
3195 TRACE("(%p): Updating the hdc's palette\n", This);
3196 for (n=0; n<256; n++) {
3198 col[n].rgbRed = pal->palents[n].peRed;
3199 col[n].rgbGreen = pal->palents[n].peGreen;
3200 col[n].rgbBlue = pal->palents[n].peBlue;
3202 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3203 /* Use the default device palette */
3204 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
3205 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
3206 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
3208 col[n].rgbReserved = 0;
3210 SetDIBColorTable(This->hDC, 0, 256, col);
3216 HRESULT WINAPI IWineD3DSurfaceImpl_SetPalette(IWineD3DSurface *iface, IWineD3DPalette *Pal) {
3217 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3218 IWineD3DPaletteImpl *PalImpl = (IWineD3DPaletteImpl *) Pal;
3219 TRACE("(%p)->(%p)\n", This, Pal);
3221 if(This->palette != NULL)
3222 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3223 This->palette->Flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
3225 if(PalImpl != NULL) {
3226 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3227 /* Set the device's main palette if the palette
3228 * wasn't a primary palette before
3230 if(!(PalImpl->Flags & WINEDDPCAPS_PRIMARYSURFACE)) {
3231 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3234 for(i=0; i < 256; i++) {
3235 device->palettes[device->currentPalette][i] = PalImpl->palents[i];
3239 (PalImpl)->Flags |= WINEDDPCAPS_PRIMARYSURFACE;
3242 This->palette = PalImpl;
3244 return IWineD3DSurface_RealizePalette(iface);
3247 HRESULT WINAPI IWineD3DSurfaceImpl_SetColorKey(IWineD3DSurface *iface, DWORD Flags, WINEDDCOLORKEY *CKey) {
3248 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3249 TRACE("(%p)->(%08x,%p)\n", This, Flags, CKey);
3251 if ((Flags & WINEDDCKEY_COLORSPACE) != 0) {
3252 FIXME(" colorkey value not supported (%08x) !\n", Flags);
3253 return WINED3DERR_INVALIDCALL;
3256 /* Dirtify the surface, but only if a key was changed */
3258 switch (Flags & ~WINEDDCKEY_COLORSPACE) {
3259 case WINEDDCKEY_DESTBLT:
3260 This->DestBltCKey = *CKey;
3261 This->CKeyFlags |= WINEDDSD_CKDESTBLT;
3264 case WINEDDCKEY_DESTOVERLAY:
3265 This->DestOverlayCKey = *CKey;
3266 This->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3269 case WINEDDCKEY_SRCOVERLAY:
3270 This->SrcOverlayCKey = *CKey;
3271 This->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3274 case WINEDDCKEY_SRCBLT:
3275 This->SrcBltCKey = *CKey;
3276 This->CKeyFlags |= WINEDDSD_CKSRCBLT;
3281 switch (Flags & ~WINEDDCKEY_COLORSPACE) {
3282 case WINEDDCKEY_DESTBLT:
3283 This->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3286 case WINEDDCKEY_DESTOVERLAY:
3287 This->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3290 case WINEDDCKEY_SRCOVERLAY:
3291 This->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3294 case WINEDDCKEY_SRCBLT:
3295 This->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3303 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3304 /** Check against the maximum texture sizes supported by the video card **/
3305 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3307 TRACE("%p\n", This);
3308 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3309 /* one of three options
3310 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)
3311 2: Set the texture to the maxium size (bad idea)
3312 3: WARN and return WINED3DERR_NOTAVAILABLE;
3313 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.
3315 WARN("(%p) Creating an oversized surface\n", This);
3316 This->Flags |= SFLAG_OVERSIZE;
3318 /* This will be initialized on the first blt */
3319 This->glRect.left = 0;
3320 This->glRect.top = 0;
3321 This->glRect.right = 0;
3322 This->glRect.bottom = 0;
3324 /* No oversize, gl rect is the full texture size */
3325 This->Flags &= ~SFLAG_OVERSIZE;
3326 This->glRect.left = 0;
3327 This->glRect.top = 0;
3328 This->glRect.right = This->pow2Width;
3329 This->glRect.bottom = This->pow2Height;
3332 if(GL_SUPPORT(APPLE_CLIENT_STORAGE) && This->resource.allocatedMemory == NULL) {
3333 /* Make sure that memory is allocated from the start if we are going to use GL_APPLE_client_storage.
3334 * Otherwise a glTexImage2D with a NULL pointer may be done, e.g. when blitting or with offscreen render
3335 * targets, thus the client storage wouldn't be used for that texture
3337 This->resource.allocatedMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->resource.size + 4);
3342 DWORD WINAPI IWineD3DSurfaceImpl_GetPitch(IWineD3DSurface *iface) {
3343 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3345 TRACE("(%p)\n", This);
3347 /* DXTn formats don't have exact pitches as they are to the new row of blocks,
3348 where each block is 4x4 pixels, 8 bytes (dxt1) and 16 bytes (dxt2/3/4/5)
3349 ie pitch = (width/4) * bytes per block */
3350 if (This->resource.format == WINED3DFMT_DXT1) /* DXT1 is 8 bytes per block */
3351 ret = ((This->currentDesc.Width + 3) >> 2) << 3;
3352 else if (This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
3353 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) /* DXT2/3/4/5 is 16 bytes per block */
3354 ret = ((This->currentDesc.Width + 3) >> 2) << 4;
3356 ret = This->bytesPerPixel * This->currentDesc.Width; /* Bytes / row */
3357 /* Surfaces are 32 bit aligned */
3358 ret = (ret + SURFACE_ALIGNMENT - 1) & ~(SURFACE_ALIGNMENT - 1);
3360 TRACE("(%p) Returning %d\n", This, ret);
3364 HRESULT WINAPI IWineD3DSurfaceImpl_SetOverlayPosition(IWineD3DSurface *iface, LONG X, LONG Y) {
3365 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3367 FIXME("(%p)->(%d,%d) Stub!\n", This, X, Y);
3369 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3371 TRACE("(%p): Not an overlay surface\n", This);
3372 return WINEDDERR_NOTAOVERLAYSURFACE;
3378 HRESULT WINAPI IWineD3DSurfaceImpl_GetOverlayPosition(IWineD3DSurface *iface, LONG *X, LONG *Y) {
3379 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3381 FIXME("(%p)->(%p,%p) Stub!\n", This, X, Y);
3383 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3385 TRACE("(%p): Not an overlay surface\n", This);
3386 return WINEDDERR_NOTAOVERLAYSURFACE;
3392 HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlayZOrder(IWineD3DSurface *iface, DWORD Flags, IWineD3DSurface *Ref) {
3393 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3394 IWineD3DSurfaceImpl *RefImpl = (IWineD3DSurfaceImpl *) Ref;
3396 FIXME("(%p)->(%08x,%p) Stub!\n", This, Flags, RefImpl);
3398 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3400 TRACE("(%p): Not an overlay surface\n", This);
3401 return WINEDDERR_NOTAOVERLAYSURFACE;
3407 HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlay(IWineD3DSurface *iface, RECT *SrcRect, IWineD3DSurface *DstSurface, RECT *DstRect, DWORD Flags, WINEDDOVERLAYFX *FX) {
3408 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3409 IWineD3DSurfaceImpl *Dst = (IWineD3DSurfaceImpl *) DstSurface;
3410 FIXME("(%p)->(%p, %p, %p, %08x, %p)\n", This, SrcRect, Dst, DstRect, Flags, FX);
3412 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3414 TRACE("(%p): Not an overlay surface\n", This);
3415 return WINEDDERR_NOTAOVERLAYSURFACE;
3421 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
3424 IWineD3DSurfaceImpl_QueryInterface,
3425 IWineD3DSurfaceImpl_AddRef,
3426 IWineD3DSurfaceImpl_Release,
3427 /* IWineD3DResource */
3428 IWineD3DSurfaceImpl_GetParent,
3429 IWineD3DSurfaceImpl_GetDevice,
3430 IWineD3DSurfaceImpl_SetPrivateData,
3431 IWineD3DSurfaceImpl_GetPrivateData,
3432 IWineD3DSurfaceImpl_FreePrivateData,
3433 IWineD3DSurfaceImpl_SetPriority,
3434 IWineD3DSurfaceImpl_GetPriority,
3435 IWineD3DSurfaceImpl_PreLoad,
3436 IWineD3DSurfaceImpl_GetType,
3437 /* IWineD3DSurface */
3438 IWineD3DSurfaceImpl_GetContainer,
3439 IWineD3DSurfaceImpl_GetDesc,
3440 IWineD3DSurfaceImpl_LockRect,
3441 IWineD3DSurfaceImpl_UnlockRect,
3442 IWineD3DSurfaceImpl_GetDC,
3443 IWineD3DSurfaceImpl_ReleaseDC,
3444 IWineD3DSurfaceImpl_Flip,
3445 IWineD3DSurfaceImpl_Blt,
3446 IWineD3DSurfaceImpl_GetBltStatus,
3447 IWineD3DSurfaceImpl_GetFlipStatus,
3448 IWineD3DSurfaceImpl_IsLost,
3449 IWineD3DSurfaceImpl_Restore,
3450 IWineD3DSurfaceImpl_BltFast,
3451 IWineD3DSurfaceImpl_GetPalette,
3452 IWineD3DSurfaceImpl_SetPalette,
3453 IWineD3DSurfaceImpl_RealizePalette,
3454 IWineD3DSurfaceImpl_SetColorKey,
3455 IWineD3DSurfaceImpl_GetPitch,
3456 IWineD3DSurfaceImpl_SetMem,
3457 IWineD3DSurfaceImpl_SetOverlayPosition,
3458 IWineD3DSurfaceImpl_GetOverlayPosition,
3459 IWineD3DSurfaceImpl_UpdateOverlayZOrder,
3460 IWineD3DSurfaceImpl_UpdateOverlay,
3462 IWineD3DSurfaceImpl_AddDirtyRect,
3463 IWineD3DSurfaceImpl_LoadTexture,
3464 IWineD3DSurfaceImpl_SaveSnapshot,
3465 IWineD3DSurfaceImpl_SetContainer,
3466 IWineD3DSurfaceImpl_SetGlTextureDesc,
3467 IWineD3DSurfaceImpl_GetGlDesc,
3468 IWineD3DSurfaceImpl_GetData,
3469 IWineD3DSurfaceImpl_SetFormat,
3470 IWineD3DSurfaceImpl_PrivateSetup