2 * IWineD3DSurface Implementation
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006 Stefan Dösinger for CodeWeavers
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 #include "wine/port.h"
29 #include "wined3d_private.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
32 #define GLINFO_LOCATION ((IWineD3DImpl *)(((IWineD3DDeviceImpl *)This->resource.wineD3DDevice)->wineD3D))->gl_info
51 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf);
53 static void surface_download_data(IWineD3DSurfaceImpl *This) {
54 if (This->resource.format == WINED3DFMT_DXT1 ||
55 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
56 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
57 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
58 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
60 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
61 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
65 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
66 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);
94 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
95 This->glDescription.glType, mem);
96 checkGLcall("glGetTexImage()");
100 if (This->Flags & SFLAG_NONPOW2) {
101 LPBYTE src_data, dst_data;
104 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
105 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
106 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
108 * We're doing this...
110 * instead of boxing the texture :
111 * |<-texture width ->| -->pow2width| /\
112 * |111111111111111111| | |
113 * |222 Texture 222222| boxed empty | texture height
114 * |3333 Data 33333333| | |
115 * |444444444444444444| | \/
116 * ----------------------------------- |
117 * | boxed empty | boxed empty | pow2height
119 * -----------------------------------
122 * we're repacking the data to the expected texture width
124 * |<-texture width ->| -->pow2width| /\
125 * |111111111111111111222222222222222| |
126 * |222333333333333333333444444444444| texture height
130 * | empty | pow2height
132 * -----------------------------------
136 * |<-texture width ->| /\
137 * |111111111111111111|
138 * |222222222222222222|texture height
139 * |333333333333333333|
140 * |444444444444444444| \/
141 * --------------------
143 * this also means that any references to allocatedMemory should work with the data as if were a
144 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
146 * internally the texture is still stored in a boxed format so any references to textureName will
147 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
149 * Performance should not be an issue, because applications normally do not lock the surfaces when
150 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
151 * and doesn't have to be re-read.
154 dst_data = This->resource.allocatedMemory;
155 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
156 for (y = 1 ; y < This->currentDesc.Height; y++) {
157 /* skip the first row */
158 src_data += src_pitch;
159 dst_data += dst_pitch;
160 memcpy(dst_data, src_data, dst_pitch);
163 HeapFree(GetProcessHeap(), 0, mem);
168 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
169 if (This->resource.format == WINED3DFMT_DXT1 ||
170 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
171 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
172 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
173 FIXME("Using DXT1/3/5 without advertized support\n");
175 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
177 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
178 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
179 * function uses glCompressedTexImage2D instead of the SubImage call
181 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, This->glDescription.glFormatInternal,
182 width, height, 0 /* border */, This->resource.size, data));
183 checkGLcall("glCompressedTexSubImage2D");
187 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
189 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
190 checkGLcall("glTexSubImage2D");
195 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
196 TRACE("(%p) : Creating surface (target %#x) level %d, d3d format %s, internal format %#x, width %d, height %d, gl format %#x, gl type=%#x\n", This,
197 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
199 if (This->resource.format == WINED3DFMT_DXT1 ||
200 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
201 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
202 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
203 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
209 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, NULL);
210 checkGLcall("glTexImage2D");
215 /* *******************************************
216 IWineD3DSurface IUnknown parts follow
217 ******************************************* */
218 HRESULT WINAPI IWineD3DSurfaceImpl_QueryInterface(IWineD3DSurface *iface, REFIID riid, LPVOID *ppobj)
220 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
221 /* Warn ,but be nice about things */
222 TRACE("(%p)->(%s,%p)\n", This,debugstr_guid(riid),ppobj);
224 if (IsEqualGUID(riid, &IID_IUnknown)
225 || IsEqualGUID(riid, &IID_IWineD3DBase)
226 || IsEqualGUID(riid, &IID_IWineD3DResource)
227 || IsEqualGUID(riid, &IID_IWineD3DSurface)) {
228 IUnknown_AddRef((IUnknown*)iface);
233 return E_NOINTERFACE;
236 ULONG WINAPI IWineD3DSurfaceImpl_AddRef(IWineD3DSurface *iface) {
237 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
238 ULONG ref = InterlockedIncrement(&This->resource.ref);
239 TRACE("(%p) : AddRef increasing from %d\n", This,ref - 1);
243 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
244 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
245 ULONG ref = InterlockedDecrement(&This->resource.ref);
246 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
248 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) This->resource.wineD3DDevice;
249 TRACE("(%p) : cleaning up\n", This);
251 if(iface == device->lastActiveRenderTarget) {
252 IWineD3DSwapChainImpl *swapchain = device->swapchains ? (IWineD3DSwapChainImpl *) device->swapchains[0] : NULL;
254 TRACE("Last active render target destroyed\n");
255 /* Find a replacement surface for the currently active back buffer. The context manager does not do NULL
256 * checks, so switch to a valid target as long as the currently set surface is still valid. Use the
257 * surface of the implicit swpchain. If that is the same as the destroyed surface the device is destroyed
258 * and the lastActiveRenderTarget member shouldn't matter
261 if(swapchain->backBuffer && swapchain->backBuffer[0] != iface) {
262 TRACE("Activating primary back buffer\n");
263 ActivateContext(device, swapchain->backBuffer[0], CTXUSAGE_RESOURCELOAD);
264 } else if(!swapchain->backBuffer && swapchain->frontBuffer != iface) {
265 /* Single buffering environment */
266 TRACE("Activating primary front buffer\n");
267 ActivateContext(device, swapchain->frontBuffer, CTXUSAGE_RESOURCELOAD);
269 TRACE("Device is being destroyed, setting lastActiveRenderTarget = 0xdeadbabe\n");
270 /* Implicit render target destroyed, that means the device is being destroyed
271 * whatever we set here, it shouldn't matter
273 device->lastActiveRenderTarget = (IWineD3DSurface *) 0xdeadbabe;
276 /* May happen during ddraw uninitialization */
277 TRACE("Render target set, but swapchain does not exist!\n");
278 device->lastActiveRenderTarget = (IWineD3DSurface *) 0xdeadcafe;
282 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
284 TRACE("Deleting texture %d\n", This->glDescription.textureName);
285 glDeleteTextures(1, &This->glDescription.textureName);
289 if(This->Flags & SFLAG_DIBSECTION) {
291 SelectObject(This->hDC, This->dib.holdbitmap);
293 /* Release the DIB section */
294 DeleteObject(This->dib.DIBsection);
295 This->dib.bitmap_data = NULL;
296 This->resource.allocatedMemory = NULL;
298 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
300 HeapFree(GetProcessHeap(), 0, This->palette9);
302 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
303 if(iface == device->ddraw_primary)
304 device->ddraw_primary = NULL;
306 TRACE("(%p) Released\n", This);
307 HeapFree(GetProcessHeap(), 0, This);
313 /* ****************************************************
314 IWineD3DSurface IWineD3DResource parts follow
315 **************************************************** */
316 HRESULT WINAPI IWineD3DSurfaceImpl_GetDevice(IWineD3DSurface *iface, IWineD3DDevice** ppDevice) {
317 return IWineD3DResourceImpl_GetDevice((IWineD3DResource *)iface, ppDevice);
320 HRESULT WINAPI IWineD3DSurfaceImpl_SetPrivateData(IWineD3DSurface *iface, REFGUID refguid, CONST void* pData, DWORD SizeOfData, DWORD Flags) {
321 return IWineD3DResourceImpl_SetPrivateData((IWineD3DResource *)iface, refguid, pData, SizeOfData, Flags);
324 HRESULT WINAPI IWineD3DSurfaceImpl_GetPrivateData(IWineD3DSurface *iface, REFGUID refguid, void* pData, DWORD* pSizeOfData) {
325 return IWineD3DResourceImpl_GetPrivateData((IWineD3DResource *)iface, refguid, pData, pSizeOfData);
328 HRESULT WINAPI IWineD3DSurfaceImpl_FreePrivateData(IWineD3DSurface *iface, REFGUID refguid) {
329 return IWineD3DResourceImpl_FreePrivateData((IWineD3DResource *)iface, refguid);
332 DWORD WINAPI IWineD3DSurfaceImpl_SetPriority(IWineD3DSurface *iface, DWORD PriorityNew) {
333 return IWineD3DResourceImpl_SetPriority((IWineD3DResource *)iface, PriorityNew);
336 DWORD WINAPI IWineD3DSurfaceImpl_GetPriority(IWineD3DSurface *iface) {
337 return IWineD3DResourceImpl_GetPriority((IWineD3DResource *)iface);
340 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
341 /* TODO: re-write the way textures and managed,
342 * use a 'opengl context manager' to manage RenderTarget surfaces
343 ** *********************************************************/
345 /* TODO: check for locks */
346 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
347 IWineD3DBaseTexture *baseTexture = NULL;
348 TRACE("(%p)Checking to see if the container is a base texture\n", This);
349 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
350 TRACE("Passing to conatiner\n");
351 IWineD3DBaseTexture_PreLoad(baseTexture);
352 IWineD3DBaseTexture_Release(baseTexture);
354 TRACE("(%p) : About to load surface\n", This);
356 #if 0 /* TODO: context manager support */
357 IWineD3DContextManager_PushState(This->contextManager, GL_TEXTURE_2D, ENABLED, NOW /* make sure the state is applied now */);
359 glEnable(This->glDescription.target);/* make sure texture support is enabled in this context */
360 if (!This->glDescription.level) {
361 if (!This->glDescription.textureName) {
362 glGenTextures(1, &This->glDescription.textureName);
363 checkGLcall("glGenTextures");
364 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
366 glBindTexture(This->glDescription.target, This->glDescription.textureName);
367 checkGLcall("glBindTexture");
368 IWineD3DSurface_LoadTexture(iface);
369 /* This is where we should be reducing the amount of GLMemoryUsed */
370 } else if (This->glDescription.textureName) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
371 /* assume this is a coding error not a real error for now */
372 FIXME("Mipmap surface has a glTexture bound to it!\n");
374 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
375 /* Tell opengl to try and keep this texture in video ram (well mostly) */
378 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
380 /* TODO: disable texture support, if it wastn't enabled when we entered. */
381 #if 0 /* TODO: context manager support */
382 IWineD3DContextManager_PopState(This->contextManager, GL_TEXTURE_2D, DISABLED,DELAYED
383 /* we don't care when the state is disabled(if atall) */);
390 WINED3DRESOURCETYPE WINAPI IWineD3DSurfaceImpl_GetType(IWineD3DSurface *iface) {
391 TRACE("(%p) : calling resourceimpl_GetType\n", iface);
392 return IWineD3DResourceImpl_GetType((IWineD3DResource *)iface);
395 HRESULT WINAPI IWineD3DSurfaceImpl_GetParent(IWineD3DSurface *iface, IUnknown **pParent) {
396 TRACE("(%p) : calling resourceimpl_GetParent\n", iface);
397 return IWineD3DResourceImpl_GetParent((IWineD3DResource *)iface, pParent);
400 /* ******************************************************
401 IWineD3DSurface IWineD3DSurface parts follow
402 ****************************************************** */
404 HRESULT WINAPI IWineD3DSurfaceImpl_GetContainer(IWineD3DSurface* iface, REFIID riid, void** ppContainer) {
405 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
406 IWineD3DBase *container = 0;
408 TRACE("(This %p, riid %s, ppContainer %p)\n", This, debugstr_guid(riid), ppContainer);
411 ERR("Called without a valid ppContainer.\n");
415 * If the surface is created using CreateImageSurface/CreateOffscreenPlainSurface, CreateRenderTarget,
416 * or CreateDepthStencilSurface, the surface is considered stand alone. In this case,
417 * GetContainer will return the Direct3D device used to create the surface.
419 if (This->container) {
420 container = This->container;
422 container = (IWineD3DBase *)This->resource.wineD3DDevice;
425 TRACE("Relaying to QueryInterface\n");
426 return IUnknown_QueryInterface(container, riid, ppContainer);
429 HRESULT WINAPI IWineD3DSurfaceImpl_GetDesc(IWineD3DSurface *iface, WINED3DSURFACE_DESC *pDesc) {
430 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
432 TRACE("(%p) : copying into %p\n", This, pDesc);
433 if(pDesc->Format != NULL) *(pDesc->Format) = This->resource.format;
434 if(pDesc->Type != NULL) *(pDesc->Type) = This->resource.resourceType;
435 if(pDesc->Usage != NULL) *(pDesc->Usage) = This->resource.usage;
436 if(pDesc->Pool != NULL) *(pDesc->Pool) = This->resource.pool;
437 if(pDesc->Size != NULL) *(pDesc->Size) = This->resource.size; /* dx8 only */
438 if(pDesc->MultiSampleType != NULL) *(pDesc->MultiSampleType) = This->currentDesc.MultiSampleType;
439 if(pDesc->MultiSampleQuality != NULL) *(pDesc->MultiSampleQuality) = This->currentDesc.MultiSampleQuality;
440 if(pDesc->Width != NULL) *(pDesc->Width) = This->currentDesc.Width;
441 if(pDesc->Height != NULL) *(pDesc->Height) = This->currentDesc.Height;
445 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
446 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
447 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
448 if (This->glDescription.textureName == 0 && textureName != 0) {
449 This->Flags &= ~SFLAG_INTEXTURE;
450 IWineD3DSurface_AddDirtyRect(iface, NULL);
452 This->glDescription.textureName = textureName;
453 This->glDescription.target = target;
456 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
457 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
458 TRACE("(%p) : returning %p\n", This, &This->glDescription);
459 *glDescription = &This->glDescription;
462 /* TODO: think about moving this down to resource? */
463 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
464 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
465 /* This should only be called for sysmem textures, it may be a good idea to extend this to all pools at some point in the futture */
466 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
467 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
469 return (CONST void*)(This->resource.allocatedMemory);
472 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch, BOOL srcUpsideDown) {
476 BYTE *row, *top, *bottom;
480 switch(This->resource.format)
484 /* GL can't return palettized data, so read ARGB pixels into a
485 * separate block of memory and convert them into palettized format
486 * in software. Slow, but if the app means to use palettized render
487 * targets and locks it...
489 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
490 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
491 * for the color channels when palettizing the colors.
494 type = GL_UNSIGNED_BYTE;
496 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
498 ERR("Out of memory\n");
501 bpp = This->bytesPerPixel * 3;
507 fmt = This->glDescription.glFormat;
508 type = This->glDescription.glType;
509 bpp = This->bytesPerPixel;
512 glReadPixels(rect->left, rect->top,
513 rect->right - rect->left,
514 rect->bottom - rect->top,
516 vcheckGLcall("glReadPixels");
518 /* TODO: Merge this with the palettization loop below for P8 targets */
522 /* glReadPixels returns the image upside down, and there is no way to prevent this.
523 Flip the lines in software */
524 len = (rect->right - rect->left) * bpp;
525 off = rect->left * bpp;
527 row = HeapAlloc(GetProcessHeap(), 0, len);
529 ERR("Out of memory\n");
530 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
534 top = mem + pitch * rect->top;
535 bottom = ((BYTE *) mem) + pitch * ( rect->bottom - rect->top - 1);
536 for(i = 0; i < (rect->bottom - rect->top) / 2; i++) {
537 memcpy(row, top + off, len);
538 memcpy(top + off, bottom + off, len);
539 memcpy(bottom + off, row, len);
543 HeapFree(GetProcessHeap(), 0, row);
546 if(This->resource.format == WINED3DFMT_P8) {
548 DWORD width = pitch / 3;
551 pal = This->palette->palents;
553 pal = This->resource.wineD3DDevice->palettes[This->resource.wineD3DDevice->currentPalette];
556 for(y = rect->top; y < rect->bottom; y++) {
557 for(x = rect->left; x < rect->right; x++) {
558 /* start lines pixels */
559 BYTE *blue = (BYTE *) ((BYTE *) mem) + y * pitch + x * (sizeof(BYTE) * 3);
560 BYTE *green = blue + 1;
561 BYTE *red = green + 1;
563 for(c = 0; c < 256; c++) {
564 if(*red == pal[c].peRed &&
565 *green == pal[c].peGreen &&
566 *blue == pal[c].peBlue)
568 *((BYTE *) dest + y * width + x) = c;
574 HeapFree(GetProcessHeap(), 0, mem);
578 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
579 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
580 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
581 IWineD3DSwapChainImpl *swapchain = NULL;
583 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
585 if (!(This->Flags & SFLAG_LOCKABLE)) {
586 /* Note: UpdateTextures calls CopyRects which calls this routine to populate the
587 texture regions, and since the destination is an unlockable region we need
589 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
590 /*return WINED3DERR_INVALIDCALL; */
593 pLockedRect->Pitch = IWineD3DSurface_GetPitch(iface);
595 /* Mark the surface locked */
596 This->Flags |= SFLAG_LOCKED;
598 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy */
599 if(!This->resource.allocatedMemory) {
600 This->resource.allocatedMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + 4);
601 This->Flags &= ~SFLAG_INSYSMEM; /* This is the marker that surface data has to be downloaded */
604 /* Calculate the correct start address to report */
606 pLockedRect->pBits = This->resource.allocatedMemory;
607 This->lockedRect.left = 0;
608 This->lockedRect.top = 0;
609 This->lockedRect.right = This->currentDesc.Width;
610 This->lockedRect.bottom = This->currentDesc.Height;
611 TRACE("Locked Rect (%p) = l %d, t %d, r %d, b %d\n", &This->lockedRect, This->lockedRect.left, This->lockedRect.top, This->lockedRect.right, This->lockedRect.bottom);
613 TRACE("Lock Rect (%p) = l %d, t %d, r %d, b %d\n", pRect, pRect->left, pRect->top, pRect->right, pRect->bottom);
615 if ((pRect->top < 0) ||
617 (pRect->left >= pRect->right) ||
618 (pRect->top >= pRect->bottom) ||
619 (pRect->right > This->currentDesc.Width) ||
620 (pRect->bottom > This->currentDesc.Height))
622 WARN(" Invalid values in pRect !!!\n");
623 return WINED3DERR_INVALIDCALL;
626 if (This->resource.format == WINED3DFMT_DXT1) { /* DXT1 is half byte per pixel */
627 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top) + ((pRect->left * This->bytesPerPixel / 2));
629 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top) + (pRect->left * This->bytesPerPixel);
631 This->lockedRect.left = pRect->left;
632 This->lockedRect.top = pRect->top;
633 This->lockedRect.right = pRect->right;
634 This->lockedRect.bottom = pRect->bottom;
637 if (This->Flags & SFLAG_NONPOW2) {
638 TRACE("Locking non-power 2 texture\n");
641 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
642 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
645 if(!(This->Flags & SFLAG_DYNLOCK)) {
647 /* MAXLOCKCOUNT is defined in wined3d_private.h */
648 if(This->lockCount > MAXLOCKCOUNT) {
649 TRACE("Surface is locked regularily, not freeing the system memory copy any more\n");
650 This->Flags |= SFLAG_DYNLOCK;
654 if((Flags & WINED3DLOCK_DISCARD) || (This->Flags & SFLAG_INSYSMEM)) {
655 TRACE("WINED3DLOCK_DISCARD flag passed, or local copy is up to date, not downloading data\n");
659 /* Now download the surface content from opengl
660 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
661 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
663 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
664 if(swapchain || iface == myDevice->render_targets[0]) {
665 BOOL srcIsUpsideDown;
667 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
668 static BOOL warned = FALSE;
670 ERR("The application tries to lock the render target, but render target locking is disabled\n");
673 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
677 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
678 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
679 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
680 * context->last_was_blit set on the unlock.
683 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
685 /* Select the correct read buffer, and give some debug output.
686 * There is no need to keep track of the current read buffer or reset it, every part of the code
687 * that reads sets the read buffer as desired.
690 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
691 * Read from the back buffer
693 TRACE("Locking offscreen render target\n");
694 glReadBuffer(myDevice->offscreenBuffer);
695 srcIsUpsideDown = TRUE;
697 if(iface == swapchain->frontBuffer) {
698 TRACE("Locking the front buffer\n");
699 glReadBuffer(GL_FRONT);
700 } else if(swapchain->backBuffer && iface == swapchain->backBuffer[0]) {
701 TRACE("Locking the back buffer\n");
702 glReadBuffer(GL_BACK);
704 /* Ok, there is an issue: OpenGL does not guarant any back buffer number, so all we can do is to read GL_BACK
705 * and hope it gives what the app wants
707 FIXME("Application is locking a 2nd or higher back buffer\n");
708 glReadBuffer(GL_BACK);
710 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
711 srcIsUpsideDown = FALSE;
714 switch(wined3d_settings.rendertargetlock_mode) {
718 read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
723 read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
724 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
729 /* Mark the local copy up to date if a full download was done */
730 if(This->lockedRect.left == 0 &&
731 This->lockedRect.top == 0 &&
732 This->lockedRect.right == This->currentDesc.Width &&
733 This->lockedRect.bottom == This->currentDesc.Height) {
734 This->Flags |= SFLAG_INSYSMEM;
736 } else if(iface == myDevice->stencilBufferTarget) {
737 /** the depth stencil in openGL has a format of GL_FLOAT
738 * which should be good for WINED3DFMT_D16_LOCKABLE
740 * it is unclear what format the stencil buffer is in except.
741 * 'Each index is converted to fixed point...
742 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
743 * mappings in the table GL_PIXEL_MAP_S_TO_S.
744 * glReadPixels(This->lockedRect.left,
745 * This->lockedRect.bottom - j - 1,
746 * This->lockedRect.right - This->lockedRect.left,
748 * GL_DEPTH_COMPONENT,
750 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
752 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
753 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
754 * none of that is the case the problem is not in this function :-)
755 ********************************************/
756 FIXME("Depth stencil locking not supported yet\n");
758 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
759 TRACE("locking an ordinarary surface\n");
761 /* TODO: Make sure that *any* context is active for this thread. It is not important which context that is,
762 * nor that is has any special setup(CTXUSAGE_LOADRESOURCE is fine), but the code below needs a context.
763 * A context is guaranteed to be there in a single threaded environment, but not with multithreading
765 if (0 != This->glDescription.textureName) {
766 /* Now I have to copy thing bits back */
768 /* Make sure that a proper texture unit is selected, bind the texture and dirtify the sampler to restore the texture on the next draw */
769 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
771 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
772 checkGLcall("glActiveTextureARB");
775 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(0));
776 IWineD3DSurface_PreLoad(iface);
778 surface_download_data(This);
781 /* The local copy is now up to date to the opengl one because a full download was done */
782 This->Flags |= SFLAG_INSYSMEM;
786 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
789 IWineD3DBaseTexture *pBaseTexture;
792 * as seen in msdn docs
794 IWineD3DSurface_AddDirtyRect(iface, &This->lockedRect);
796 /** Dirtify Container if needed */
797 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
798 TRACE("Making container dirty\n");
799 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
800 IWineD3DBaseTexture_Release(pBaseTexture);
802 TRACE("Surface is standalone, no need to dirty the container\n");
806 TRACE("returning memory@%p, pitch(%d) dirtyfied(%d)\n", pLockedRect->pBits, pLockedRect->Pitch,
807 This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
811 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This) {
813 GLint prev_rasterpos[4];
815 BOOL storechanged = FALSE, memory_allocated = FALSE;
819 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
821 glDisable(GL_TEXTURE_2D);
822 vcheckGLcall("glDisable(GL_TEXTURE_2D)");
825 vcheckGLcall("glFlush");
826 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
827 vcheckGLcall("glIntegerv");
828 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
829 vcheckGLcall("glIntegerv");
830 glPixelZoom(1.0, -1.0);
831 vcheckGLcall("glPixelZoom");
833 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
834 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
835 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
837 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
838 vcheckGLcall("glRasterPos2f");
840 /* Some drivers(radeon dri, others?) don't like exceptions during
841 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
842 * after ReleaseDC. Reading it will cause an exception, which x11drv will
843 * catch to put the dib section in InSync mode, which leads to a crash
844 * and a blocked x server on my radeon card.
846 * The following lines read the dib section so it is put in inSync mode
847 * before glDrawPixels is called and the crash is prevented. There won't
848 * be any interfering gdi accesses, because UnlockRect is called from
849 * ReleaseDC, and the app won't use the dc any more afterwards.
851 if(This->Flags & SFLAG_DIBSECTION) {
853 read = This->resource.allocatedMemory[0];
856 switch (This->resource.format) {
857 /* No special care needed */
858 case WINED3DFMT_A4R4G4B4:
859 case WINED3DFMT_R5G6B5:
860 case WINED3DFMT_A1R5G5B5:
861 case WINED3DFMT_R8G8B8:
862 type = This->glDescription.glType;
863 fmt = This->glDescription.glFormat;
864 mem = This->resource.allocatedMemory;
865 bpp = This->bytesPerPixel;
868 case WINED3DFMT_X4R4G4B4:
871 unsigned short *data;
872 data = (unsigned short *)This->resource.allocatedMemory;
873 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
879 type = This->glDescription.glType;
880 fmt = This->glDescription.glFormat;
881 mem = This->resource.allocatedMemory;
882 bpp = This->bytesPerPixel;
886 case WINED3DFMT_X1R5G5B5:
889 unsigned short *data;
890 data = (unsigned short *)This->resource.allocatedMemory;
891 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
897 type = This->glDescription.glType;
898 fmt = This->glDescription.glFormat;
899 mem = This->resource.allocatedMemory;
900 bpp = This->bytesPerPixel;
904 case WINED3DFMT_X8R8G8B8:
906 /* make sure the X byte is set to alpha on, since it
907 could be any random value. This fixes the intro movie in Pirates! */
910 data = (unsigned int *)This->resource.allocatedMemory;
911 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
920 case WINED3DFMT_A8R8G8B8:
922 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
923 vcheckGLcall("glPixelStorei");
925 type = This->glDescription.glType;
926 fmt = This->glDescription.glFormat;
927 mem = This->resource.allocatedMemory;
928 bpp = This->bytesPerPixel;
932 case WINED3DFMT_A2R10G10B10:
934 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
935 vcheckGLcall("glPixelStorei");
937 type = This->glDescription.glType;
938 fmt = This->glDescription.glFormat;
939 mem = This->resource.allocatedMemory;
940 bpp = This->bytesPerPixel;
946 int height = This->glRect.bottom - This->glRect.top;
947 type = GL_UNSIGNED_BYTE;
950 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * sizeof(DWORD));
952 ERR("Out of memory\n");
955 memory_allocated = TRUE;
956 d3dfmt_convert_surface(This->resource.allocatedMemory,
964 bpp = This->bytesPerPixel * 4;
970 FIXME("Unsupported Format %u in locking func\n", This->resource.format);
973 type = This->glDescription.glType;
974 fmt = This->glDescription.glFormat;
975 mem = This->resource.allocatedMemory;
976 bpp = This->bytesPerPixel;
979 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
980 (This->lockedRect.bottom - This->lockedRect.top)-1,
982 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
983 checkGLcall("glDrawPixels");
984 glPixelZoom(1.0,1.0);
985 vcheckGLcall("glPixelZoom");
987 glRasterPos3iv(&prev_rasterpos[0]);
988 vcheckGLcall("glRasterPos3iv");
990 /* Reset to previous pack row length */
991 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
992 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
994 glPixelStorei(GL_PACK_SWAP_BYTES, prev_store);
995 vcheckGLcall("glPixelStorei GL_PACK_SWAP_BYTES");
998 /* Blitting environment requires that 2D texturing is enabled. It was turned off before,
1001 glEnable(GL_TEXTURE_2D);
1002 checkGLcall("glEnable(GL_TEXTURE_2D)");
1004 if(memory_allocated) HeapFree(GetProcessHeap(), 0, mem);
1008 static void flush_to_framebuffer_texture(IWineD3DSurfaceImpl *This) {
1009 float glTexCoord[4];
1011 glTexCoord[0] = (float) This->lockedRect.left / (float) This->pow2Width; /* left */
1012 glTexCoord[1] = (float) This->lockedRect.right / (float) This->pow2Width; /* right */
1013 glTexCoord[2] = (float) This->lockedRect.top / (float) This->pow2Height; /* top */
1014 glTexCoord[3] = (float) This->lockedRect.bottom / (float) This->pow2Height; /* bottom */
1016 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
1020 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
1021 checkGLcall("glEnable glBindTexture");
1023 /* No filtering for blts */
1024 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1025 checkGLcall("glTexParameteri");
1026 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1027 checkGLcall("glTexParameteri");
1029 /* Start drawing a quad */
1032 glColor3d(1.0f, 1.0f, 1.0f);
1033 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
1034 glVertex3f(This->lockedRect.left, This->lockedRect.top, 0.0);
1036 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
1037 glVertex3f(This->lockedRect.left, This->lockedRect.bottom, 0.0);
1039 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
1040 glVertex3d(This->lockedRect.right, This->lockedRect.bottom, 0.0);
1042 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
1043 glVertex3f(This->lockedRect.right, This->lockedRect.top, 0.0);
1046 checkGLcall("glEnd");
1048 /* Unbind the texture */
1049 glBindTexture(GL_TEXTURE_2D, 0);
1050 checkGLcall("glEnable glBindTexture");
1055 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1056 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1057 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1058 IWineD3DSwapChainImpl *swapchain = NULL;
1060 if (!(This->Flags & SFLAG_LOCKED)) {
1061 WARN("trying to Unlock an unlocked surf@%p\n", This);
1062 return WINED3DERR_INVALIDCALL;
1065 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1067 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1068 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1072 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1073 if(swapchain || iface == myDevice->render_targets[0]) {
1074 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1075 static BOOL warned = FALSE;
1077 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1080 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1084 /* Activate the correct context for the render target */
1086 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1089 /* Primary offscreen render target */
1090 TRACE("Offscreen render target\n");
1091 glDrawBuffer(myDevice->offscreenBuffer);
1092 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1094 if(iface == swapchain->frontBuffer) {
1095 TRACE("Onscreen front buffer\n");
1096 glDrawBuffer(GL_FRONT);
1097 checkGLcall("glDrawBuffer(GL_FRONT)");
1098 } else if(iface == swapchain->backBuffer[0]) {
1099 TRACE("Onscreen back buffer\n");
1100 glDrawBuffer(GL_BACK);
1101 checkGLcall("glDrawBuffer(GL_BACK)");
1103 FIXME("Unlocking a higher back buffer\n");
1104 glDrawBuffer(GL_BACK);
1105 checkGLcall("glDrawBuffer(GL_BACK)");
1107 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1110 switch(wined3d_settings.rendertargetlock_mode) {
1114 flush_to_framebuffer_drawpixels(This);
1119 flush_to_framebuffer_texture(This);
1123 glDrawBuffer(myDevice->offscreenBuffer);
1124 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1125 } else if(swapchain->backBuffer) {
1126 glDrawBuffer(GL_BACK);
1127 checkGLcall("glDrawBuffer(GL_BACK)");
1129 glDrawBuffer(GL_FRONT);
1130 checkGLcall("glDrawBuffer(GL_FRONT)");
1134 This->dirtyRect.left = This->currentDesc.Width;
1135 This->dirtyRect.top = This->currentDesc.Height;
1136 This->dirtyRect.right = 0;
1137 This->dirtyRect.bottom = 0;
1138 This->Flags |= SFLAG_INDRAWABLE;
1139 } else if(iface == myDevice->stencilBufferTarget) {
1140 FIXME("Depth Stencil buffer locking is not implemented\n");
1142 /* The rest should be a normal texture */
1143 IWineD3DBaseTextureImpl *impl;
1144 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1145 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1146 * states need resetting
1148 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1149 if(impl->baseTexture.bindCount) {
1150 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1152 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1157 This->Flags &= ~SFLAG_LOCKED;
1158 memset(&This->lockedRect, 0, sizeof(RECT));
1162 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1163 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1164 WINED3DLOCKED_RECT lock;
1171 const PixelFormatDesc *formatEntry = getFormatDescEntry(This->resource.format);
1173 TRACE("(%p)->(%p)\n",This,pHDC);
1175 if(This->Flags & SFLAG_USERPTR) {
1176 ERR("Not supported on surfaces with an application-provided surfaces\n");
1180 /* Give more detailed info for ddraw */
1181 if (This->Flags & SFLAG_DCINUSE)
1182 return DDERR_DCALREADYCREATED;
1184 /* Can't GetDC if the surface is locked */
1185 if (This->Flags & SFLAG_LOCKED)
1186 return WINED3DERR_INVALIDCALL;
1188 memset(&lock, 0, sizeof(lock)); /* To be sure */
1190 /* Create a DIB section if there isn't a hdc yet */
1193 SYSTEM_INFO sysInfo;
1195 switch (This->bytesPerPixel) {
1198 /* Allocate extra space to store the RGB bit masks. */
1199 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
1203 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
1207 /* Allocate extra space for a palette. */
1208 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1209 sizeof(BITMAPINFOHEADER)
1211 * (1 << (This->bytesPerPixel * 8)));
1216 return E_OUTOFMEMORY;
1218 /* Some apps access the surface in via DWORDs, and do not take the necessary care at the end of the
1219 * surface. So we need at least extra 4 bytes at the end of the surface. Check against the page size,
1220 * if the last page used for the surface has at least 4 spare bytes we're safe, otherwise
1221 * add an extra line to the dib section
1223 GetSystemInfo(&sysInfo);
1224 if( ((This->resource.size + 3) % sysInfo.dwPageSize) < 4) {
1226 TRACE("Adding an extra line to the dib section\n");
1229 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1230 b_info->bmiHeader.biWidth = This->currentDesc.Width;
1231 b_info->bmiHeader.biHeight = -This->currentDesc.Height -extraline;
1232 b_info->bmiHeader.biSizeImage = ( This->currentDesc.Height + extraline) * IWineD3DSurface_GetPitch(iface);
1233 b_info->bmiHeader.biPlanes = 1;
1234 b_info->bmiHeader.biBitCount = This->bytesPerPixel * 8;
1236 b_info->bmiHeader.biXPelsPerMeter = 0;
1237 b_info->bmiHeader.biYPelsPerMeter = 0;
1238 b_info->bmiHeader.biClrUsed = 0;
1239 b_info->bmiHeader.biClrImportant = 0;
1241 /* Get the bit masks */
1242 masks = (DWORD *) &(b_info->bmiColors);
1243 switch (This->resource.format) {
1244 case WINED3DFMT_R8G8B8:
1245 usage = DIB_RGB_COLORS;
1246 b_info->bmiHeader.biCompression = BI_RGB;
1249 case WINED3DFMT_X1R5G5B5:
1250 case WINED3DFMT_A1R5G5B5:
1251 case WINED3DFMT_A4R4G4B4:
1252 case WINED3DFMT_X4R4G4B4:
1253 case WINED3DFMT_R3G3B2:
1254 case WINED3DFMT_A8R3G3B2:
1255 case WINED3DFMT_A2B10G10R10:
1256 case WINED3DFMT_A8B8G8R8:
1257 case WINED3DFMT_X8B8G8R8:
1258 case WINED3DFMT_A2R10G10B10:
1259 case WINED3DFMT_R5G6B5:
1260 case WINED3DFMT_A16B16G16R16:
1262 b_info->bmiHeader.biCompression = BI_BITFIELDS;
1263 masks[0] = formatEntry->redMask;
1264 masks[1] = formatEntry->greenMask;
1265 masks[2] = formatEntry->blueMask;
1269 /* Don't know palette */
1270 b_info->bmiHeader.biCompression = BI_RGB;
1277 HeapFree(GetProcessHeap(), 0, b_info);
1278 return HRESULT_FROM_WIN32(GetLastError());
1281 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);
1282 This->dib.DIBsection = CreateDIBSection(ddc, b_info, usage, &This->dib.bitmap_data, 0 /* Handle */, 0 /* Offset */);
1285 if (!This->dib.DIBsection) {
1286 ERR("CreateDIBSection failed!\n");
1287 HeapFree(GetProcessHeap(), 0, b_info);
1288 return HRESULT_FROM_WIN32(GetLastError());
1291 TRACE("DIBSection at : %p\n", This->dib.bitmap_data);
1293 /* copy the existing surface to the dib section */
1294 if(This->resource.allocatedMemory) {
1295 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, b_info->bmiHeader.biSizeImage);
1296 /* We won't need that any more */
1297 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
1299 /* This is to make LockRect read the gl Texture although memory is allocated */
1300 This->Flags &= ~SFLAG_INSYSMEM;
1303 HeapFree(GetProcessHeap(), 0, b_info);
1305 /* Use the dib section from now on */
1306 This->resource.allocatedMemory = This->dib.bitmap_data;
1308 /* Now allocate a HDC */
1309 This->hDC = CreateCompatibleDC(0);
1310 This->dib.holdbitmap = SelectObject(This->hDC, This->dib.DIBsection);
1311 TRACE("using wined3d palette %p\n", This->palette);
1312 SelectPalette(This->hDC,
1313 This->palette ? This->palette->hpal : 0,
1316 This->Flags |= SFLAG_DIBSECTION;
1319 /* Lock the surface */
1320 hr = IWineD3DSurface_LockRect(iface,
1325 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1326 /* keep the dib section */
1330 if(This->resource.format == WINED3DFMT_P8 ||
1331 This->resource.format == WINED3DFMT_A8P8) {
1334 PALETTEENTRY ent[256];
1336 GetPaletteEntries(This->palette->hpal, 0, 256, ent);
1337 for (n=0; n<256; n++) {
1338 col[n].rgbRed = ent[n].peRed;
1339 col[n].rgbGreen = ent[n].peGreen;
1340 col[n].rgbBlue = ent[n].peBlue;
1341 col[n].rgbReserved = 0;
1344 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1346 for (n=0; n<256; n++) {
1347 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
1348 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
1349 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
1350 col[n].rgbReserved = 0;
1354 SetDIBColorTable(This->hDC, 0, 256, col);
1358 TRACE("returning %p\n",*pHDC);
1359 This->Flags |= SFLAG_DCINUSE;
1364 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1365 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1367 TRACE("(%p)->(%p)\n",This,hDC);
1369 if (!(This->Flags & SFLAG_DCINUSE))
1370 return WINED3DERR_INVALIDCALL;
1372 /* we locked first, so unlock now */
1373 IWineD3DSurface_UnlockRect(iface);
1375 This->Flags &= ~SFLAG_DCINUSE;
1380 /* ******************************************************
1381 IWineD3DSurface Internal (No mapping to directx api) parts follow
1382 ****************************************************** */
1384 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) {
1385 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & DDSD_CKSRCBLT);
1386 const PixelFormatDesc *formatEntry = getFormatDescEntry(This->resource.format);
1388 /* Default values: From the surface */
1389 *format = formatEntry->glFormat;
1390 *internal = formatEntry->glInternal;
1391 *type = formatEntry->glType;
1392 *convert = NO_CONVERSION;
1393 *target_bpp = This->bytesPerPixel;
1395 /* Ok, now look if we have to do any conversion */
1396 switch(This->resource.format) {
1401 /* Use conversion when the paletted texture extension is not available, or when it is available make sure it is used
1402 * for texturing as it won't work for calls like glDraw-/glReadPixels and further also use conversion in case of color keying.
1404 if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) || colorkey_active || (!use_texturing && GL_SUPPORT(EXT_PALETTED_TEXTURE)) ) {
1406 *internal = GL_RGBA;
1407 *type = GL_UNSIGNED_BYTE;
1409 if(colorkey_active) {
1410 *convert = CONVERT_PALETTED_CK;
1412 *convert = CONVERT_PALETTED;
1418 case WINED3DFMT_R3G3B2:
1419 /* **********************
1420 GL_UNSIGNED_BYTE_3_3_2
1421 ********************** */
1422 if (colorkey_active) {
1423 /* This texture format will never be used.. So do not care about color keying
1424 up until the point in time it will be needed :-) */
1425 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1429 case WINED3DFMT_R5G6B5:
1430 if (colorkey_active) {
1431 *convert = CONVERT_CK_565;
1433 *internal = GL_RGBA;
1434 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1438 case WINED3DFMT_R8G8B8:
1439 if (colorkey_active) {
1440 *convert = CONVERT_CK_RGB24;
1442 *internal = GL_RGBA;
1443 *type = GL_UNSIGNED_INT_8_8_8_8;
1448 case WINED3DFMT_X8R8G8B8:
1449 if (colorkey_active) {
1450 *convert = CONVERT_RGB32_888;
1452 *internal = GL_RGBA;
1453 *type = GL_UNSIGNED_INT_8_8_8_8;
1457 case WINED3DFMT_V8U8:
1458 /* TODO: GL_ATI_envmap_bumpmap provides suitable formats.
1459 * use it instead of converting
1460 * Remember to adjust the texbem instruction in the shader
1462 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1463 *convert = CONVERT_V8U8;
1465 *internal = GL_RGB8;
1470 case WINED3DFMT_X8L8V8U8:
1471 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1472 FIXME("Conversion for D3D_X8L8V8U8 not implemented\n");
1474 *internal = GL_RGBA8;
1479 case WINED3DFMT_Q8W8V8U8:
1480 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1481 FIXME("Conversion for D3D_Q8W8V8U8 not implemented\n");
1483 *internal = GL_RGBA8;
1488 case WINED3DFMT_V16U16:
1489 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1490 FIXME("Conversion for D3D_V16U16 not implemented\n");
1491 *format = GL_COLOR_INDEX;
1492 *internal = GL_COLOR_INDEX;
1504 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf) {
1505 BYTE *source, *dest;
1506 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surf);
1511 memcpy(dst, src, pitch * height);
1514 case CONVERT_PALETTED:
1515 case CONVERT_PALETTED_CK:
1517 IWineD3DPaletteImpl* pal = surf->palette;
1523 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1527 /* Still no palette? Use the device's palette */
1528 /* Get the surface's palette */
1529 for (i = 0; i < 256; i++) {
1530 IWineD3DDeviceImpl *device = surf->resource.wineD3DDevice;
1532 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1533 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1534 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1535 if ((convert == CONVERT_PALETTED_CK) &&
1536 (i >= surf->SrcBltCKey.dwColorSpaceLowValue) &&
1537 (i <= surf->SrcBltCKey.dwColorSpaceHighValue)) {
1538 /* We should maybe here put a more 'neutral' color than the standard bright purple
1539 one often used by application to prevent the nice purple borders when bi-linear
1547 TRACE("Using surface palette %p\n", pal);
1548 /* Get the surface's palette */
1549 for (i = 0; i < 256; i++) {
1550 table[i][0] = pal->palents[i].peRed;
1551 table[i][1] = pal->palents[i].peGreen;
1552 table[i][2] = pal->palents[i].peBlue;
1553 if ((convert == CONVERT_PALETTED_CK) &&
1554 (i >= surf->SrcBltCKey.dwColorSpaceLowValue) &&
1555 (i <= surf->SrcBltCKey.dwColorSpaceHighValue)) {
1556 /* We should maybe here put a more 'neutral' color than the standard bright purple
1557 one often used by application to prevent the nice purple borders when bi-linear
1566 for (y = 0; y < height; y++)
1568 source = src + pitch * y;
1569 dest = dst + outpitch * y;
1570 /* This is an 1 bpp format, using the width here is fine */
1571 for (x = 0; x < width; x++) {
1572 BYTE color = *source++;
1573 *dest++ = table[color][0];
1574 *dest++ = table[color][1];
1575 *dest++ = table[color][2];
1576 *dest++ = table[color][3];
1582 case CONVERT_CK_565:
1584 /* Converting the 565 format in 5551 packed to emulate color-keying.
1586 Note : in all these conversion, it would be best to average the averaging
1587 pixels to get the color of the pixel that will be color-keyed to
1588 prevent 'color bleeding'. This will be done later on if ever it is
1591 Note2: Nvidia documents say that their driver does not support alpha + color keying
1592 on the same surface and disables color keying in such a case
1598 TRACE("Color keyed 565\n");
1600 for (y = 0; y < height; y++) {
1601 Source = (WORD *) (src + y * pitch);
1602 Dest = (WORD *) (dst + y * outpitch);
1603 for (x = 0; x < width; x++ ) {
1604 WORD color = *Source++;
1605 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1606 if ((color < surf->SrcBltCKey.dwColorSpaceLowValue) ||
1607 (color > surf->SrcBltCKey.dwColorSpaceHighValue)) {
1620 unsigned char *Dest;
1621 for(y = 0; y < height; y++) {
1622 Source = (short *) (src + y * pitch);
1623 Dest = (unsigned char *) (dst + y * outpitch);
1624 for (x = 0; x < width; x++ ) {
1625 long color = (*Source++);
1626 /* B */ Dest[0] = 0xff;
1627 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1628 /* R */ Dest[2] = (color) + 128; /* U */
1636 ERR("Unsupported conversation type %d\n", convert);
1641 /* This function is used in case of 8bit paletted textures to upload the palette.
1642 For now it only supports GL_EXT_paletted_texture extension but support for other
1643 extensions like ARB_fragment_program and ATI_fragment_shaders will be added as well.
1645 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
1646 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1647 IWineD3DPaletteImpl* pal = This->palette;
1652 /* Still no palette? Use the device's palette */
1653 /* Get the surface's palette */
1654 for (i = 0; i < 256; i++) {
1655 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1657 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1658 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1659 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1660 if ((convert == CONVERT_PALETTED_CK) &&
1661 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1662 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1663 /* We should maybe here put a more 'neutral' color than the standard bright purple
1664 one often used by application to prevent the nice purple borders when bi-linear
1672 TRACE("Using surface palette %p\n", pal);
1673 /* Get the surface's palette */
1674 for (i = 0; i < 256; i++) {
1675 table[i][0] = pal->palents[i].peRed;
1676 table[i][1] = pal->palents[i].peGreen;
1677 table[i][2] = pal->palents[i].peBlue;
1678 if ((convert == CONVERT_PALETTED_CK) &&
1679 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1680 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1681 /* We should maybe here put a more 'neutral' color than the standard bright purple
1682 one often used by application to prevent the nice purple borders when bi-linear
1690 GL_EXTCALL(glColorTableEXT(GL_TEXTURE_2D,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
1693 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
1694 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1696 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
1697 /* If a ddraw-style palette is attached assume no d3d9 palette change.
1698 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
1703 if(This->palette9) {
1704 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
1708 This->palette9 = (PALETTEENTRY *) HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
1710 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
1714 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface) {
1715 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1716 GLenum format, internal, type;
1717 CONVERT_TYPES convert;
1719 int width, pitch, outpitch;
1722 if (!(This->Flags & SFLAG_INTEXTURE)) {
1723 TRACE("Reloading because surface is dirty\n");
1724 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
1725 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & DDSD_CKSRCBLT))) ||
1726 /* Reload: vice versa OR */
1727 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & DDSD_CKSRCBLT)) ||
1728 /* Also reload: Color key is active AND the color key has changed */
1729 ((This->CKeyFlags & DDSD_CKSRCBLT) && (
1730 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
1731 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
1732 TRACE("Reloading because of color keying\n");
1733 } else if(palette9_changed(This)) {
1734 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
1736 TRACE("surface is already in texture\n");
1740 This->Flags |= SFLAG_INTEXTURE;
1742 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
1743 * These resources are not bound by device size or format restrictions. Because of this,
1744 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
1745 * However, these resources can always be created, locked, and copied.
1747 if (This->resource.pool == WINED3DPOOL_SCRATCH && !(This->Flags & SFLAG_FORCELOAD) )
1749 FIXME("(%p) Operation not supported for scratch textures\n",This);
1750 return WINED3DERR_INVALIDCALL;
1753 if (This->Flags & SFLAG_INDRAWABLE) {
1754 if (This->glDescription.level != 0)
1755 FIXME("Surface in texture is only supported for level 0\n");
1756 else if (This->resource.format == WINED3DFMT_P8 || This->resource.format == WINED3DFMT_A8P8 ||
1757 This->resource.format == WINED3DFMT_DXT1 || This->resource.format == WINED3DFMT_DXT2 ||
1758 This->resource.format == WINED3DFMT_DXT3 || This->resource.format == WINED3DFMT_DXT4 ||
1759 This->resource.format == WINED3DFMT_DXT5)
1760 FIXME("Format %d not supported\n", This->resource.format);
1765 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1766 vcheckGLcall("glGetIntegerv");
1767 glReadBuffer(This->resource.wineD3DDevice->offscreenBuffer);
1768 vcheckGLcall("glReadBuffer");
1770 glCopyTexImage2D(This->glDescription.target,
1771 This->glDescription.level,
1772 This->glDescription.glFormatInternal,
1775 This->currentDesc.Width,
1776 This->currentDesc.Height,
1779 checkGLcall("glCopyTexImage2D");
1780 glReadBuffer(prevRead);
1781 vcheckGLcall("glReadBuffer");
1785 TRACE("Updated target %d\n", This->glDescription.target);
1789 /* Otherwise: System memory copy must be most up to date */
1791 if(This->CKeyFlags & DDSD_CKSRCBLT) {
1792 This->Flags |= SFLAG_GLCKEY;
1793 This->glCKey = This->SrcBltCKey;
1795 else This->Flags &= ~SFLAG_GLCKEY;
1797 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp);
1799 /* The width is in 'length' not in bytes */
1800 width = This->currentDesc.Width;
1801 pitch = IWineD3DSurface_GetPitch(iface);
1803 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
1804 int height = This->currentDesc.Height;
1806 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
1807 outpitch = width * bpp;
1808 outpitch = (outpitch + SURFACE_ALIGNMENT - 1) & ~(SURFACE_ALIGNMENT - 1);
1810 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
1812 ERR("Out of memory %d, %d!\n", outpitch, height);
1813 return WINED3DERR_OUTOFVIDEOMEMORY;
1815 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
1817 This->Flags |= SFLAG_CONVERTED;
1818 } else if (This->resource.format == WINED3DFMT_P8 && GL_SUPPORT(EXT_PALETTED_TEXTURE)) {
1819 d3dfmt_p8_upload_palette(iface, convert);
1820 This->Flags &= ~SFLAG_CONVERTED;
1821 mem = This->resource.allocatedMemory;
1823 This->Flags &= ~SFLAG_CONVERTED;
1824 mem = This->resource.allocatedMemory;
1827 /* Make sure the correct pitch is used */
1828 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
1830 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
1831 TRACE("non power of two support\n");
1832 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
1834 surface_upload_data(This, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
1837 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
1839 surface_upload_data(This, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
1843 /* Restore the default pitch */
1844 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1846 if (mem != This->resource.allocatedMemory)
1847 HeapFree(GetProcessHeap(), 0, mem);
1851 static unsigned int gen = 0;
1854 if ((gen % 10) == 0) {
1855 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
1856 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
1859 * debugging crash code
1868 if (!(This->Flags & SFLAG_DONOTFREE)) {
1869 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
1870 This->resource.allocatedMemory = NULL;
1871 This->Flags &= ~SFLAG_INSYSMEM;
1879 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
1882 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1883 char *allocatedMemory;
1885 IWineD3DSwapChain *swapChain = NULL;
1887 GLuint tmpTexture = 0;
1890 Textures my not be stored in ->allocatedgMemory and a GlTexture
1891 so we should lock the surface before saving a snapshot, or at least check that
1893 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
1894 by calling GetTexImage and in compressed form by calling
1895 GetCompressedTexImageARB. Queried compressed images can be saved and
1896 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
1897 texture images do not need to be processed by the GL and should
1898 significantly improve texture loading performance relative to uncompressed
1901 /* Setup the width and height to be the internal texture width and height. */
1902 width = This->pow2Width;
1903 height = This->pow2Height;
1904 /* check to see if were a 'virtual' texture e.g. were not a pbuffer of texture were a back buffer*/
1905 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
1907 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
1908 /* if were not a real texture then read the back buffer into a real texture */
1909 /* we don't want to interfere with the back buffer so read the data into a temporary
1910 * texture and then save the data out of the temporary texture
1914 TRACE("(%p) Reading render target into texture\n", This);
1915 glEnable(GL_TEXTURE_2D);
1917 glGenTextures(1, &tmpTexture);
1918 glBindTexture(GL_TEXTURE_2D, tmpTexture);
1920 glTexImage2D(GL_TEXTURE_2D,
1927 GL_UNSIGNED_INT_8_8_8_8_REV,
1930 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1931 vcheckGLcall("glGetIntegerv");
1932 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
1933 vcheckGLcall("glReadBuffer");
1934 glCopyTexImage2D(GL_TEXTURE_2D,
1943 checkGLcall("glCopyTexImage2D");
1944 glReadBuffer(prevRead);
1947 } else { /* bind the real texture, and make sure it up to date */
1948 IWineD3DSurface_PreLoad(iface);
1950 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
1952 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
1953 glGetTexImage(GL_TEXTURE_2D,
1954 This->glDescription.level,
1956 GL_UNSIGNED_INT_8_8_8_8_REV,
1958 checkGLcall("glTexImage2D");
1960 glBindTexture(GL_TEXTURE_2D, 0);
1961 glDeleteTextures(1, &tmpTexture);
1965 f = fopen(filename, "w+");
1967 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
1968 return WINED3DERR_INVALIDCALL;
1970 /* Save the dat out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha chanel*/
1971 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
1986 fwrite(&width,2,1,f);
1988 fwrite(&height,2,1,f);
1993 /* 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*/
1995 textureRow = allocatedMemory + (width * (height - 1) *4);
1997 textureRow = allocatedMemory;
1998 for (y = 0 ; y < height; y++) {
1999 for (i = 0; i < width; i++) {
2000 color = *((DWORD*)textureRow);
2001 fputc((color >> 16) & 0xFF, f); /* B */
2002 fputc((color >> 8) & 0xFF, f); /* G */
2003 fputc((color >> 0) & 0xFF, f); /* R */
2004 fputc((color >> 24) & 0xFF, f); /* A */
2007 /* take two rows of the pointer to the texture memory */
2009 (textureRow-= width << 3);
2012 TRACE("Closing file\n");
2016 IWineD3DSwapChain_Release(swapChain);
2018 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2023 * Slightly inefficient way to handle multiple dirty rects but it works :)
2025 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2026 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2027 IWineD3DBaseTexture *baseTexture = NULL;
2028 This->Flags &= ~(SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
2029 if (NULL != pDirtyRect) {
2030 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2031 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2032 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2033 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2035 This->dirtyRect.left = 0;
2036 This->dirtyRect.top = 0;
2037 This->dirtyRect.right = This->currentDesc.Width;
2038 This->dirtyRect.bottom = This->currentDesc.Height;
2040 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2041 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2042 /* if the container is a basetexture then mark it dirty. */
2043 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2044 TRACE("Passing to conatiner\n");
2045 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2046 IWineD3DBaseTexture_Release(baseTexture);
2051 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
2052 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2054 TRACE("This %p, container %p\n", This, container);
2056 /* We can't keep a reference to the container, since the container already keeps a reference to us. */
2058 TRACE("Setting container to %p from %p\n", container, This->container);
2059 This->container = container;
2064 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2065 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2066 const PixelFormatDesc *formatEntry = getFormatDescEntry(format);
2068 if (This->resource.format != WINED3DFMT_UNKNOWN) {
2069 FIXME("(%p) : The foramt of the surface must be WINED3DFORMAT_UNKNOWN\n", This);
2070 return WINED3DERR_INVALIDCALL;
2073 TRACE("(%p) : Setting texture foramt to (%d,%s)\n", This, format, debug_d3dformat(format));
2074 if (format == WINED3DFMT_UNKNOWN) {
2075 This->resource.size = 0;
2076 } else if (format == WINED3DFMT_DXT1) {
2077 /* DXT1 is half byte per pixel */
2078 This->resource.size = ((max(This->pow2Width, 4) * formatEntry->bpp) * max(This->pow2Height, 4)) >> 1;
2080 } else if (format == WINED3DFMT_DXT2 || format == WINED3DFMT_DXT3 ||
2081 format == WINED3DFMT_DXT4 || format == WINED3DFMT_DXT5) {
2082 This->resource.size = ((max(This->pow2Width, 4) * formatEntry->bpp) * max(This->pow2Height, 4));
2084 This->resource.size = ((This->pow2Width * formatEntry->bpp) + SURFACE_ALIGNMENT - 1) & ~(SURFACE_ALIGNMENT - 1);
2085 This->resource.size *= This->pow2Height;
2089 /* Setup some glformat defaults */
2090 This->glDescription.glFormat = formatEntry->glFormat;
2091 This->glDescription.glFormatInternal = formatEntry->glInternal;
2092 This->glDescription.glType = formatEntry->glType;
2094 if (format != WINED3DFMT_UNKNOWN) {
2095 This->bytesPerPixel = formatEntry->bpp;
2097 This->bytesPerPixel = 0;
2100 This->Flags |= (WINED3DFMT_D16_LOCKABLE == format) ? SFLAG_LOCKABLE : 0;
2102 This->resource.format = format;
2104 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);
2109 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2110 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2112 /* Render targets depend on their hdc, and we can't create a hdc on a user pointer */
2113 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
2114 ERR("Not supported on render targets\n");
2115 return WINED3DERR_INVALIDCALL;
2118 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2119 WARN("Surface is locked or the HDC is in use\n");
2120 return WINED3DERR_INVALIDCALL;
2123 if(Mem && Mem != This->resource.allocatedMemory) {
2125 /* Do I have to copy the old surface content? */
2126 if(This->Flags & SFLAG_DIBSECTION) {
2127 /* Release the DC. No need to hold the critical section for the update
2128 * Thread because this thread runs only on front buffers, but this method
2129 * fails for render targets in the check above.
2131 SelectObject(This->hDC, This->dib.holdbitmap);
2132 DeleteDC(This->hDC);
2133 /* Release the DIB section */
2134 DeleteObject(This->dib.DIBsection);
2135 This->dib.bitmap_data = NULL;
2136 This->resource.allocatedMemory = NULL;
2138 This->Flags &= ~SFLAG_DIBSECTION;
2139 } else if(!(This->Flags & SFLAG_USERPTR)) {
2140 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
2142 This->resource.allocatedMemory = Mem;
2143 This->Flags |= SFLAG_USERPTR;
2144 } else if(This->Flags & SFLAG_USERPTR) {
2145 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2146 This->resource.allocatedMemory = NULL;
2147 This->Flags &= ~SFLAG_USERPTR;
2152 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2153 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2154 IWineD3DDevice *D3D = (IWineD3DDevice *) This->resource.wineD3DDevice;
2155 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2157 /* Flipping is only supported on RenderTargets */
2158 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return DDERR_NOTFLIPPABLE;
2161 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2162 * FIXME("(%p) Target override is not supported by now\n", This);
2163 * Additionally, it isn't really possible to support triple-buffering
2164 * properly on opengl at all
2168 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2169 return IWineD3DDevice_Present(D3D, NULL, NULL, 0, NULL);
2172 /* Does a direct frame buffer -> texture copy. Stretching is done
2173 * with single pixel copy calls
2175 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown) {
2176 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2179 BOOL warned = FALSE; /* deliberately not static */
2180 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2184 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2186 /* Bind the target texture */
2187 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
2188 checkGLcall("glBindTexture");
2190 glReadBuffer(myDevice->offscreenBuffer);
2191 } else if(swapchain->backBuffer && SrcSurface == swapchain->backBuffer[0]) {
2192 glReadBuffer(GL_BACK);
2194 glReadBuffer(GL_FRONT);
2196 checkGLcall("glReadBuffer");
2198 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2199 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2201 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2202 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2206 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2207 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2208 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2210 glCopyTexSubImage2D(This->glDescription.target,
2211 This->glDescription.level,
2212 drect->x1, drect->y1, /* xoffset, yoffset */
2213 srect->x1, Src->currentDesc.Height - srect->y2,
2214 drect->x2 - drect->x1, drect->y2 - drect->y1);
2216 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2217 /* I have to process this row by row to swap the image,
2218 * otherwise it would be upside down, so streching in y direction
2219 * doesn't cost extra time
2221 * However, streching in x direction can be avoided if not necessary
2223 for(row = drect->y1; row < drect->y2; row++) {
2224 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2225 /* Well, that stuff works, but it's very slow.
2226 * find a better way instead
2232 FIXME("Doing a pixel by pixel render target -> texture copy, expect performance issues\n");
2235 for(col = drect->x1; col < drect->x2; col++) {
2236 glCopyTexSubImage2D(This->glDescription.target,
2237 This->glDescription.level,
2238 drect->x1 + col, row, /* xoffset, yoffset */
2239 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2243 glCopyTexSubImage2D(This->glDescription.target,
2244 This->glDescription.level,
2245 drect->x1, row, /* xoffset, yoffset */
2246 srect->x1, yoffset - (int) (row * yrel),
2247 drect->x2-drect->x1, 1);
2252 vcheckGLcall("glCopyTexSubImage2D");
2256 /* Uses the hardware to stretch and flip the image */
2257 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown) {
2258 GLuint src, backup = 0;
2259 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2260 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2261 float left, right, top, bottom; /* Texture coordinates */
2262 UINT fbwidth = Src->currentDesc.Width;
2263 UINT fbheight = Src->currentDesc.Height;
2264 GLenum drawBuffer = GL_BACK;
2266 TRACE("Using hwstretch blit\n");
2267 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2269 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2271 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2272 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2274 if(GL_LIMITS(aux_buffers) >= 2) {
2275 /* Got more than one aux buffer? Use the 2nd aux buffer */
2276 drawBuffer = GL_AUX1;
2277 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2278 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2279 drawBuffer = GL_AUX0;
2282 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2283 glGenTextures(1, &backup);
2284 checkGLcall("glGenTextures\n");
2285 glBindTexture(GL_TEXTURE_2D, backup);
2286 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2288 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2289 * we are reading from the back buffer, the backup can be used as source texture
2291 if(Src->glDescription.textureName == 0) {
2292 /* Get it a description */
2293 IWineD3DSurface_PreLoad(SrcSurface);
2295 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
2296 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2298 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2299 Src->Flags &= ~SFLAG_INTEXTURE;
2302 glReadBuffer(GL_BACK);
2303 checkGLcall("glReadBuffer(GL_BACK)");
2305 /* TODO: Only back up the part that will be overwritten */
2306 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2307 0, 0 /* read offsets */,
2312 checkGLcall("glCopyTexSubImage2D");
2314 /* No issue with overriding these - the sampler is dirty due to blit usage */
2315 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2316 checkGLcall("glTexParameteri");
2317 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2318 checkGLcall("glTexParameteri");
2320 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2321 src = backup ? backup : Src->glDescription.textureName;
2323 glReadBuffer(GL_FRONT);
2324 checkGLcall("glReadBuffer(GL_FRONT)");
2326 glGenTextures(1, &src);
2327 checkGLcall("glGenTextures(1, &src)");
2328 glBindTexture(GL_TEXTURE_2D, src);
2329 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2331 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2332 * out for power of 2 sizes
2334 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2335 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2336 checkGLcall("glTexImage2D");
2337 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2338 0, 0 /* read offsets */,
2343 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2344 checkGLcall("glTexParameteri");
2345 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2346 checkGLcall("glTexParameteri");
2348 glReadBuffer(GL_BACK);
2349 checkGLcall("glReadBuffer(GL_BACK)");
2351 checkGLcall("glEnd and previous");
2353 left = (float) srect->x1 / (float) Src->pow2Width;
2354 right = (float) srect->x2 / (float) Src->pow2Width;
2357 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2358 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2360 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2361 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2364 /* draw the source texture stretched and upside down. The correct surface is bound already */
2365 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
2366 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
2368 glDrawBuffer(drawBuffer);
2369 glReadBuffer(drawBuffer);
2373 glTexCoord2f(left, bottom);
2374 glVertex2i(0, fbheight);
2377 glTexCoord2f(left, top);
2378 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2381 glTexCoord2f(right, top);
2382 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2385 glTexCoord2f(right, bottom);
2386 glVertex2i(drect->x2 - drect->x1, fbheight);
2388 checkGLcall("glEnd and previous");
2390 /* Now read the stretched and upside down image into the destination texture */
2391 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2392 checkGLcall("glBindTexture");
2393 glCopyTexSubImage2D(This->glDescription.target,
2395 drect->x1, drect->y1, /* xoffset, yoffset */
2396 0, 0, /* We blitted the image to the origin */
2397 drect->x2 - drect->x1, drect->y2 - drect->y1);
2398 checkGLcall("glCopyTexSubImage2D");
2400 /* Write the back buffer backup back */
2401 glBindTexture(GL_TEXTURE_2D, backup ? backup : Src->glDescription.textureName);
2402 checkGLcall("glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName)");
2404 if(drawBuffer == GL_BACK) {
2407 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2411 glTexCoord2f(0.0, 0.0);
2412 glVertex2i(0, fbheight);
2415 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2416 glVertex2i(fbwidth, Src->currentDesc.Height);
2419 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2420 glVertex2i(fbwidth, 0);
2423 /* Restore the old draw buffer */
2424 glDrawBuffer(GL_BACK);
2428 if(src != Src->glDescription.textureName && src != backup) {
2429 glDeleteTextures(1, &src);
2430 checkGLcall("glDeleteTextures(1, &src)");
2433 glDeleteTextures(1, &backup);
2434 checkGLcall("glDeleteTextures(1, &backup)");
2439 /* Not called from the VTable */
2440 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, DDBLTFX *DDBltFx) {
2442 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2443 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2444 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2447 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2449 /* Get the swapchain. One of the surfaces has to be a primary surface */
2450 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2451 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2453 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2454 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2457 /* Early sort out of cases where no render target is used */
2458 if(!dstSwapchain && !srcSwapchain &&
2459 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2460 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2461 return WINED3DERR_INVALIDCALL;
2464 /* No destination color keying supported */
2465 if(Flags & (DDBLT_KEYDEST | DDBLT_KEYDESTOVERRIDE)) {
2466 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2467 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2468 return WINED3DERR_INVALIDCALL;
2472 rect.x1 = DestRect->left;
2473 rect.y1 = DestRect->top;
2474 rect.x2 = DestRect->right;
2475 rect.y2 = DestRect->bottom;
2479 rect.x2 = This->currentDesc.Width;
2480 rect.y2 = This->currentDesc.Height;
2483 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2484 if(dstSwapchain && dstSwapchain == srcSwapchain) {
2485 /* Half-life does a Blt from the back buffer to the front buffer,
2486 * Full surface size, no flags... Use present instead
2489 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2491 if( (SrcRect->left == 0) && (SrcRect->top == 0) &&
2492 (SrcRect->right == Src->currentDesc.Width) && (SrcRect->bottom == Src->currentDesc.Height) ) {
2499 /* Check the Destination rect and the surface sizes */
2501 (rect.x1 == 0) && (rect.y1 == 0) &&
2502 (rect.x2 == This->currentDesc.Width) && (rect.y2 == This->currentDesc.Height) &&
2503 (This->currentDesc.Width == Src->currentDesc.Width) &&
2504 (This->currentDesc.Height == Src->currentDesc.Height)) {
2505 /* These flags are unimportant for the flag check, remove them */
2507 if((Flags & ~(DDBLT_DONOTWAIT | DDBLT_WAIT)) == 0) {
2508 if( dstSwapchain->backBuffer && ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) &&
2509 SrcSurface == dstSwapchain->backBuffer[0] ) {
2511 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
2513 /* The idea behind this is that a glReadPixels and a glDrawPixels call
2514 * take very long, while a flip is fast.
2515 * This applies to Half-Life, which does such Blts every time it finished
2516 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
2517 * menu. This is also used by all apps when they do windowed rendering
2519 * The problem is that flipping is not really the same as copying. After a
2520 * Blt the front buffer is a copy of the back buffer, and the back buffer is
2521 * untouched. Therefore it's necessary to override the swap effect
2522 * and to set it back after the flip.
2525 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
2527 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
2528 IWineD3DDevice_Present((IWineD3DDevice *) This->resource.wineD3DDevice,
2529 NULL, NULL, 0, NULL);
2531 dstSwapchain->presentParms.SwapEffect = orig_swap;
2538 TRACE("Unsupported blit between buffers on the same swapchain\n");
2539 return WINED3DERR_INVALIDCALL;
2540 } else if((dstSwapchain || This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) &&
2541 (srcSwapchain || SrcSurface == myDevice->render_targets[0]) ) {
2542 ERR("Can't perform hardware blit between 2 different swapchains, falling back to software\n");
2543 return WINED3DERR_INVALIDCALL;
2546 if(srcSwapchain || SrcSurface == myDevice->render_targets[0]) {
2547 /* Blit from render target to texture */
2549 BOOL upsideDown, stretchx;
2551 if(Flags & (DDBLT_KEYSRC | DDBLT_KEYSRCOVERRIDE)) {
2552 TRACE("Color keying not supported by frame buffer to texture blit\n");
2553 return WINED3DERR_INVALIDCALL;
2554 /* Destination color key is checked above */
2557 /* Call preload for the surface to make sure it isn't dirty */
2558 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2560 /* Make sure that the top pixel is always above the bottom pixel, and keep a seperate upside down flag
2561 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2564 if(SrcRect->top < SrcRect->bottom) {
2565 srect.y1 = SrcRect->top;
2566 srect.y2 = SrcRect->bottom;
2569 srect.y1 = SrcRect->bottom;
2570 srect.y2 = SrcRect->top;
2573 srect.x1 = SrcRect->left;
2574 srect.x2 = SrcRect->right;
2578 srect.x2 = Src->currentDesc.Width;
2579 srect.y2 = Src->currentDesc.Height;
2582 if(rect.x1 > rect.x2) {
2586 upsideDown = !upsideDown;
2589 TRACE("Reading from an offscreen target\n");
2590 upsideDown = !upsideDown;
2593 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
2599 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2600 * flip the image nor scale it. If GL_EXT_framebuffer_blit is available it can be used(hopefully,
2601 * not implemented by now). Otherwise:
2603 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2604 * -> If the app wants a image width an unscaled width, copy it line per line
2605 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
2606 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2607 * back buffer. This is slower than reading line per line, thus not used for flipping
2608 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2611 if(FALSE /* GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) */) {
2612 TRACE("Using GL_EXT_framebuffer_blit for copying\n");
2613 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
2614 rect.y2 - rect.y1 > Src->currentDesc.Height) {
2615 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
2616 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown);
2618 TRACE("Using hardware stretching to flip / stretch the texture\n");
2619 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown);
2622 if(!(This->Flags & SFLAG_DONOTFREE)) {
2623 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
2624 This->resource.allocatedMemory = NULL;
2626 This->Flags &= ~SFLAG_INSYSMEM;
2628 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
2629 * path is never entered
2631 This->Flags |= SFLAG_INTEXTURE;
2635 /* Blit from offscreen surface to render target */
2636 float glTexCoord[4];
2637 DWORD oldCKeyFlags = Src->CKeyFlags;
2638 DDCOLORKEY oldBltCKey = This->SrcBltCKey;
2639 RECT SourceRectangle;
2642 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
2645 SourceRectangle.left = SrcRect->left;
2646 SourceRectangle.right = SrcRect->right;
2647 SourceRectangle.top = SrcRect->top;
2648 SourceRectangle.bottom = SrcRect->bottom;
2650 SourceRectangle.left = 0;
2651 SourceRectangle.right = Src->currentDesc.Width;
2652 SourceRectangle.top = 0;
2653 SourceRectangle.bottom = Src->currentDesc.Height;
2656 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
2657 /* Fall back to software */
2658 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
2659 SourceRectangle.left, SourceRectangle.top,
2660 SourceRectangle.right, SourceRectangle.bottom);
2661 return WINED3DERR_INVALIDCALL;
2664 /* Color keying: Check if we have to do a color keyed blt,
2665 * and if not check if a color key is activated.
2667 * Just modify the color keying parameters in the surface and restore them afterwards
2668 * The surface keeps track of the color key last used to load the opengl surface.
2669 * PreLoad will catch the change to the flags and color key and reload if neccessary.
2671 if(Flags & DDBLT_KEYSRC) {
2672 /* Use color key from surface */
2673 } else if(Flags & DDBLT_KEYSRCOVERRIDE) {
2674 /* Use color key from DDBltFx */
2675 Src->CKeyFlags |= DDSD_CKSRCBLT;
2676 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
2678 /* Do not use color key */
2679 Src->CKeyFlags &= ~DDSD_CKSRCBLT;
2682 /* Now load the surface */
2683 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
2687 /* Activate the destination context, set it up for blitting */
2688 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
2690 glGetIntegerv(GL_DRAW_BUFFER, &oldDraw);
2691 if(This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer) {
2692 TRACE("Drawing to front buffer\n");
2693 glDrawBuffer(GL_FRONT);
2694 checkGLcall("glDrawBuffer GL_FRONT");
2697 /* Bind the texture */
2698 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
2699 checkGLcall("glBindTexture");
2701 /* No filtering for blts */
2702 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
2704 checkGLcall("glTexParameteri");
2705 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
2707 checkGLcall("glTexParameteri");
2708 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
2709 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
2710 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2711 checkGLcall("glTexEnvi");
2713 /* This is for color keying */
2714 if(Flags & (DDBLT_KEYSRC | DDBLT_KEYSRCOVERRIDE)) {
2715 glEnable(GL_ALPHA_TEST);
2716 checkGLcall("glEnable GL_ALPHA_TEST");
2717 glAlphaFunc(GL_NOTEQUAL, 0.0);
2718 checkGLcall("glAlphaFunc\n");
2720 glDisable(GL_ALPHA_TEST);
2721 checkGLcall("glDisable GL_ALPHA_TEST");
2724 /* Draw a textured quad
2728 glColor3d(1.0f, 1.0f, 1.0f);
2729 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
2734 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
2735 glVertex3f(rect.x1, rect.y2, 0.0);
2737 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
2742 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
2747 checkGLcall("glEnd");
2749 if(Flags & (DDBLT_KEYSRC | DDBLT_KEYSRCOVERRIDE)) {
2750 glDisable(GL_ALPHA_TEST);
2751 checkGLcall("glDisable(GL_ALPHA_TEST)");
2754 /* Unbind the texture */
2755 glBindTexture(GL_TEXTURE_2D, 0);
2756 checkGLcall("glEnable glBindTexture");
2758 if(This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && oldDraw == GL_BACK) {
2759 glDrawBuffer(oldDraw);
2761 /* Restore the color key parameters */
2762 Src->CKeyFlags = oldCKeyFlags;
2763 This->SrcBltCKey = oldBltCKey;
2767 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
2768 This->Flags &= ~SFLAG_INSYSMEM;
2769 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
2772 if(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO) {
2773 This->Flags |= SFLAG_INDRAWABLE;
2774 This->Flags &= ~SFLAG_INTEXTURE;
2776 This->Flags |= SFLAG_INTEXTURE;
2781 /* Source-Less Blit to render target */
2782 if (Flags & DDBLT_COLORFILL) {
2783 /* This is easy to handle for the D3D Device... */
2786 TRACE("Colorfill\n");
2788 /* The color as given in the Blt function is in the format of the frame-buffer...
2789 * 'clear' expect it in ARGB format => we need to do some conversion :-)
2791 if (This->resource.format == WINED3DFMT_P8) {
2792 if (This->palette) {
2793 color = ((0xFF000000) |
2794 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
2795 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
2796 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
2801 else if (This->resource.format == WINED3DFMT_R5G6B5) {
2802 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
2805 color = ((0xFF000000) |
2806 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
2807 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
2808 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
2811 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
2812 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
2813 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
2815 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
2816 color = DDBltFx->u5.dwFillColor;
2819 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
2820 return WINED3DERR_INVALIDCALL;
2823 TRACE("Calling GetSwapChain with mydevice = %p\n", myDevice);
2824 if(dstSwapchain && dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]) {
2825 glDrawBuffer(GL_BACK);
2826 checkGLcall("glDrawBuffer(GL_BACK)");
2827 } else if (dstSwapchain && This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer) {
2828 glDrawBuffer(GL_FRONT);
2829 checkGLcall("glDrawBuffer(GL_FRONT)");
2830 } else if(This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2831 glDrawBuffer(myDevice->offscreenBuffer);
2832 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer3)");
2834 TRACE("Surface is higher back buffer, falling back to software\n");
2835 return WINED3DERR_INVALIDCALL;
2838 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
2840 IWineD3DDevice_Clear( (IWineD3DDevice *) myDevice,
2841 1 /* Number of rectangles */,
2843 WINED3DCLEAR_TARGET,
2848 /* Restore the original draw buffer */
2850 glDrawBuffer(myDevice->offscreenBuffer);
2851 } else if(dstSwapchain->backBuffer && dstSwapchain->backBuffer[0]) {
2852 glDrawBuffer(GL_BACK);
2854 vcheckGLcall("glDrawBuffer");
2860 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
2861 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
2862 return WINED3DERR_INVALIDCALL;
2865 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, DDBLTFX *DDBltFx) {
2866 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2867 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2868 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2869 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2870 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
2872 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair */
2873 if(myDevice->inScene &&
2874 (iface == myDevice->stencilBufferTarget ||
2875 (SrcSurface && SrcSurface == myDevice->stencilBufferTarget))) {
2876 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
2877 return WINED3DERR_INVALIDCALL;
2880 /* Special cases for RenderTargets */
2881 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
2882 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
2883 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) return WINED3D_OK;
2886 /* For the rest call the X11 surface implementation.
2887 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
2888 * other Blts are rather rare
2890 return IWineGDISurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2893 HRESULT WINAPI IWineD3DSurfaceImpl_GetBltStatus(IWineD3DSurface *iface, DWORD Flags) {
2894 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2895 TRACE("(%p)->(%x)\n", This, Flags);
2900 case DDGBS_ISBLTDONE:
2904 return DDERR_INVALIDPARAMS;
2908 HRESULT WINAPI IWineD3DSurfaceImpl_GetFlipStatus(IWineD3DSurface *iface, DWORD Flags) {
2909 /* XXX: DDERR_INVALIDSURFACETYPE */
2911 TRACE("(%p)->(%08x)\n",iface,Flags);
2914 case DDGFS_ISFLIPDONE:
2918 return DDERR_INVALIDPARAMS;
2922 HRESULT WINAPI IWineD3DSurfaceImpl_IsLost(IWineD3DSurface *iface) {
2923 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2924 TRACE("(%p)\n", This);
2926 return This->Flags & SFLAG_LOST ? DDERR_SURFACELOST : WINED3D_OK;
2929 HRESULT WINAPI IWineD3DSurfaceImpl_Restore(IWineD3DSurface *iface) {
2930 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2931 TRACE("(%p)\n", This);
2933 /* So far we don't lose anything :) */
2934 This->Flags &= ~SFLAG_LOST;
2938 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
2939 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2940 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
2941 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2942 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
2944 if(myDevice->inScene &&
2945 (iface == myDevice->stencilBufferTarget ||
2946 (Source && Source == myDevice->stencilBufferTarget))) {
2947 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
2948 return WINED3DERR_INVALIDCALL;
2951 /* Special cases for RenderTargets */
2952 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
2953 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
2955 RECT SrcRect, DstRect;
2959 SrcRect.left = rsrc->left;
2960 SrcRect.top= rsrc->top;
2961 SrcRect.bottom = rsrc->bottom;
2962 SrcRect.right = rsrc->right;
2966 SrcRect.right = srcImpl->currentDesc.Width;
2967 SrcRect.bottom = srcImpl->currentDesc.Height;
2970 DstRect.left = dstx;
2972 DstRect.right = dstx + SrcRect.right - SrcRect.left;
2973 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
2975 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
2976 if(trans & DDBLTFAST_SRCCOLORKEY)
2977 Flags |= DDBLT_KEYSRC;
2978 if(trans & DDBLTFAST_DESTCOLORKEY)
2979 Flags |= DDBLT_KEYDEST;
2980 if(trans & DDBLTFAST_WAIT)
2981 Flags |= DDBLT_WAIT;
2982 if(trans & DDBLTFAST_DONOTWAIT)
2983 Flags |= DDBLT_DONOTWAIT;
2985 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL) == WINED3D_OK) return WINED3D_OK;
2989 return IWineGDISurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
2992 HRESULT WINAPI IWineD3DSurfaceImpl_GetPalette(IWineD3DSurface *iface, IWineD3DPalette **Pal) {
2993 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2994 TRACE("(%p)->(%p)\n", This, Pal);
2996 *Pal = (IWineD3DPalette *) This->palette;
3000 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
3001 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3003 IWineD3DPaletteImpl *pal = This->palette;
3005 TRACE("(%p)\n", This);
3007 if(This->resource.format == WINED3DFMT_P8 ||
3008 This->resource.format == WINED3DFMT_A8P8)
3010 if(!This->Flags & SFLAG_INSYSMEM) {
3011 FIXME("Palette changed with surface that does not have an up to date system memory copy\n");
3013 TRACE("Dirtifying surface\n");
3014 This->Flags &= ~(SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3017 if(This->Flags & SFLAG_DIBSECTION) {
3018 TRACE("(%p): Updating the hdc's palette\n", This);
3019 for (n=0; n<256; n++) {
3021 col[n].rgbRed = pal->palents[n].peRed;
3022 col[n].rgbGreen = pal->palents[n].peGreen;
3023 col[n].rgbBlue = pal->palents[n].peBlue;
3025 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3026 /* Use the default device palette */
3027 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
3028 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
3029 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
3031 col[n].rgbReserved = 0;
3033 SetDIBColorTable(This->hDC, 0, 256, col);
3039 HRESULT WINAPI IWineD3DSurfaceImpl_SetPalette(IWineD3DSurface *iface, IWineD3DPalette *Pal) {
3040 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3041 IWineD3DPaletteImpl *PalImpl = (IWineD3DPaletteImpl *) Pal;
3042 TRACE("(%p)->(%p)\n", This, Pal);
3044 if(This->palette != NULL)
3045 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3046 This->palette->Flags &= ~DDPCAPS_PRIMARYSURFACE;
3048 if(PalImpl != NULL) {
3049 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3050 /* Set the device's main palette if the palette
3051 * wasn't a primary palette before
3053 if(!(PalImpl->Flags & DDPCAPS_PRIMARYSURFACE)) {
3054 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3057 for(i=0; i < 256; i++) {
3058 device->palettes[device->currentPalette][i] = PalImpl->palents[i];
3062 (PalImpl)->Flags |= DDPCAPS_PRIMARYSURFACE;
3065 This->palette = PalImpl;
3067 return IWineD3DSurface_RealizePalette(iface);
3070 HRESULT WINAPI IWineD3DSurfaceImpl_SetColorKey(IWineD3DSurface *iface, DWORD Flags, DDCOLORKEY *CKey) {
3071 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3072 TRACE("(%p)->(%08x,%p)\n", This, Flags, CKey);
3074 if ((Flags & DDCKEY_COLORSPACE) != 0) {
3075 FIXME(" colorkey value not supported (%08x) !\n", Flags);
3076 return DDERR_INVALIDPARAMS;
3079 /* Dirtify the surface, but only if a key was changed */
3081 switch (Flags & ~DDCKEY_COLORSPACE) {
3082 case DDCKEY_DESTBLT:
3083 This->DestBltCKey = *CKey;
3084 This->CKeyFlags |= DDSD_CKDESTBLT;
3087 case DDCKEY_DESTOVERLAY:
3088 This->DestOverlayCKey = *CKey;
3089 This->CKeyFlags |= DDSD_CKDESTOVERLAY;
3092 case DDCKEY_SRCOVERLAY:
3093 This->SrcOverlayCKey = *CKey;
3094 This->CKeyFlags |= DDSD_CKSRCOVERLAY;
3098 This->SrcBltCKey = *CKey;
3099 This->CKeyFlags |= DDSD_CKSRCBLT;
3104 switch (Flags & ~DDCKEY_COLORSPACE) {
3105 case DDCKEY_DESTBLT:
3106 This->CKeyFlags &= ~DDSD_CKDESTBLT;
3109 case DDCKEY_DESTOVERLAY:
3110 This->CKeyFlags &= ~DDSD_CKDESTOVERLAY;
3113 case DDCKEY_SRCOVERLAY:
3114 This->CKeyFlags &= ~DDSD_CKSRCOVERLAY;
3118 This->CKeyFlags &= ~DDSD_CKSRCBLT;
3126 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3127 /** Check against the maximum texture sizes supported by the video card **/
3128 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3130 TRACE("%p\n", This);
3131 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3132 /* one of three options
3133 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)
3134 2: Set the texture to the maxium size (bad idea)
3135 3: WARN and return WINED3DERR_NOTAVAILABLE;
3136 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.
3138 WARN("(%p) Creating an oversized surface\n", This);
3139 This->Flags |= SFLAG_OVERSIZE;
3141 /* This will be initialized on the first blt */
3142 This->glRect.left = 0;
3143 This->glRect.top = 0;
3144 This->glRect.right = 0;
3145 This->glRect.bottom = 0;
3147 /* No oversize, gl rect is the full texture size */
3148 This->Flags &= ~SFLAG_OVERSIZE;
3149 This->glRect.left = 0;
3150 This->glRect.top = 0;
3151 This->glRect.right = This->pow2Width;
3152 This->glRect.bottom = This->pow2Height;
3158 DWORD WINAPI IWineD3DSurfaceImpl_GetPitch(IWineD3DSurface *iface) {
3159 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3161 TRACE("(%p)\n", This);
3163 /* DXTn formats don't have exact pitches as they are to the new row of blocks,
3164 where each block is 4x4 pixels, 8 bytes (dxt1) and 16 bytes (dxt2/3/4/5)
3165 ie pitch = (width/4) * bytes per block */
3166 if (This->resource.format == WINED3DFMT_DXT1) /* DXT1 is 8 bytes per block */
3167 ret = ((This->currentDesc.Width + 3) >> 2) << 3;
3168 else if (This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
3169 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) /* DXT2/3/4/5 is 16 bytes per block */
3170 ret = ((This->currentDesc.Width + 3) >> 2) << 4;
3172 ret = This->bytesPerPixel * This->currentDesc.Width; /* Bytes / row */
3173 /* Surfaces are 32 bit aligned */
3174 ret = (ret + SURFACE_ALIGNMENT - 1) & ~(SURFACE_ALIGNMENT - 1);
3176 TRACE("(%p) Returning %d\n", This, ret);
3180 HRESULT WINAPI IWineD3DSurfaceImpl_SetOverlayPosition(IWineD3DSurface *iface, LONG X, LONG Y) {
3181 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3183 FIXME("(%p)->(%d,%d) Stub!\n", This, X, Y);
3185 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3187 TRACE("(%p): Not an overlay surface\n", This);
3188 return DDERR_NOTAOVERLAYSURFACE;
3194 HRESULT WINAPI IWineD3DSurfaceImpl_GetOverlayPosition(IWineD3DSurface *iface, LONG *X, LONG *Y) {
3195 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3197 FIXME("(%p)->(%p,%p) Stub!\n", This, X, Y);
3199 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3201 TRACE("(%p): Not an overlay surface\n", This);
3202 return DDERR_NOTAOVERLAYSURFACE;
3208 HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlayZOrder(IWineD3DSurface *iface, DWORD Flags, IWineD3DSurface *Ref) {
3209 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3210 IWineD3DSurfaceImpl *RefImpl = (IWineD3DSurfaceImpl *) Ref;
3212 FIXME("(%p)->(%08x,%p) Stub!\n", This, Flags, RefImpl);
3214 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3216 TRACE("(%p): Not an overlay surface\n", This);
3217 return DDERR_NOTAOVERLAYSURFACE;
3223 HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlay(IWineD3DSurface *iface, RECT *SrcRect, IWineD3DSurface *DstSurface, RECT *DstRect, DWORD Flags, WINEDDOVERLAYFX *FX) {
3224 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3225 IWineD3DSurfaceImpl *Dst = (IWineD3DSurfaceImpl *) DstSurface;
3226 FIXME("(%p)->(%p, %p, %p, %08x, %p)\n", This, SrcRect, Dst, DstRect, Flags, FX);
3228 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3230 TRACE("(%p): Not an overlay surface\n", This);
3231 return DDERR_NOTAOVERLAYSURFACE;
3237 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
3240 IWineD3DSurfaceImpl_QueryInterface,
3241 IWineD3DSurfaceImpl_AddRef,
3242 IWineD3DSurfaceImpl_Release,
3243 /* IWineD3DResource */
3244 IWineD3DSurfaceImpl_GetParent,
3245 IWineD3DSurfaceImpl_GetDevice,
3246 IWineD3DSurfaceImpl_SetPrivateData,
3247 IWineD3DSurfaceImpl_GetPrivateData,
3248 IWineD3DSurfaceImpl_FreePrivateData,
3249 IWineD3DSurfaceImpl_SetPriority,
3250 IWineD3DSurfaceImpl_GetPriority,
3251 IWineD3DSurfaceImpl_PreLoad,
3252 IWineD3DSurfaceImpl_GetType,
3253 /* IWineD3DSurface */
3254 IWineD3DSurfaceImpl_GetContainer,
3255 IWineD3DSurfaceImpl_GetDesc,
3256 IWineD3DSurfaceImpl_LockRect,
3257 IWineD3DSurfaceImpl_UnlockRect,
3258 IWineD3DSurfaceImpl_GetDC,
3259 IWineD3DSurfaceImpl_ReleaseDC,
3260 IWineD3DSurfaceImpl_Flip,
3261 IWineD3DSurfaceImpl_Blt,
3262 IWineD3DSurfaceImpl_GetBltStatus,
3263 IWineD3DSurfaceImpl_GetFlipStatus,
3264 IWineD3DSurfaceImpl_IsLost,
3265 IWineD3DSurfaceImpl_Restore,
3266 IWineD3DSurfaceImpl_BltFast,
3267 IWineD3DSurfaceImpl_GetPalette,
3268 IWineD3DSurfaceImpl_SetPalette,
3269 IWineD3DSurfaceImpl_RealizePalette,
3270 IWineD3DSurfaceImpl_SetColorKey,
3271 IWineD3DSurfaceImpl_GetPitch,
3272 IWineD3DSurfaceImpl_SetMem,
3273 IWineD3DSurfaceImpl_SetOverlayPosition,
3274 IWineD3DSurfaceImpl_GetOverlayPosition,
3275 IWineD3DSurfaceImpl_UpdateOverlayZOrder,
3276 IWineD3DSurfaceImpl_UpdateOverlay,
3278 IWineD3DSurfaceImpl_AddDirtyRect,
3279 IWineD3DSurfaceImpl_LoadTexture,
3280 IWineD3DSurfaceImpl_SaveSnapshot,
3281 IWineD3DSurfaceImpl_SetContainer,
3282 IWineD3DSurfaceImpl_SetGlTextureDesc,
3283 IWineD3DSurfaceImpl_GetGlDesc,
3284 IWineD3DSurfaceImpl_GetData,
3285 IWineD3DSurfaceImpl_SetFormat,
3286 IWineD3DSurfaceImpl_PrivateSetup