quartz: Report the previous refcount of a PullPin object.
[wine/wine-kai.git] / dlls / wined3d / surface.c
blob95b7cc78c552ff028e2240bd61c76584afd784a1
1 /*
2 * IWineD3DSurface Implementation
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006 Stefan Dösinger for CodeWeavers
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
27 #include "config.h"
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
34 typedef enum {
35 NO_CONVERSION,
36 CONVERT_PALETTED,
37 CONVERT_PALETTED_CK,
38 CONVERT_CK_565,
39 CONVERT_CK_5551,
40 CONVERT_CK_4444,
41 CONVERT_CK_4444_ARGB,
42 CONVERT_CK_1555,
43 CONVERT_555,
44 CONVERT_CK_RGB24,
45 CONVERT_CK_8888,
46 CONVERT_CK_8888_ARGB,
47 CONVERT_RGB32_888,
48 CONVERT_V8U8
49 } CONVERT_TYPES;
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);
59 } else {
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);
63 ENTER_GL();
65 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
66 checkGLcall("glGetCompressedTexImageARB()");
68 LEAVE_GL();
70 } else {
71 void *mem;
72 int src_pitch = 0;
73 int dst_pitch = 0;
75 if(This->Flags & SFLAG_CONVERTED) {
76 FIXME("Read back converted textures unsupported\n");
77 return;
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);
85 } else {
86 mem = This->resource.allocatedMemory;
89 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
90 This->glDescription.glFormat, This->glDescription.glType, mem);
92 ENTER_GL();
94 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
95 This->glDescription.glType, mem);
96 checkGLcall("glGetTexImage()");
98 LEAVE_GL();
100 if (This->Flags & SFLAG_NONPOW2) {
101 LPBYTE src_data, dst_data;
102 int y;
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
118 * | | | \/
119 * -----------------------------------
122 * we're repacking the data to the expected texture width
124 * |<-texture width ->| -->pow2width| /\
125 * |111111111111111111222222222222222| |
126 * |222333333333333333333444444444444| texture height
127 * |444444 | |
128 * | | \/
129 * | | |
130 * | empty | pow2height
131 * | | \/
132 * -----------------------------------
134 * == is the same as
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.
153 src_data = mem;
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");
174 } else {
175 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
176 ENTER_GL();
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");
184 LEAVE_GL();
186 } else {
187 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
188 ENTER_GL();
189 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
190 checkGLcall("glTexSubImage2D");
191 LEAVE_GL();
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");
204 return;
207 ENTER_GL();
209 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, NULL);
210 checkGLcall("glTexImage2D");
212 LEAVE_GL();
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);
229 *ppobj = This;
230 return S_OK;
232 *ppobj = NULL;
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);
240 return ref;
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);
247 if (ref == 0) {
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
260 if(swapchain) {
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);
268 } else {
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;
275 } else {
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.. */
283 ENTER_GL();
284 TRACE("Deleting texture %d\n", This->glDescription.textureName);
285 glDeleteTextures(1, &This->glDescription.textureName);
286 LEAVE_GL();
289 if(This->Flags & SFLAG_DIBSECTION) {
290 /* Release the DC */
291 SelectObject(This->hDC, This->dib.holdbitmap);
292 DeleteDC(This->hDC);
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);
310 return ref;
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);
353 } else {
354 TRACE("(%p) : About to load surface\n", This);
355 ENTER_GL();
356 #if 0 /* TODO: context manager support */
357 IWineD3DContextManager_PushState(This->contextManager, GL_TEXTURE_2D, ENABLED, NOW /* make sure the state is applied now */);
358 #endif
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) */
376 GLclampf tmp;
377 tmp = 0.9f;
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) */);
384 #endif
385 LEAVE_GL();
387 return;
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);
410 if (!ppContainer) {
411 ERR("Called without a valid ppContainer.\n");
414 /** From MSDN:
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;
421 } else {
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;
442 return WINED3D_OK;
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_DIRTY;
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) {
473 BYTE *mem;
474 GLint fmt;
475 GLint type;
476 BYTE *row, *top, *bottom;
477 int i;
478 BOOL bpp;
480 switch(This->resource.format)
482 case WINED3DFMT_P8:
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.
493 fmt = GL_RGB;
494 type = GL_UNSIGNED_BYTE;
495 pitch *= 3;
496 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
497 if(!mem) {
498 ERR("Out of memory\n");
499 return;
501 bpp = This->bytesPerPixel * 3;
503 break;
505 default:
506 mem = dest;
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,
515 fmt, type, mem);
516 vcheckGLcall("glReadPixels");
518 /* TODO: Merge this with the palettization loop below for P8 targets */
520 if(!srcUpsideDown) {
521 UINT len, off;
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);
528 if(!row) {
529 ERR("Out of memory\n");
530 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
531 return;
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);
540 top += pitch;
541 bottom -= pitch;
543 HeapFree(GetProcessHeap(), 0, row);
546 if(This->resource.format == WINED3DFMT_P8) {
547 PALETTEENTRY *pal;
548 DWORD width = pitch / 3;
549 int x, y, c;
550 if(This->palette) {
551 pal = This->palette->palents;
552 } else {
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;
569 break;
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
588 to tolerate this */
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_GLDIRTY; /* This is the marker that surface data has to be downloaded */
604 /* Calculate the correct start address to report */
605 if (NULL == pRect) {
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);
612 } else {
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) ||
616 (pRect->left < 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));
628 } else {
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
643 * changed
645 if(!(This->Flags & SFLAG_DYNLOCK)) {
646 This->lockCount++;
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_GLDIRTY) ) {
655 TRACE("WINED3DLOCK_DISCARD flag passed, or local copy is up to date, not downloading data\n");
656 goto lock_end;
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;
669 if(!warned) {
670 ERR("The application tries to lock the render target, but render target locking is disabled\n");
671 warned = TRUE;
673 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
674 return WINED3D_OK;
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.
682 ENTER_GL();
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.
689 if(!swapchain) {
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;
696 } else {
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);
703 } else {
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) {
715 case RTL_AUTO:
716 case RTL_READDRAW:
717 case RTL_READTEX:
718 read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
719 break;
721 case RTL_TEXDRAW:
722 case RTL_TEXTEX:
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");
725 break;
727 LEAVE_GL();
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_GLDIRTY;
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
739 * and WINED3DFMT_D16
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,
747 * 1,
748 * GL_DEPTH_COMPONENT,
749 * type,
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 missing SFLAG_GLDIRTY 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");
757 } else {
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)) {
770 ENTER_GL();
771 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
772 checkGLcall("glActiveTextureARB");
773 LEAVE_GL();
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_GLDIRTY;
785 lock_end:
786 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
787 /* Don't dirtify */
788 } else {
789 IWineD3DBaseTexture *pBaseTexture;
791 * Dirtify on lock
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);
801 } else {
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, This->Flags & SFLAG_DIRTY ? 0 : 1);
807 return WINED3D_OK;
810 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This) {
811 GLint prev_store;
812 GLint prev_rasterpos[4];
813 GLint skipBytes = 0;
814 BOOL storechanged = FALSE, memory_allocated = FALSE;
815 GLint fmt, type;
816 BYTE *mem;
817 UINT bpp;
818 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
820 glDisable(GL_TEXTURE_2D);
821 vcheckGLcall("glDisable(GL_TEXTURE_2D)");
823 glFlush();
824 vcheckGLcall("glFlush");
825 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
826 vcheckGLcall("glIntegerv");
827 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
828 vcheckGLcall("glIntegerv");
829 glPixelZoom(1.0, -1.0);
830 vcheckGLcall("glPixelZoom");
832 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
833 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
834 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
836 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
837 vcheckGLcall("glRasterPos2f");
839 /* Some drivers(radeon dri, others?) don't like exceptions during
840 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
841 * after ReleaseDC. Reading it will cause an exception, which x11drv will
842 * catch to put the dib section in InSync mode, which leads to a crash
843 * and a blocked x server on my radeon card.
845 * The following lines read the dib section so it is put in inSync mode
846 * before glDrawPixels is called and the crash is prevented. There won't
847 * be any interfering gdi accesses, because UnlockRect is called from
848 * ReleaseDC, and the app won't use the dc any more afterwards.
850 if(This->Flags & SFLAG_DIBSECTION) {
851 volatile BYTE read;
852 read = This->resource.allocatedMemory[0];
855 switch (This->resource.format) {
856 /* No special care needed */
857 case WINED3DFMT_A4R4G4B4:
858 case WINED3DFMT_R5G6B5:
859 case WINED3DFMT_A1R5G5B5:
860 case WINED3DFMT_R8G8B8:
861 type = This->glDescription.glType;
862 fmt = This->glDescription.glFormat;
863 mem = This->resource.allocatedMemory;
864 bpp = This->bytesPerPixel;
865 break;
867 case WINED3DFMT_X4R4G4B4:
869 int size;
870 unsigned short *data;
871 data = (unsigned short *)This->resource.allocatedMemory;
872 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
873 while(size > 0) {
874 *data |= 0xF000;
875 data++;
876 size--;
878 type = This->glDescription.glType;
879 fmt = This->glDescription.glFormat;
880 mem = This->resource.allocatedMemory;
881 bpp = This->bytesPerPixel;
883 break;
885 case WINED3DFMT_X1R5G5B5:
887 int size;
888 unsigned short *data;
889 data = (unsigned short *)This->resource.allocatedMemory;
890 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
891 while(size > 0) {
892 *data |= 0x8000;
893 data++;
894 size--;
896 type = This->glDescription.glType;
897 fmt = This->glDescription.glFormat;
898 mem = This->resource.allocatedMemory;
899 bpp = This->bytesPerPixel;
901 break;
903 case WINED3DFMT_X8R8G8B8:
905 /* make sure the X byte is set to alpha on, since it
906 could be any random value. This fixes the intro movie in Pirates! */
907 int size;
908 unsigned int *data;
909 data = (unsigned int *)This->resource.allocatedMemory;
910 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
911 while(size > 0) {
912 *data |= 0xFF000000;
913 data++;
914 size--;
917 /* Fall through */
919 case WINED3DFMT_A8R8G8B8:
921 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
922 vcheckGLcall("glPixelStorei");
923 storechanged = TRUE;
924 type = This->glDescription.glType;
925 fmt = This->glDescription.glFormat;
926 mem = This->resource.allocatedMemory;
927 bpp = This->bytesPerPixel;
929 break;
931 case WINED3DFMT_A2R10G10B10:
933 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
934 vcheckGLcall("glPixelStorei");
935 storechanged = TRUE;
936 type = This->glDescription.glType;
937 fmt = This->glDescription.glFormat;
938 mem = This->resource.allocatedMemory;
939 bpp = This->bytesPerPixel;
941 break;
943 case WINED3DFMT_P8:
945 int height = This->glRect.bottom - This->glRect.top;
946 type = GL_UNSIGNED_BYTE;
947 fmt = GL_RGBA;
949 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * sizeof(DWORD));
950 if(!mem) {
951 ERR("Out of memory\n");
952 return;
954 memory_allocated = TRUE;
955 d3dfmt_convert_surface(This->resource.allocatedMemory,
956 mem,
957 pitch,
958 pitch,
959 height,
960 pitch * 4,
961 CONVERT_PALETTED,
962 This);
963 bpp = This->bytesPerPixel * 4;
964 pitch *= 4;
966 break;
968 default:
969 FIXME("Unsupported Format %u in locking func\n", This->resource.format);
971 /* Give it a try */
972 type = This->glDescription.glType;
973 fmt = This->glDescription.glFormat;
974 mem = This->resource.allocatedMemory;
975 bpp = This->bytesPerPixel;
978 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
979 (This->lockedRect.bottom - This->lockedRect.top)-1,
980 fmt, type,
981 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
982 checkGLcall("glDrawPixels");
983 glPixelZoom(1.0,1.0);
984 vcheckGLcall("glPixelZoom");
986 glRasterPos3iv(&prev_rasterpos[0]);
987 vcheckGLcall("glRasterPos3iv");
989 /* Reset to previous pack row length */
990 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
991 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
992 if(storechanged) {
993 glPixelStorei(GL_PACK_SWAP_BYTES, prev_store);
994 vcheckGLcall("glPixelStorei GL_PACK_SWAP_BYTES");
997 /* Blitting environment requires that 2D texturing is enabled. It was turned off before,
998 * turn it on again
1000 glEnable(GL_TEXTURE_2D);
1001 checkGLcall("glEnable(GL_TEXTURE_2D)");
1003 if(memory_allocated) HeapFree(GetProcessHeap(), 0, mem);
1004 return;
1007 static void flush_to_framebuffer_texture(IWineD3DSurfaceImpl *This) {
1008 float glTexCoord[4];
1010 glTexCoord[0] = (float) This->lockedRect.left / (float) This->pow2Width; /* left */
1011 glTexCoord[1] = (float) This->lockedRect.right / (float) This->pow2Width; /* right */
1012 glTexCoord[2] = (float) This->lockedRect.top / (float) This->pow2Height; /* top */
1013 glTexCoord[3] = (float) This->lockedRect.bottom / (float) This->pow2Height; /* bottom */
1015 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
1017 ENTER_GL();
1019 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
1020 checkGLcall("glEnable glBindTexture");
1022 /* No filtering for blts */
1023 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1024 checkGLcall("glTexParameteri");
1025 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1026 checkGLcall("glTexParameteri");
1028 /* Start drawing a quad */
1029 glBegin(GL_QUADS);
1031 glColor3d(1.0f, 1.0f, 1.0f);
1032 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
1033 glVertex3f(This->lockedRect.left, This->lockedRect.top, 0.0);
1035 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
1036 glVertex3f(This->lockedRect.left, This->lockedRect.bottom, 0.0);
1038 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
1039 glVertex3d(This->lockedRect.right, This->lockedRect.bottom, 0.0);
1041 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
1042 glVertex3f(This->lockedRect.right, This->lockedRect.top, 0.0);
1044 glEnd();
1045 checkGLcall("glEnd");
1047 /* Unbind the texture */
1048 glBindTexture(GL_TEXTURE_2D, 0);
1049 checkGLcall("glEnable glBindTexture");
1051 LEAVE_GL();
1054 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1055 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1056 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1057 IWineD3DSwapChainImpl *swapchain = NULL;
1059 if (!(This->Flags & SFLAG_LOCKED)) {
1060 WARN("trying to Unlock an unlocked surf@%p\n", This);
1061 return WINED3DERR_INVALIDCALL;
1064 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & SFLAG_DIRTY ? 1 : 0);
1066 if (!(This->Flags & SFLAG_DIRTY)) {
1067 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1068 goto unlock_end;
1071 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1072 if(swapchain || iface == myDevice->render_targets[0]) {
1073 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1074 static BOOL warned = FALSE;
1075 if(!warned) {
1076 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1077 warned = TRUE;
1079 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1080 goto unlock_end;
1083 /* Activate the correct context for the render target */
1084 ENTER_GL();
1085 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1087 if(!swapchain) {
1088 /* Primary offscreen render target */
1089 TRACE("Offscreen render target\n");
1090 glDrawBuffer(myDevice->offscreenBuffer);
1091 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1092 } else {
1093 if(iface == swapchain->frontBuffer) {
1094 TRACE("Onscreen front buffer\n");
1095 glDrawBuffer(GL_FRONT);
1096 checkGLcall("glDrawBuffer(GL_FRONT)");
1097 } else if(iface == swapchain->backBuffer[0]) {
1098 TRACE("Onscreen back buffer\n");
1099 glDrawBuffer(GL_BACK);
1100 checkGLcall("glDrawBuffer(GL_BACK)");
1101 } else {
1102 FIXME("Unlocking a higher back buffer\n");
1103 glDrawBuffer(GL_BACK);
1104 checkGLcall("glDrawBuffer(GL_BACK)");
1106 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1109 switch(wined3d_settings.rendertargetlock_mode) {
1110 case RTL_AUTO:
1111 case RTL_READDRAW:
1112 case RTL_TEXDRAW:
1113 flush_to_framebuffer_drawpixels(This);
1114 break;
1116 case RTL_READTEX:
1117 case RTL_TEXTEX:
1118 flush_to_framebuffer_texture(This);
1119 break;
1121 if(!swapchain) {
1122 glDrawBuffer(myDevice->offscreenBuffer);
1123 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1124 } else if(swapchain->backBuffer) {
1125 glDrawBuffer(GL_BACK);
1126 checkGLcall("glDrawBuffer(GL_BACK)");
1127 } else {
1128 glDrawBuffer(GL_FRONT);
1129 checkGLcall("glDrawBuffer(GL_FRONT)");
1131 LEAVE_GL();
1133 /** restore clean dirty state */
1134 IWineD3DSurface_CleanDirtyRect(iface);
1135 } else if(iface == myDevice->stencilBufferTarget) {
1136 FIXME("Depth Stencil buffer locking is not implemented\n");
1137 } else {
1138 /* The rest should be a normal texture */
1139 IWineD3DBaseTextureImpl *impl;
1140 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1141 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1142 * states need resetting
1144 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1145 if(impl->baseTexture.bindCount) {
1146 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1148 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1152 unlock_end:
1153 This->Flags &= ~SFLAG_LOCKED;
1154 memset(&This->lockedRect, 0, sizeof(RECT));
1155 return WINED3D_OK;
1158 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1159 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1160 WINED3DLOCKED_RECT lock;
1161 UINT usage;
1162 BITMAPINFO* b_info;
1163 HDC ddc;
1164 DWORD *masks;
1165 HRESULT hr;
1166 RGBQUAD col[256];
1167 const PixelFormatDesc *formatEntry = getFormatDescEntry(This->resource.format);
1169 TRACE("(%p)->(%p)\n",This,pHDC);
1171 if(This->Flags & SFLAG_USERPTR) {
1172 ERR("Not supported on surfaces with an application-provided surfaces\n");
1173 return DDERR_NODC;
1176 /* Give more detailed info for ddraw */
1177 if (This->Flags & SFLAG_DCINUSE)
1178 return DDERR_DCALREADYCREATED;
1180 /* Can't GetDC if the surface is locked */
1181 if (This->Flags & SFLAG_LOCKED)
1182 return WINED3DERR_INVALIDCALL;
1184 memset(&lock, 0, sizeof(lock)); /* To be sure */
1186 /* Create a DIB section if there isn't a hdc yet */
1187 if(!This->hDC) {
1188 int extraline = 0;
1189 SYSTEM_INFO sysInfo;
1191 switch (This->bytesPerPixel) {
1192 case 2:
1193 case 4:
1194 /* Allocate extra space to store the RGB bit masks. */
1195 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
1196 break;
1198 case 3:
1199 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
1200 break;
1202 default:
1203 /* Allocate extra space for a palette. */
1204 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1205 sizeof(BITMAPINFOHEADER)
1206 + sizeof(RGBQUAD)
1207 * (1 << (This->bytesPerPixel * 8)));
1208 break;
1211 if (!b_info)
1212 return E_OUTOFMEMORY;
1214 /* Some apps access the surface in via DWORDs, and do not take the necessary care at the end of the
1215 * surface. So we need at least extra 4 bytes at the end of the surface. Check against the page size,
1216 * if the last page used for the surface has at least 4 spare bytes we're safe, otherwise
1217 * add an extra line to the dib section
1219 GetSystemInfo(&sysInfo);
1220 if( ((This->resource.size + 3) % sysInfo.dwPageSize) < 4) {
1221 extraline = 1;
1222 TRACE("Adding an extra line to the dib section\n");
1225 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1226 b_info->bmiHeader.biWidth = This->currentDesc.Width;
1227 b_info->bmiHeader.biHeight = -This->currentDesc.Height -extraline;
1228 b_info->bmiHeader.biSizeImage = ( This->currentDesc.Height + extraline) * IWineD3DSurface_GetPitch(iface);
1229 b_info->bmiHeader.biPlanes = 1;
1230 b_info->bmiHeader.biBitCount = This->bytesPerPixel * 8;
1232 b_info->bmiHeader.biXPelsPerMeter = 0;
1233 b_info->bmiHeader.biYPelsPerMeter = 0;
1234 b_info->bmiHeader.biClrUsed = 0;
1235 b_info->bmiHeader.biClrImportant = 0;
1237 /* Get the bit masks */
1238 masks = (DWORD *) &(b_info->bmiColors);
1239 switch (This->resource.format) {
1240 case WINED3DFMT_R8G8B8:
1241 usage = DIB_RGB_COLORS;
1242 b_info->bmiHeader.biCompression = BI_RGB;
1243 break;
1245 case WINED3DFMT_X1R5G5B5:
1246 case WINED3DFMT_A1R5G5B5:
1247 case WINED3DFMT_A4R4G4B4:
1248 case WINED3DFMT_X4R4G4B4:
1249 case WINED3DFMT_R3G3B2:
1250 case WINED3DFMT_A8R3G3B2:
1251 case WINED3DFMT_A2B10G10R10:
1252 case WINED3DFMT_A8B8G8R8:
1253 case WINED3DFMT_X8B8G8R8:
1254 case WINED3DFMT_A2R10G10B10:
1255 case WINED3DFMT_R5G6B5:
1256 case WINED3DFMT_A16B16G16R16:
1257 usage = 0;
1258 b_info->bmiHeader.biCompression = BI_BITFIELDS;
1259 masks[0] = formatEntry->redMask;
1260 masks[1] = formatEntry->greenMask;
1261 masks[2] = formatEntry->blueMask;
1262 break;
1264 default:
1265 /* Don't know palette */
1266 b_info->bmiHeader.biCompression = BI_RGB;
1267 usage = 0;
1268 break;
1271 ddc = GetDC(0);
1272 if (ddc == 0) {
1273 HeapFree(GetProcessHeap(), 0, b_info);
1274 return HRESULT_FROM_WIN32(GetLastError());
1277 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);
1278 This->dib.DIBsection = CreateDIBSection(ddc, b_info, usage, &This->dib.bitmap_data, 0 /* Handle */, 0 /* Offset */);
1279 ReleaseDC(0, ddc);
1281 if (!This->dib.DIBsection) {
1282 ERR("CreateDIBSection failed!\n");
1283 HeapFree(GetProcessHeap(), 0, b_info);
1284 return HRESULT_FROM_WIN32(GetLastError());
1287 TRACE("DIBSection at : %p\n", This->dib.bitmap_data);
1289 /* copy the existing surface to the dib section */
1290 if(This->resource.allocatedMemory) {
1291 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, b_info->bmiHeader.biSizeImage);
1292 /* We won't need that any more */
1293 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
1294 } else {
1295 /* This is to make LockRect read the gl Texture although memory is allocated */
1296 This->Flags |= SFLAG_GLDIRTY;
1299 HeapFree(GetProcessHeap(), 0, b_info);
1301 /* Use the dib section from now on */
1302 This->resource.allocatedMemory = This->dib.bitmap_data;
1304 /* Now allocate a HDC */
1305 This->hDC = CreateCompatibleDC(0);
1306 This->dib.holdbitmap = SelectObject(This->hDC, This->dib.DIBsection);
1307 TRACE("using wined3d palette %p\n", This->palette);
1308 SelectPalette(This->hDC,
1309 This->palette ? This->palette->hpal : 0,
1310 FALSE);
1312 This->Flags |= SFLAG_DIBSECTION;
1315 /* Lock the surface */
1316 hr = IWineD3DSurface_LockRect(iface,
1317 &lock,
1318 NULL,
1320 if(FAILED(hr)) {
1321 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1322 /* keep the dib section */
1323 return hr;
1326 if(This->resource.format == WINED3DFMT_P8 ||
1327 This->resource.format == WINED3DFMT_A8P8) {
1328 unsigned int n;
1329 if(This->palette) {
1330 PALETTEENTRY ent[256];
1332 GetPaletteEntries(This->palette->hpal, 0, 256, ent);
1333 for (n=0; n<256; n++) {
1334 col[n].rgbRed = ent[n].peRed;
1335 col[n].rgbGreen = ent[n].peGreen;
1336 col[n].rgbBlue = ent[n].peBlue;
1337 col[n].rgbReserved = 0;
1339 } else {
1340 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1342 for (n=0; n<256; n++) {
1343 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
1344 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
1345 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
1346 col[n].rgbReserved = 0;
1350 SetDIBColorTable(This->hDC, 0, 256, col);
1353 *pHDC = This->hDC;
1354 TRACE("returning %p\n",*pHDC);
1355 This->Flags |= SFLAG_DCINUSE;
1357 return WINED3D_OK;
1360 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1361 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1363 TRACE("(%p)->(%p)\n",This,hDC);
1365 if (!(This->Flags & SFLAG_DCINUSE))
1366 return WINED3DERR_INVALIDCALL;
1368 /* we locked first, so unlock now */
1369 IWineD3DSurface_UnlockRect(iface);
1371 This->Flags &= ~SFLAG_DCINUSE;
1373 return WINED3D_OK;
1376 /* ******************************************************
1377 IWineD3DSurface Internal (No mapping to directx api) parts follow
1378 ****************************************************** */
1380 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) {
1381 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & DDSD_CKSRCBLT);
1382 const PixelFormatDesc *formatEntry = getFormatDescEntry(This->resource.format);
1384 /* Default values: From the surface */
1385 *format = formatEntry->glFormat;
1386 *internal = formatEntry->glInternal;
1387 *type = formatEntry->glType;
1388 *convert = NO_CONVERSION;
1389 *target_bpp = This->bytesPerPixel;
1391 /* Ok, now look if we have to do any conversion */
1392 switch(This->resource.format) {
1393 case WINED3DFMT_P8:
1394 /* ****************
1395 Paletted Texture
1396 **************** */
1397 /* Use conversion when the paletted texture extension is not available, or when it is available make sure it is used
1398 * for texturing as it won't work for calls like glDraw-/glReadPixels and further also use conversion in case of color keying.
1400 if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) || colorkey_active || (!use_texturing && GL_SUPPORT(EXT_PALETTED_TEXTURE)) ) {
1401 *format = GL_RGBA;
1402 *internal = GL_RGBA;
1403 *type = GL_UNSIGNED_BYTE;
1404 *target_bpp = 4;
1405 if(colorkey_active) {
1406 *convert = CONVERT_PALETTED_CK;
1407 } else {
1408 *convert = CONVERT_PALETTED;
1412 break;
1414 case WINED3DFMT_R3G3B2:
1415 /* **********************
1416 GL_UNSIGNED_BYTE_3_3_2
1417 ********************** */
1418 if (colorkey_active) {
1419 /* This texture format will never be used.. So do not care about color keying
1420 up until the point in time it will be needed :-) */
1421 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1423 break;
1425 case WINED3DFMT_R5G6B5:
1426 if (colorkey_active) {
1427 *convert = CONVERT_CK_565;
1428 *format = GL_RGBA;
1429 *internal = GL_RGBA;
1430 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1432 break;
1434 case WINED3DFMT_R8G8B8:
1435 if (colorkey_active) {
1436 *convert = CONVERT_CK_RGB24;
1437 *format = GL_RGBA;
1438 *internal = GL_RGBA;
1439 *type = GL_UNSIGNED_INT_8_8_8_8;
1440 *target_bpp = 4;
1442 break;
1444 case WINED3DFMT_X8R8G8B8:
1445 if (colorkey_active) {
1446 *convert = CONVERT_RGB32_888;
1447 *format = GL_RGBA;
1448 *internal = GL_RGBA;
1449 *type = GL_UNSIGNED_INT_8_8_8_8;
1451 break;
1453 case WINED3DFMT_V8U8:
1454 *convert = CONVERT_V8U8;
1455 *format = GL_BGR;
1456 *internal = GL_RGB8;
1457 *type = GL_BYTE;
1458 *target_bpp = 3;
1459 break;
1461 default:
1462 break;
1465 return WINED3D_OK;
1468 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf) {
1469 BYTE *source, *dest;
1470 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surf);
1472 switch (convert) {
1473 case NO_CONVERSION:
1475 memcpy(dst, src, pitch * height);
1476 break;
1478 case CONVERT_PALETTED:
1479 case CONVERT_PALETTED_CK:
1481 IWineD3DPaletteImpl* pal = surf->palette;
1482 BYTE table[256][4];
1483 unsigned int i;
1484 unsigned int x, y;
1486 if( pal == NULL) {
1487 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1490 if (pal == NULL) {
1491 /* Still no palette? Use the device's palette */
1492 /* Get the surface's palette */
1493 for (i = 0; i < 256; i++) {
1494 IWineD3DDeviceImpl *device = surf->resource.wineD3DDevice;
1496 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1497 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1498 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1499 if ((convert == CONVERT_PALETTED_CK) &&
1500 (i >= surf->SrcBltCKey.dwColorSpaceLowValue) &&
1501 (i <= surf->SrcBltCKey.dwColorSpaceHighValue)) {
1502 /* We should maybe here put a more 'neutral' color than the standard bright purple
1503 one often used by application to prevent the nice purple borders when bi-linear
1504 filtering is on */
1505 table[i][3] = 0x00;
1506 } else {
1507 table[i][3] = 0xFF;
1510 } else {
1511 TRACE("Using surface palette %p\n", pal);
1512 /* Get the surface's palette */
1513 for (i = 0; i < 256; i++) {
1514 table[i][0] = pal->palents[i].peRed;
1515 table[i][1] = pal->palents[i].peGreen;
1516 table[i][2] = pal->palents[i].peBlue;
1517 if ((convert == CONVERT_PALETTED_CK) &&
1518 (i >= surf->SrcBltCKey.dwColorSpaceLowValue) &&
1519 (i <= surf->SrcBltCKey.dwColorSpaceHighValue)) {
1520 /* We should maybe here put a more 'neutral' color than the standard bright purple
1521 one often used by application to prevent the nice purple borders when bi-linear
1522 filtering is on */
1523 table[i][3] = 0x00;
1524 } else {
1525 table[i][3] = 0xFF;
1530 for (y = 0; y < height; y++)
1532 source = src + pitch * y;
1533 dest = dst + outpitch * y;
1534 /* This is an 1 bpp format, using the width here is fine */
1535 for (x = 0; x < width; x++) {
1536 BYTE color = *source++;
1537 *dest++ = table[color][0];
1538 *dest++ = table[color][1];
1539 *dest++ = table[color][2];
1540 *dest++ = table[color][3];
1544 break;
1546 case CONVERT_CK_565:
1548 /* Converting the 565 format in 5551 packed to emulate color-keying.
1550 Note : in all these conversion, it would be best to average the averaging
1551 pixels to get the color of the pixel that will be color-keyed to
1552 prevent 'color bleeding'. This will be done later on if ever it is
1553 too visible.
1555 Note2: Nvidia documents say that their driver does not support alpha + color keying
1556 on the same surface and disables color keying in such a case
1558 unsigned int x, y;
1559 WORD *Source;
1560 WORD *Dest;
1562 TRACE("Color keyed 565\n");
1564 for (y = 0; y < height; y++) {
1565 Source = (WORD *) (src + y * pitch);
1566 Dest = (WORD *) (dst + y * outpitch);
1567 for (x = 0; x < width; x++ ) {
1568 WORD color = *Source++;
1569 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1570 if ((color < surf->SrcBltCKey.dwColorSpaceLowValue) ||
1571 (color > surf->SrcBltCKey.dwColorSpaceHighValue)) {
1572 *Dest |= 0x0001;
1574 Dest++;
1578 break;
1580 case CONVERT_V8U8:
1582 unsigned int x, y;
1583 short *Source;
1584 char *Dest;
1585 for(y = 0; y < height; y++) {
1586 Source = (short *) (src + y * pitch);
1587 Dest = (char *) (dst + y * outpitch);
1588 for (x = 0; x < width; x++ ) {
1589 long color = (*Source++);
1590 Dest[0] = color >> 8;
1591 Dest[1] = color;
1592 Dest[2] = 0xff;
1593 Dest += 3;
1596 break;
1599 default:
1600 ERR("Unsupported conversation type %d\n", convert);
1602 return WINED3D_OK;
1605 /* This function is used in case of 8bit paletted textures to upload the palette.
1606 For now it only supports GL_EXT_paletted_texture extension but support for other
1607 extensions like ARB_fragment_program and ATI_fragment_shaders will be added as well.
1609 void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
1610 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1611 IWineD3DPaletteImpl* pal = This->palette;
1612 BYTE table[256][4];
1613 int i;
1615 if (pal == NULL) {
1616 /* Still no palette? Use the device's palette */
1617 /* Get the surface's palette */
1618 for (i = 0; i < 256; i++) {
1619 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1621 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1622 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1623 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1624 if ((convert == CONVERT_PALETTED_CK) &&
1625 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1626 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1627 /* We should maybe here put a more 'neutral' color than the standard bright purple
1628 one often used by application to prevent the nice purple borders when bi-linear
1629 filtering is on */
1630 table[i][3] = 0x00;
1631 } else {
1632 table[i][3] = 0xFF;
1635 } else {
1636 TRACE("Using surface palette %p\n", pal);
1637 /* Get the surface's palette */
1638 for (i = 0; i < 256; i++) {
1639 table[i][0] = pal->palents[i].peRed;
1640 table[i][1] = pal->palents[i].peGreen;
1641 table[i][2] = pal->palents[i].peBlue;
1642 if ((convert == CONVERT_PALETTED_CK) &&
1643 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1644 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1645 /* We should maybe here put a more 'neutral' color than the standard bright purple
1646 one often used by application to prevent the nice purple borders when bi-linear
1647 filtering is on */
1648 table[i][3] = 0x00;
1649 } else {
1650 table[i][3] = 0xFF;
1654 GL_EXTCALL(glColorTableEXT(GL_TEXTURE_2D,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
1657 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
1658 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1660 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
1661 /* If a ddraw-style palette is attached assume no d3d9 palette change.
1662 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
1664 return FALSE;
1667 if(This->palette9) {
1668 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
1669 return FALSE;
1671 } else {
1672 This->palette9 = (PALETTEENTRY *) HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
1674 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
1675 return TRUE;
1678 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface) {
1679 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1680 GLenum format, internal, type;
1681 CONVERT_TYPES convert;
1682 int bpp;
1683 int width, pitch, outpitch;
1684 BYTE *mem;
1686 if (This->Flags & SFLAG_INTEXTURE) {
1687 TRACE("Surface already in texture\n");
1688 return WINED3D_OK;
1690 if (This->Flags & SFLAG_DIRTY) {
1691 TRACE("Reloading because surface is dirty\n");
1692 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
1693 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & DDSD_CKSRCBLT))) ||
1694 /* Reload: vice versa OR */
1695 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & DDSD_CKSRCBLT)) ||
1696 /* Also reload: Color key is active AND the color key has changed */
1697 ((This->CKeyFlags & DDSD_CKSRCBLT) && (
1698 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
1699 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
1700 TRACE("Reloading because of color keying\n");
1701 } else if(palette9_changed(This)) {
1702 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
1703 } else {
1704 TRACE("surface isn't dirty\n");
1705 return WINED3D_OK;
1708 This->Flags &= ~SFLAG_DIRTY;
1710 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
1711 * These resources are not bound by device size or format restrictions. Because of this,
1712 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
1713 * However, these resources can always be created, locked, and copied.
1715 if (This->resource.pool == WINED3DPOOL_SCRATCH && !(This->Flags & SFLAG_FORCELOAD) )
1717 FIXME("(%p) Operation not supported for scratch textures\n",This);
1718 return WINED3DERR_INVALIDCALL;
1721 if (This->Flags & SFLAG_INPBUFFER) {
1722 if (This->glDescription.level != 0)
1723 FIXME("Surface in texture is only supported for level 0\n");
1724 else if (This->resource.format == WINED3DFMT_P8 || This->resource.format == WINED3DFMT_A8P8 ||
1725 This->resource.format == WINED3DFMT_DXT1 || This->resource.format == WINED3DFMT_DXT2 ||
1726 This->resource.format == WINED3DFMT_DXT3 || This->resource.format == WINED3DFMT_DXT4 ||
1727 This->resource.format == WINED3DFMT_DXT5)
1728 FIXME("Format %d not supported\n", This->resource.format);
1729 else {
1730 GLint prevRead;
1732 ENTER_GL();
1734 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1735 vcheckGLcall("glGetIntegerv");
1736 glReadBuffer(This->resource.wineD3DDevice->offscreenBuffer);
1737 vcheckGLcall("glReadBuffer");
1739 glCopyTexImage2D(This->glDescription.target,
1740 This->glDescription.level,
1741 This->glDescription.glFormatInternal,
1744 This->currentDesc.Width,
1745 This->currentDesc.Height,
1748 checkGLcall("glCopyTexImage2D");
1749 glReadBuffer(prevRead);
1750 vcheckGLcall("glReadBuffer");
1752 LEAVE_GL();
1754 TRACE("Updating target %d\n", This->glDescription.target);
1755 This->Flags |= SFLAG_INTEXTURE;
1757 return WINED3D_OK;
1760 if(This->CKeyFlags & DDSD_CKSRCBLT) {
1761 This->Flags |= SFLAG_GLCKEY;
1762 This->glCKey = This->SrcBltCKey;
1764 else This->Flags &= ~SFLAG_GLCKEY;
1766 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp);
1768 /* The width is in 'length' not in bytes */
1769 width = This->currentDesc.Width;
1770 pitch = IWineD3DSurface_GetPitch(iface);
1772 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
1773 int height = This->currentDesc.Height;
1775 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
1776 outpitch = width * bpp;
1777 outpitch = (outpitch + SURFACE_ALIGNMENT - 1) & ~(SURFACE_ALIGNMENT - 1);
1779 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
1780 if(!mem) {
1781 ERR("Out of memory %d, %d!\n", outpitch, height);
1782 return WINED3DERR_OUTOFVIDEOMEMORY;
1784 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
1786 This->Flags |= SFLAG_CONVERTED;
1787 } else if (This->resource.format == WINED3DFMT_P8 && GL_SUPPORT(EXT_PALETTED_TEXTURE)) {
1788 d3dfmt_p8_upload_palette(iface, convert);
1789 This->Flags &= ~SFLAG_CONVERTED;
1790 mem = This->resource.allocatedMemory;
1791 } else {
1792 This->Flags &= ~SFLAG_CONVERTED;
1793 mem = This->resource.allocatedMemory;
1796 /* Make sure the correct pitch is used */
1797 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
1799 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
1800 TRACE("non power of two support\n");
1801 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
1802 if (mem) {
1803 surface_upload_data(This, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
1805 } else {
1806 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
1807 if (mem) {
1808 surface_upload_data(This, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
1812 /* Restore the default pitch */
1813 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1815 if (mem != This->resource.allocatedMemory)
1816 HeapFree(GetProcessHeap(), 0, mem);
1818 #if 0
1820 static unsigned int gen = 0;
1821 char buffer[4096];
1822 ++gen;
1823 if ((gen % 10) == 0) {
1824 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
1825 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
1828 * debugging crash code
1829 if (gen == 250) {
1830 void** test = NULL;
1831 *test = 0;
1835 #endif
1837 if (!(This->Flags & SFLAG_DONOTFREE)) {
1838 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
1839 This->resource.allocatedMemory = NULL;
1842 return WINED3D_OK;
1845 #include <errno.h>
1846 #include <stdio.h>
1847 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
1848 FILE* f = NULL;
1849 UINT i, y;
1850 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1851 char *allocatedMemory;
1852 char *textureRow;
1853 IWineD3DSwapChain *swapChain = NULL;
1854 int width, height;
1855 GLuint tmpTexture = 0;
1856 DWORD color;
1857 /*FIXME:
1858 Textures my not be stored in ->allocatedgMemory and a GlTexture
1859 so we should lock the surface before saving a snapshot, or at least check that
1861 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
1862 by calling GetTexImage and in compressed form by calling
1863 GetCompressedTexImageARB. Queried compressed images can be saved and
1864 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
1865 texture images do not need to be processed by the GL and should
1866 significantly improve texture loading performance relative to uncompressed
1867 images. */
1869 /* Setup the width and height to be the internal texture width and height. */
1870 width = This->pow2Width;
1871 height = This->pow2Height;
1872 /* check to see if were a 'virtual' texture e.g. were not a pbuffer of texture were a back buffer*/
1873 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
1875 if (swapChain || (This->Flags & SFLAG_INPBUFFER)) { /* if were not a real texture then read the back buffer into a real texture*/
1876 /* we don't want to interfere with the back buffer so read the data into a temporary texture and then save the data out of the temporary texture */
1877 GLint prevRead;
1878 ENTER_GL();
1879 FIXME("(%p) This surface needs to be locked before a snapshot can be taken\n", This);
1880 glEnable(GL_TEXTURE_2D);
1882 glGenTextures(1, &tmpTexture);
1883 glBindTexture(GL_TEXTURE_2D, tmpTexture);
1885 glTexImage2D(GL_TEXTURE_2D,
1887 GL_RGBA,
1888 width,
1889 height,
1890 0/*border*/,
1891 GL_RGBA,
1892 GL_UNSIGNED_INT_8_8_8_8_REV,
1893 NULL);
1895 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1896 vcheckGLcall("glGetIntegerv");
1897 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
1898 vcheckGLcall("glReadBuffer");
1899 glCopyTexImage2D(GL_TEXTURE_2D,
1901 GL_RGBA,
1904 width,
1905 height,
1908 checkGLcall("glCopyTexImage2D");
1909 glReadBuffer(prevRead);
1910 LEAVE_GL();
1912 } else { /* bind the real texture */
1913 IWineD3DSurface_PreLoad(iface);
1915 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
1916 ENTER_GL();
1917 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
1918 glGetTexImage(GL_TEXTURE_2D,
1919 This->glDescription.level,
1920 GL_RGBA,
1921 GL_UNSIGNED_INT_8_8_8_8_REV,
1922 allocatedMemory);
1923 checkGLcall("glTexImage2D");
1924 if (tmpTexture) {
1925 glBindTexture(GL_TEXTURE_2D, 0);
1926 glDeleteTextures(1, &tmpTexture);
1928 LEAVE_GL();
1930 f = fopen(filename, "w+");
1931 if (NULL == f) {
1932 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
1933 return WINED3DERR_INVALIDCALL;
1935 /* Save the dat out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha chanel*/
1936 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
1937 /* TGA header */
1938 fputc(0,f);
1939 fputc(0,f);
1940 fputc(2,f);
1941 fputc(0,f);
1942 fputc(0,f);
1943 fputc(0,f);
1944 fputc(0,f);
1945 fputc(0,f);
1946 fputc(0,f);
1947 fputc(0,f);
1948 fputc(0,f);
1949 fputc(0,f);
1950 /* short width*/
1951 fwrite(&width,2,1,f);
1952 /* short height */
1953 fwrite(&height,2,1,f);
1954 /* format rgba */
1955 fputc(0x20,f);
1956 fputc(0x28,f);
1957 /* raw data */
1958 /* 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*/
1959 if(swapChain)
1960 textureRow = allocatedMemory + (width * (height - 1) *4);
1961 else
1962 textureRow = allocatedMemory;
1963 for (y = 0 ; y < height; y++) {
1964 for (i = 0; i < width; i++) {
1965 color = *((DWORD*)textureRow);
1966 fputc((color >> 16) & 0xFF, f); /* B */
1967 fputc((color >> 8) & 0xFF, f); /* G */
1968 fputc((color >> 0) & 0xFF, f); /* R */
1969 fputc((color >> 24) & 0xFF, f); /* A */
1970 textureRow += 4;
1972 /* take two rows of the pointer to the texture memory */
1973 if(swapChain)
1974 (textureRow-= width << 3);
1977 TRACE("Closing file\n");
1978 fclose(f);
1980 if(swapChain) {
1981 IWineD3DSwapChain_Release(swapChain);
1983 HeapFree(GetProcessHeap(), 0, allocatedMemory);
1984 return WINED3D_OK;
1987 HRESULT WINAPI IWineD3DSurfaceImpl_CleanDirtyRect(IWineD3DSurface *iface) {
1988 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1989 This->Flags &= ~SFLAG_DIRTY;
1990 This->dirtyRect.left = This->currentDesc.Width;
1991 This->dirtyRect.top = This->currentDesc.Height;
1992 This->dirtyRect.right = 0;
1993 This->dirtyRect.bottom = 0;
1994 TRACE("(%p) : Dirty?%d, Rect:(%d,%d,%d,%d)\n", This, This->Flags & SFLAG_DIRTY ? 1 : 0, This->dirtyRect.left,
1995 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
1996 return WINED3D_OK;
2000 * Slightly inefficient way to handle multiple dirty rects but it works :)
2002 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2003 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2004 IWineD3DBaseTexture *baseTexture = NULL;
2005 This->Flags |= SFLAG_DIRTY;
2006 if (NULL != pDirtyRect) {
2007 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2008 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2009 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2010 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2011 } else {
2012 This->dirtyRect.left = 0;
2013 This->dirtyRect.top = 0;
2014 This->dirtyRect.right = This->currentDesc.Width;
2015 This->dirtyRect.bottom = This->currentDesc.Height;
2017 TRACE("(%p) : Dirty?%d, Rect:(%d,%d,%d,%d)\n", This, This->Flags & SFLAG_DIRTY, This->dirtyRect.left,
2018 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2019 /* if the container is a basetexture then mark it dirty. */
2020 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2021 TRACE("Passing to conatiner\n");
2022 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2023 IWineD3DBaseTexture_Release(baseTexture);
2025 return WINED3D_OK;
2028 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
2029 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2031 TRACE("This %p, container %p\n", This, container);
2033 /* We can't keep a reference to the container, since the container already keeps a reference to us. */
2035 TRACE("Setting container to %p from %p\n", container, This->container);
2036 This->container = container;
2038 return WINED3D_OK;
2041 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2042 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2043 const PixelFormatDesc *formatEntry = getFormatDescEntry(format);
2045 if (This->resource.format != WINED3DFMT_UNKNOWN) {
2046 FIXME("(%p) : The foramt of the surface must be WINED3DFORMAT_UNKNOWN\n", This);
2047 return WINED3DERR_INVALIDCALL;
2050 TRACE("(%p) : Setting texture foramt to (%d,%s)\n", This, format, debug_d3dformat(format));
2051 if (format == WINED3DFMT_UNKNOWN) {
2052 This->resource.size = 0;
2053 } else if (format == WINED3DFMT_DXT1) {
2054 /* DXT1 is half byte per pixel */
2055 This->resource.size = ((max(This->pow2Width, 4) * formatEntry->bpp) * max(This->pow2Height, 4)) >> 1;
2057 } else if (format == WINED3DFMT_DXT2 || format == WINED3DFMT_DXT3 ||
2058 format == WINED3DFMT_DXT4 || format == WINED3DFMT_DXT5) {
2059 This->resource.size = ((max(This->pow2Width, 4) * formatEntry->bpp) * max(This->pow2Height, 4));
2060 } else {
2061 This->resource.size = ((This->pow2Width * formatEntry->bpp) + SURFACE_ALIGNMENT - 1) & ~(SURFACE_ALIGNMENT - 1);
2062 This->resource.size *= This->pow2Height;
2066 /* Setup some glformat defaults */
2067 This->glDescription.glFormat = formatEntry->glFormat;
2068 This->glDescription.glFormatInternal = formatEntry->glInternal;
2069 This->glDescription.glType = formatEntry->glType;
2071 if (format != WINED3DFMT_UNKNOWN) {
2072 This->bytesPerPixel = formatEntry->bpp;
2073 } else {
2074 This->bytesPerPixel = 0;
2077 This->Flags |= (WINED3DFMT_D16_LOCKABLE == format) ? SFLAG_LOCKABLE : 0;
2079 This->resource.format = format;
2081 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);
2083 return WINED3D_OK;
2086 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2087 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2089 /* Render targets depend on their hdc, and we can't create a hdc on a user pointer */
2090 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
2091 ERR("Not supported on render targets\n");
2092 return WINED3DERR_INVALIDCALL;
2095 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2096 WARN("Surface is locked or the HDC is in use\n");
2097 return WINED3DERR_INVALIDCALL;
2100 if(Mem && Mem != This->resource.allocatedMemory) {
2102 /* Do I have to copy the old surface content? */
2103 if(This->Flags & SFLAG_DIBSECTION) {
2104 /* Release the DC. No need to hold the critical section for the update
2105 * Thread because this thread runs only on front buffers, but this method
2106 * fails for render targets in the check above.
2108 SelectObject(This->hDC, This->dib.holdbitmap);
2109 DeleteDC(This->hDC);
2110 /* Release the DIB section */
2111 DeleteObject(This->dib.DIBsection);
2112 This->dib.bitmap_data = NULL;
2113 This->resource.allocatedMemory = NULL;
2114 This->hDC = NULL;
2115 This->Flags &= ~SFLAG_DIBSECTION;
2116 } else if(!(This->Flags & SFLAG_USERPTR)) {
2117 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
2119 This->resource.allocatedMemory = Mem;
2120 This->Flags |= SFLAG_USERPTR;
2121 } else if(This->Flags & SFLAG_USERPTR) {
2122 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2123 This->resource.allocatedMemory = NULL;
2124 This->Flags &= ~SFLAG_USERPTR;
2126 return WINED3D_OK;
2129 /* TODO: replace this function with context management routines */
2130 HRESULT WINAPI IWineD3DSurfaceImpl_SetPBufferState(IWineD3DSurface *iface, BOOL inPBuffer, BOOL inTexture) {
2131 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2133 if(inPBuffer) {
2134 This->Flags |= SFLAG_INPBUFFER;
2135 } else {
2136 This->Flags &= ~SFLAG_INPBUFFER;
2139 if(inTexture) {
2140 This->Flags |= SFLAG_INTEXTURE;
2141 } else {
2142 This->Flags &= ~SFLAG_INTEXTURE;
2145 return WINED3D_OK;
2148 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2149 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2150 IWineD3DDevice *D3D = (IWineD3DDevice *) This->resource.wineD3DDevice;
2151 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2153 /* Flipping is only supported on RenderTargets */
2154 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return DDERR_NOTFLIPPABLE;
2156 if(override) {
2157 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2158 * FIXME("(%p) Target override is not supported by now\n", This);
2159 * Additionally, it isn't really possible to support triple-buffering
2160 * properly on opengl at all
2164 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2165 return IWineD3DDevice_Present(D3D, NULL, NULL, 0, NULL);
2168 /* Does a direct frame buffer -> texture copy. Stretching is done
2169 * with single pixel copy calls
2171 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown) {
2172 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2173 float xrel, yrel;
2174 UINT row;
2175 BOOL warned = FALSE; /* deliberately not static */
2176 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2178 ENTER_GL();
2180 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2182 /* Bind the target texture */
2183 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
2184 checkGLcall("glBindTexture");
2185 if(!swapchain) {
2186 glReadBuffer(myDevice->offscreenBuffer);
2187 } else if(swapchain->backBuffer && SrcSurface == swapchain->backBuffer[0]) {
2188 glReadBuffer(GL_BACK);
2189 } else {
2190 glReadBuffer(GL_FRONT);
2192 checkGLcall("glReadBuffer");
2194 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2195 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2197 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2198 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2201 if(upsidedown &&
2202 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2203 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2204 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2206 glCopyTexSubImage2D(This->glDescription.target,
2207 This->glDescription.level,
2208 drect->x1, drect->y1, /* xoffset, yoffset */
2209 srect->x1, Src->currentDesc.Height - srect->y2,
2210 drect->x2 - drect->x1, drect->y2 - drect->y1);
2211 } else {
2212 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2213 /* I have to process this row by row to swap the image,
2214 * otherwise it would be upside down, so streching in y direction
2215 * doesn't cost extra time
2217 * However, streching in x direction can be avoided if not necessary
2219 for(row = drect->y1; row < drect->y2; row++) {
2220 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2221 /* Well, that stuff works, but it's very slow.
2222 * find a better way instead
2224 UINT col;
2226 if(!warned) {
2227 warned = TRUE;
2228 FIXME("Doing a pixel by pixel render target -> texture copy, expect performance issues\n");
2231 for(col = drect->x1; col < drect->x2; col++) {
2232 glCopyTexSubImage2D(This->glDescription.target,
2233 This->glDescription.level,
2234 drect->x1 + col, row, /* xoffset, yoffset */
2235 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2236 1, 1);
2238 } else {
2239 glCopyTexSubImage2D(This->glDescription.target,
2240 This->glDescription.level,
2241 drect->x1, row, /* xoffset, yoffset */
2242 srect->x1, yoffset - (int) (row * yrel),
2243 drect->x2-drect->x1, 1);
2248 vcheckGLcall("glCopyTexSubImage2D");
2249 LEAVE_GL();
2252 /* Uses the hardware to stretch and flip the image */
2253 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown) {
2254 GLuint src, backup = 0;
2255 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2256 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2257 float left, right, top, bottom; /* Texture coordinates */
2258 UINT fbwidth = Src->currentDesc.Width;
2259 UINT fbheight = Src->currentDesc.Height;
2261 TRACE("Using hwstretch blit\n");
2262 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2263 ENTER_GL();
2264 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2266 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2267 glGenTextures(1, &backup);
2268 checkGLcall("glGenTextures\n");
2269 glBindTexture(GL_TEXTURE_2D, backup);
2270 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2271 } else {
2272 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2273 * we are reading from the back buffer, the backup can be used as source texture
2275 if(Src->glDescription.textureName == 0) {
2276 /* Get it a description */
2277 IWineD3DSurface_PreLoad(SrcSurface);
2279 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
2280 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2283 glReadBuffer(GL_BACK);
2284 checkGLcall("glReadBuffer(GL_BACK)");
2286 /* TODO: Only back up the part that will be overwritten */
2287 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2288 0, 0 /* read offsets */,
2289 0, 0,
2290 fbwidth,
2291 fbheight);
2293 checkGLcall("glCopyTexSubImage2D");
2295 /* No issue with overriding these - the sampler is dirty due to blit usage */
2296 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2297 checkGLcall("glTexParameteri");
2298 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2299 checkGLcall("glTexParameteri");
2301 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2302 src = backup ? backup : Src->glDescription.textureName;
2303 } else {
2304 glReadBuffer(GL_FRONT);
2305 checkGLcall("glReadBuffer(GL_FRONT)");
2307 glGenTextures(1, &src);
2308 checkGLcall("glGenTextures(1, &src)");
2309 glBindTexture(GL_TEXTURE_2D, src);
2310 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2312 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2313 * out for power of 2 sizes
2315 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2316 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2317 checkGLcall("glTexImage2D");
2318 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2319 0, 0 /* read offsets */,
2320 0, 0,
2321 fbwidth,
2322 fbheight);
2324 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2325 checkGLcall("glTexParameteri");
2326 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2327 checkGLcall("glTexParameteri");
2329 glReadBuffer(GL_BACK);
2330 checkGLcall("glReadBuffer(GL_BACK)");
2332 checkGLcall("glEnd and previous");
2334 left = (float) srect->x1 / (float) Src->pow2Width;
2335 right = (float) srect->x2 / (float) Src->pow2Width;
2337 if(upsidedown) {
2338 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2339 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2340 } else {
2341 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2342 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2345 /* draw the source texture stretched and upside down. The correct surface is bound already */
2346 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
2347 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
2349 glBegin(GL_QUADS);
2350 /* bottom left */
2351 glTexCoord2f(left, bottom);
2352 glVertex2i(0, fbheight);
2354 /* top left */
2355 glTexCoord2f(left, top);
2356 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2358 /* top right */
2359 glTexCoord2f(right, top);
2360 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2362 /* bottom right */
2363 glTexCoord2f(right, bottom);
2364 glVertex2i(drect->x2 - drect->x1, fbheight);
2365 glEnd();
2366 checkGLcall("glEnd and previous");
2368 /* Now read the stretched and upside down image into the destination texture */
2369 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2370 checkGLcall("glBindTexture");
2371 glCopyTexSubImage2D(This->glDescription.target,
2373 drect->x1, drect->y1, /* xoffset, yoffset */
2374 0, 0, /* We blitted the image to the origin */
2375 drect->x2 - drect->x1, drect->y2 - drect->y1);
2376 checkGLcall("glCopyTexSubImage2D");
2378 /* Write the back buffer backup back */
2379 glBindTexture(GL_TEXTURE_2D, backup ? backup : Src->glDescription.textureName);
2380 checkGLcall("glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName)");
2382 glBegin(GL_QUADS);
2383 /* top left */
2384 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2385 glVertex2i(0, 0);
2387 /* bottom left */
2388 glTexCoord2f(0.0, 0.0);
2389 glVertex2i(0, fbheight);
2391 /* bottom right */
2392 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2393 glVertex2i(fbwidth, Src->currentDesc.Height);
2395 /* top right */
2396 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2397 glVertex2i(fbwidth, 0);
2398 glEnd();
2400 /* Cleanup */
2401 if(src != Src->glDescription.textureName && src != backup) {
2402 glDeleteTextures(1, &src);
2403 checkGLcall("glDeleteTextures(1, &src)");
2405 if(backup) {
2406 glDeleteTextures(1, &backup);
2407 checkGLcall("glDeleteTextures(1, &backup)");
2409 LEAVE_GL();
2412 /* Not called from the VTable */
2413 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, DDBLTFX *DDBltFx) {
2414 WINED3DRECT rect;
2415 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2416 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2417 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2418 BOOL SrcOK = TRUE;
2420 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2422 /* Get the swapchain. One of the surfaces has to be a primary surface */
2423 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2424 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2425 if(Src) {
2426 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2427 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2430 /* Early sort out of cases where no render target is used */
2431 if(!dstSwapchain && !srcSwapchain &&
2432 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2433 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2434 return WINED3DERR_INVALIDCALL;
2437 /* No destination color keying supported */
2438 if(Flags & (DDBLT_KEYDEST | DDBLT_KEYDESTOVERRIDE)) {
2439 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2440 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2441 return WINED3DERR_INVALIDCALL;
2444 if (DestRect) {
2445 rect.x1 = DestRect->left;
2446 rect.y1 = DestRect->top;
2447 rect.x2 = DestRect->right;
2448 rect.y2 = DestRect->bottom;
2449 } else {
2450 rect.x1 = 0;
2451 rect.y1 = 0;
2452 rect.x2 = This->currentDesc.Width;
2453 rect.y2 = This->currentDesc.Height;
2456 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2457 if(dstSwapchain && dstSwapchain == srcSwapchain) {
2458 /* Half-life does a Blt from the back buffer to the front buffer,
2459 * Full surface size, no flags... Use present instead
2462 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2463 if( SrcRect ) {
2464 if( (SrcRect->left == 0) && (SrcRect->top == 0) &&
2465 (SrcRect->right == Src->currentDesc.Width) && (SrcRect->bottom == Src->currentDesc.Height) ) {
2466 SrcOK = TRUE;
2468 } else {
2469 SrcOK = TRUE;
2472 /* Check the Destination rect and the surface sizes */
2473 if(SrcOK &&
2474 (rect.x1 == 0) && (rect.y1 == 0) &&
2475 (rect.x2 == This->currentDesc.Width) && (rect.y2 == This->currentDesc.Height) &&
2476 (This->currentDesc.Width == Src->currentDesc.Width) &&
2477 (This->currentDesc.Height == Src->currentDesc.Height)) {
2478 /* These flags are unimportant for the flag check, remove them */
2480 if((Flags & ~(DDBLT_DONOTWAIT | DDBLT_WAIT)) == 0) {
2481 if( dstSwapchain->backBuffer && ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) &&
2482 SrcSurface == dstSwapchain->backBuffer[0] ) {
2484 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
2486 /* The idea behind this is that a glReadPixels and a glDrawPixels call
2487 * take very long, while a flip is fast.
2488 * This applies to Half-Life, which does such Blts every time it finished
2489 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
2490 * menu. This is also used by all apps when they do windowed rendering
2492 * The problem is that flipping is not really the same as copying. After a
2493 * Blt the front buffer is a copy of the back buffer, and the back buffer is
2494 * untouched. Therefore it's necessary to override the swap effect
2495 * and to set it back after the flip.
2498 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
2500 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
2501 IWineD3DDevice_Present((IWineD3DDevice *) This->resource.wineD3DDevice,
2502 NULL, NULL, 0, NULL);
2504 dstSwapchain->presentParms.SwapEffect = orig_swap;
2506 return WINED3D_OK;
2511 TRACE("Unsupported blit between buffers on the same swapchain\n");
2512 return WINED3DERR_INVALIDCALL;
2513 } else if((dstSwapchain || This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) &&
2514 (srcSwapchain || SrcSurface == myDevice->render_targets[0]) ) {
2515 ERR("Can't perform hardware blit between 2 different swapchains, falling back to software\n");
2516 return WINED3DERR_INVALIDCALL;
2519 if(srcSwapchain || SrcSurface == myDevice->render_targets[0]) {
2520 /* Blit from render target to texture */
2521 WINED3DRECT srect;
2522 BOOL upsideDown, stretchx;
2524 if(Flags & (DDBLT_KEYSRC | DDBLT_KEYSRCOVERRIDE)) {
2525 TRACE("Color keying not supported by frame buffer to texture blit\n");
2526 return WINED3DERR_INVALIDCALL;
2527 /* Destination color key is checked above */
2530 /* Call preload for the surface to make sure it isn't dirty */
2531 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2533 /* Make sure that the top pixel is always above the bottom pixel, and keep a seperate upside down flag
2534 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2536 if(SrcRect) {
2537 if(SrcRect->top < SrcRect->bottom) {
2538 srect.y1 = SrcRect->top;
2539 srect.y2 = SrcRect->bottom;
2540 upsideDown = FALSE;
2541 } else {
2542 srect.y1 = SrcRect->bottom;
2543 srect.y2 = SrcRect->top;
2544 upsideDown = TRUE;
2546 srect.x1 = SrcRect->left;
2547 srect.x2 = SrcRect->right;
2548 } else {
2549 srect.x1 = 0;
2550 srect.y1 = 0;
2551 srect.x2 = Src->currentDesc.Width;
2552 srect.y2 = Src->currentDesc.Height;
2553 upsideDown = FALSE;
2555 if(rect.x1 > rect.x2) {
2556 UINT tmp = rect.x2;
2557 rect.x2 = rect.x1;
2558 rect.x1 = tmp;
2559 upsideDown = !upsideDown;
2561 if(!srcSwapchain) {
2562 TRACE("Reading from an offscreen target\n");
2563 upsideDown = !upsideDown;
2566 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
2567 stretchx = TRUE;
2568 } else {
2569 stretchx = FALSE;
2572 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2573 * flip the image nor scale it. If GL_EXT_framebuffer_blit is available it can be used(hopefully,
2574 * not implemented by now). Otherwise:
2576 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2577 * -> If the app wants a image width an unscaled width, copy it line per line
2578 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
2579 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2580 * back buffer. This is slower than reading line per line, thus not used for flipping
2581 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2582 * pixel by pixel
2584 if(FALSE /* GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) */) {
2585 TRACE("Using GL_EXT_framebuffer_blit for copying\n");
2586 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
2587 rect.y2 - rect.y1 > Src->currentDesc.Height) {
2588 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
2589 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown);
2590 } else {
2591 TRACE("Using hardware stretching to flip / stretch the texture\n");
2592 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown);
2595 if(!(This->Flags & SFLAG_DONOTFREE)) {
2596 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
2597 This->resource.allocatedMemory = NULL;
2598 } else {
2599 This->Flags |= SFLAG_GLDIRTY;
2602 return WINED3D_OK;
2603 } else if(Src) {
2604 /* Blit from offscreen surface to render target */
2605 float glTexCoord[4];
2606 DWORD oldCKeyFlags = Src->CKeyFlags;
2607 DDCOLORKEY oldBltCKey = This->SrcBltCKey;
2608 RECT SourceRectangle;
2609 GLint oldDraw;
2611 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
2613 if(SrcRect) {
2614 SourceRectangle.left = SrcRect->left;
2615 SourceRectangle.right = SrcRect->right;
2616 SourceRectangle.top = SrcRect->top;
2617 SourceRectangle.bottom = SrcRect->bottom;
2618 } else {
2619 SourceRectangle.left = 0;
2620 SourceRectangle.right = Src->currentDesc.Width;
2621 SourceRectangle.top = 0;
2622 SourceRectangle.bottom = Src->currentDesc.Height;
2625 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
2626 /* Fall back to software */
2627 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
2628 SourceRectangle.left, SourceRectangle.top,
2629 SourceRectangle.right, SourceRectangle.bottom);
2630 return WINED3DERR_INVALIDCALL;
2633 /* Color keying: Check if we have to do a color keyed blt,
2634 * and if not check if a color key is activated.
2636 * Just modify the color keying parameters in the surface and restore them afterwards
2637 * The surface keeps track of the color key last used to load the opengl surface.
2638 * PreLoad will catch the change to the flags and color key and reload if neccessary.
2640 if(Flags & DDBLT_KEYSRC) {
2641 /* Use color key from surface */
2642 } else if(Flags & DDBLT_KEYSRCOVERRIDE) {
2643 /* Use color key from DDBltFx */
2644 Src->CKeyFlags |= DDSD_CKSRCBLT;
2645 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
2646 } else {
2647 /* Do not use color key */
2648 Src->CKeyFlags &= ~DDSD_CKSRCBLT;
2651 /* Now load the surface */
2652 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
2654 ENTER_GL();
2656 /* Activate the destination context, set it up for blitting */
2657 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
2659 glGetIntegerv(GL_DRAW_BUFFER, &oldDraw);
2660 if(This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer) {
2661 TRACE("Drawing to front buffer\n");
2662 glDrawBuffer(GL_FRONT);
2663 checkGLcall("glDrawBuffer GL_FRONT");
2666 /* Bind the texture */
2667 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
2668 checkGLcall("glBindTexture");
2670 /* No filtering for blts */
2671 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
2672 GL_NEAREST);
2673 checkGLcall("glTexParameteri");
2674 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
2675 GL_NEAREST);
2676 checkGLcall("glTexParameteri");
2677 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
2678 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
2679 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2680 checkGLcall("glTexEnvi");
2682 /* This is for color keying */
2683 if(Flags & (DDBLT_KEYSRC | DDBLT_KEYSRCOVERRIDE)) {
2684 glEnable(GL_ALPHA_TEST);
2685 checkGLcall("glEnable GL_ALPHA_TEST");
2686 glAlphaFunc(GL_NOTEQUAL, 0.0);
2687 checkGLcall("glAlphaFunc\n");
2688 } else {
2689 glDisable(GL_ALPHA_TEST);
2690 checkGLcall("glDisable GL_ALPHA_TEST");
2693 /* Draw a textured quad
2695 glBegin(GL_QUADS);
2697 glColor3d(1.0f, 1.0f, 1.0f);
2698 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
2699 glVertex3f(rect.x1,
2700 rect.y1,
2701 0.0);
2703 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
2704 glVertex3f(rect.x1, rect.y2, 0.0);
2706 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
2707 glVertex3f(rect.x2,
2708 rect.y2,
2709 0.0);
2711 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
2712 glVertex3f(rect.x2,
2713 rect.y1,
2714 0.0);
2715 glEnd();
2716 checkGLcall("glEnd");
2718 if(Flags & (DDBLT_KEYSRC | DDBLT_KEYSRCOVERRIDE)) {
2719 glDisable(GL_ALPHA_TEST);
2720 checkGLcall("glDisable(GL_ALPHA_TEST)");
2723 /* Unbind the texture */
2724 glBindTexture(GL_TEXTURE_2D, 0);
2725 checkGLcall("glEnable glBindTexture");
2727 if(This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && oldDraw == GL_BACK) {
2728 glDrawBuffer(oldDraw);
2730 /* Restore the color key parameters */
2731 Src->CKeyFlags = oldCKeyFlags;
2732 This->SrcBltCKey = oldBltCKey;
2734 LEAVE_GL();
2736 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
2737 This->Flags |= SFLAG_GLDIRTY;
2739 return WINED3D_OK;
2740 } else {
2741 /* Source-Less Blit to render target */
2742 if (Flags & DDBLT_COLORFILL) {
2743 /* This is easy to handle for the D3D Device... */
2744 DWORD color;
2746 TRACE("Colorfill\n");
2748 /* The color as given in the Blt function is in the format of the frame-buffer...
2749 * 'clear' expect it in ARGB format => we need to do some conversion :-)
2751 if (This->resource.format == WINED3DFMT_P8) {
2752 if (This->palette) {
2753 color = ((0xFF000000) |
2754 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
2755 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
2756 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
2757 } else {
2758 color = 0xFF000000;
2761 else if (This->resource.format == WINED3DFMT_R5G6B5) {
2762 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
2763 color = 0xFFFFFFFF;
2764 } else {
2765 color = ((0xFF000000) |
2766 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
2767 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
2768 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
2771 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
2772 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
2773 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
2775 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
2776 color = DDBltFx->u5.dwFillColor;
2778 else {
2779 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
2780 return WINED3DERR_INVALIDCALL;
2783 TRACE("Calling GetSwapChain with mydevice = %p\n", myDevice);
2784 if(dstSwapchain && dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]) {
2785 glDrawBuffer(GL_BACK);
2786 checkGLcall("glDrawBuffer(GL_BACK)");
2787 } else if (dstSwapchain && This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer) {
2788 glDrawBuffer(GL_FRONT);
2789 checkGLcall("glDrawBuffer(GL_FRONT)");
2790 } else if(This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2791 glDrawBuffer(myDevice->offscreenBuffer);
2792 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer3)");
2793 } else {
2794 TRACE("Surface is higher back buffer, falling back to software\n");
2795 return WINED3DERR_INVALIDCALL;
2798 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
2800 IWineD3DDevice_Clear( (IWineD3DDevice *) myDevice,
2801 1 /* Number of rectangles */,
2802 &rect,
2803 WINED3DCLEAR_TARGET,
2804 color,
2805 0.0 /* Z */,
2806 0 /* Stencil */);
2808 /* Restore the original draw buffer */
2809 if(!dstSwapchain) {
2810 glDrawBuffer(myDevice->offscreenBuffer);
2811 } else if(dstSwapchain->backBuffer && dstSwapchain->backBuffer[0]) {
2812 glDrawBuffer(GL_BACK);
2814 vcheckGLcall("glDrawBuffer");
2816 return WINED3D_OK;
2820 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
2821 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
2822 return WINED3DERR_INVALIDCALL;
2825 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, DDBLTFX *DDBltFx) {
2826 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2827 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2828 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2829 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2830 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
2832 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair */
2833 if(myDevice->inScene &&
2834 (iface == myDevice->stencilBufferTarget ||
2835 (SrcSurface && SrcSurface == myDevice->stencilBufferTarget))) {
2836 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
2837 return WINED3DERR_INVALIDCALL;
2840 /* Special cases for RenderTargets */
2841 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
2842 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
2843 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) return WINED3D_OK;
2846 /* For the rest call the X11 surface implementation.
2847 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
2848 * other Blts are rather rare
2850 return IWineGDISurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2853 HRESULT WINAPI IWineD3DSurfaceImpl_GetBltStatus(IWineD3DSurface *iface, DWORD Flags) {
2854 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2855 TRACE("(%p)->(%x)\n", This, Flags);
2857 switch (Flags)
2859 case DDGBS_CANBLT:
2860 case DDGBS_ISBLTDONE:
2861 return DD_OK;
2863 default:
2864 return DDERR_INVALIDPARAMS;
2868 HRESULT WINAPI IWineD3DSurfaceImpl_GetFlipStatus(IWineD3DSurface *iface, DWORD Flags) {
2869 /* XXX: DDERR_INVALIDSURFACETYPE */
2871 TRACE("(%p)->(%08x)\n",iface,Flags);
2872 switch (Flags) {
2873 case DDGFS_CANFLIP:
2874 case DDGFS_ISFLIPDONE:
2875 return DD_OK;
2877 default:
2878 return DDERR_INVALIDPARAMS;
2882 HRESULT WINAPI IWineD3DSurfaceImpl_IsLost(IWineD3DSurface *iface) {
2883 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2884 TRACE("(%p)\n", This);
2886 return This->Flags & SFLAG_LOST ? DDERR_SURFACELOST : WINED3D_OK;
2889 HRESULT WINAPI IWineD3DSurfaceImpl_Restore(IWineD3DSurface *iface) {
2890 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2891 TRACE("(%p)\n", This);
2893 /* So far we don't lose anything :) */
2894 This->Flags &= ~SFLAG_LOST;
2895 return WINED3D_OK;
2898 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
2899 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2900 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
2901 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2902 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
2904 if(myDevice->inScene &&
2905 (iface == myDevice->stencilBufferTarget ||
2906 (Source && Source == myDevice->stencilBufferTarget))) {
2907 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
2908 return WINED3DERR_INVALIDCALL;
2911 /* Special cases for RenderTargets */
2912 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
2913 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
2915 RECT SrcRect, DstRect;
2916 DWORD Flags=0;
2918 if(rsrc) {
2919 SrcRect.left = rsrc->left;
2920 SrcRect.top= rsrc->top;
2921 SrcRect.bottom = rsrc->bottom;
2922 SrcRect.right = rsrc->right;
2923 } else {
2924 SrcRect.left = 0;
2925 SrcRect.top = 0;
2926 SrcRect.right = srcImpl->currentDesc.Width;
2927 SrcRect.bottom = srcImpl->currentDesc.Height;
2930 DstRect.left = dstx;
2931 DstRect.top=dsty;
2932 DstRect.right = dstx + SrcRect.right - SrcRect.left;
2933 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
2935 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
2936 if(trans & DDBLTFAST_SRCCOLORKEY)
2937 Flags |= DDBLT_KEYSRC;
2938 if(trans & DDBLTFAST_DESTCOLORKEY)
2939 Flags |= DDBLT_KEYDEST;
2940 if(trans & DDBLTFAST_WAIT)
2941 Flags |= DDBLT_WAIT;
2942 if(trans & DDBLTFAST_DONOTWAIT)
2943 Flags |= DDBLT_DONOTWAIT;
2945 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL) == WINED3D_OK) return WINED3D_OK;
2949 return IWineGDISurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
2952 HRESULT WINAPI IWineD3DSurfaceImpl_GetPalette(IWineD3DSurface *iface, IWineD3DPalette **Pal) {
2953 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2954 TRACE("(%p)->(%p)\n", This, Pal);
2956 *Pal = (IWineD3DPalette *) This->palette;
2957 return DD_OK;
2960 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
2961 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2962 RGBQUAD col[256];
2963 IWineD3DPaletteImpl *pal = This->palette;
2964 unsigned int n;
2965 TRACE("(%p)\n", This);
2967 if(This->resource.format == WINED3DFMT_P8 ||
2968 This->resource.format == WINED3DFMT_A8P8)
2970 TRACE("Dirtifying surface\n");
2971 This->Flags |= SFLAG_DIRTY;
2974 if(This->Flags & SFLAG_DIBSECTION) {
2975 TRACE("(%p): Updating the hdc's palette\n", This);
2976 for (n=0; n<256; n++) {
2977 if(pal) {
2978 col[n].rgbRed = pal->palents[n].peRed;
2979 col[n].rgbGreen = pal->palents[n].peGreen;
2980 col[n].rgbBlue = pal->palents[n].peBlue;
2981 } else {
2982 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2983 /* Use the default device palette */
2984 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
2985 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
2986 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
2988 col[n].rgbReserved = 0;
2990 SetDIBColorTable(This->hDC, 0, 256, col);
2993 return WINED3D_OK;
2996 HRESULT WINAPI IWineD3DSurfaceImpl_SetPalette(IWineD3DSurface *iface, IWineD3DPalette *Pal) {
2997 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2998 IWineD3DPaletteImpl *PalImpl = (IWineD3DPaletteImpl *) Pal;
2999 TRACE("(%p)->(%p)\n", This, Pal);
3001 if(This->palette != NULL)
3002 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3003 This->palette->Flags &= ~DDPCAPS_PRIMARYSURFACE;
3005 if(PalImpl != NULL) {
3006 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3007 /* Set the device's main palette if the palette
3008 * wasn't a primary palette before
3010 if(!(PalImpl->Flags & DDPCAPS_PRIMARYSURFACE)) {
3011 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3012 unsigned int i;
3014 for(i=0; i < 256; i++) {
3015 device->palettes[device->currentPalette][i] = PalImpl->palents[i];
3019 (PalImpl)->Flags |= DDPCAPS_PRIMARYSURFACE;
3022 This->palette = PalImpl;
3024 return IWineD3DSurface_RealizePalette(iface);
3027 HRESULT WINAPI IWineD3DSurfaceImpl_SetColorKey(IWineD3DSurface *iface, DWORD Flags, DDCOLORKEY *CKey) {
3028 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3029 TRACE("(%p)->(%08x,%p)\n", This, Flags, CKey);
3031 if ((Flags & DDCKEY_COLORSPACE) != 0) {
3032 FIXME(" colorkey value not supported (%08x) !\n", Flags);
3033 return DDERR_INVALIDPARAMS;
3036 /* Dirtify the surface, but only if a key was changed */
3037 if(CKey) {
3038 switch (Flags & ~DDCKEY_COLORSPACE) {
3039 case DDCKEY_DESTBLT:
3040 This->DestBltCKey = *CKey;
3041 This->CKeyFlags |= DDSD_CKDESTBLT;
3042 break;
3044 case DDCKEY_DESTOVERLAY:
3045 This->DestOverlayCKey = *CKey;
3046 This->CKeyFlags |= DDSD_CKDESTOVERLAY;
3047 break;
3049 case DDCKEY_SRCOVERLAY:
3050 This->SrcOverlayCKey = *CKey;
3051 This->CKeyFlags |= DDSD_CKSRCOVERLAY;
3052 break;
3054 case DDCKEY_SRCBLT:
3055 This->SrcBltCKey = *CKey;
3056 This->CKeyFlags |= DDSD_CKSRCBLT;
3057 break;
3060 else {
3061 switch (Flags & ~DDCKEY_COLORSPACE) {
3062 case DDCKEY_DESTBLT:
3063 This->CKeyFlags &= ~DDSD_CKDESTBLT;
3064 break;
3066 case DDCKEY_DESTOVERLAY:
3067 This->CKeyFlags &= ~DDSD_CKDESTOVERLAY;
3068 break;
3070 case DDCKEY_SRCOVERLAY:
3071 This->CKeyFlags &= ~DDSD_CKSRCOVERLAY;
3072 break;
3074 case DDCKEY_SRCBLT:
3075 This->CKeyFlags &= ~DDSD_CKSRCBLT;
3076 break;
3080 return WINED3D_OK;
3083 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3084 /** Check against the maximum texture sizes supported by the video card **/
3085 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3087 TRACE("%p\n", This);
3088 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3089 /* one of three options
3090 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)
3091 2: Set the texture to the maxium size (bad idea)
3092 3: WARN and return WINED3DERR_NOTAVAILABLE;
3093 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.
3095 WARN("(%p) Creating an oversized surface\n", This);
3096 This->Flags |= SFLAG_OVERSIZE;
3098 /* This will be initialized on the first blt */
3099 This->glRect.left = 0;
3100 This->glRect.top = 0;
3101 This->glRect.right = 0;
3102 This->glRect.bottom = 0;
3103 } else {
3104 /* No oversize, gl rect is the full texture size */
3105 This->Flags &= ~SFLAG_OVERSIZE;
3106 This->glRect.left = 0;
3107 This->glRect.top = 0;
3108 This->glRect.right = This->pow2Width;
3109 This->glRect.bottom = This->pow2Height;
3112 return WINED3D_OK;
3115 DWORD WINAPI IWineD3DSurfaceImpl_GetPitch(IWineD3DSurface *iface) {
3116 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3117 DWORD ret;
3118 TRACE("(%p)\n", This);
3120 /* DXTn formats don't have exact pitches as they are to the new row of blocks,
3121 where each block is 4x4 pixels, 8 bytes (dxt1) and 16 bytes (dxt2/3/4/5)
3122 ie pitch = (width/4) * bytes per block */
3123 if (This->resource.format == WINED3DFMT_DXT1) /* DXT1 is 8 bytes per block */
3124 ret = ((This->currentDesc.Width + 3) >> 2) << 3;
3125 else if (This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
3126 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) /* DXT2/3/4/5 is 16 bytes per block */
3127 ret = ((This->currentDesc.Width + 3) >> 2) << 4;
3128 else {
3129 ret = This->bytesPerPixel * This->currentDesc.Width; /* Bytes / row */
3130 /* Surfaces are 32 bit aligned */
3131 ret = (ret + SURFACE_ALIGNMENT - 1) & ~(SURFACE_ALIGNMENT - 1);
3133 TRACE("(%p) Returning %d\n", This, ret);
3134 return ret;
3137 HRESULT WINAPI IWineD3DSurfaceImpl_SetOverlayPosition(IWineD3DSurface *iface, LONG X, LONG Y) {
3138 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3140 FIXME("(%p)->(%d,%d) Stub!\n", This, X, Y);
3142 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3144 TRACE("(%p): Not an overlay surface\n", This);
3145 return DDERR_NOTAOVERLAYSURFACE;
3148 return WINED3D_OK;
3151 HRESULT WINAPI IWineD3DSurfaceImpl_GetOverlayPosition(IWineD3DSurface *iface, LONG *X, LONG *Y) {
3152 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3154 FIXME("(%p)->(%p,%p) Stub!\n", This, X, Y);
3156 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3158 TRACE("(%p): Not an overlay surface\n", This);
3159 return DDERR_NOTAOVERLAYSURFACE;
3162 return WINED3D_OK;
3165 HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlayZOrder(IWineD3DSurface *iface, DWORD Flags, IWineD3DSurface *Ref) {
3166 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3167 IWineD3DSurfaceImpl *RefImpl = (IWineD3DSurfaceImpl *) Ref;
3169 FIXME("(%p)->(%08x,%p) Stub!\n", This, Flags, RefImpl);
3171 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3173 TRACE("(%p): Not an overlay surface\n", This);
3174 return DDERR_NOTAOVERLAYSURFACE;
3177 return WINED3D_OK;
3180 HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlay(IWineD3DSurface *iface, RECT *SrcRect, IWineD3DSurface *DstSurface, RECT *DstRect, DWORD Flags, WINEDDOVERLAYFX *FX) {
3181 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3182 IWineD3DSurfaceImpl *Dst = (IWineD3DSurfaceImpl *) DstSurface;
3183 FIXME("(%p)->(%p, %p, %p, %08x, %p)\n", This, SrcRect, Dst, DstRect, Flags, FX);
3185 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3187 TRACE("(%p): Not an overlay surface\n", This);
3188 return DDERR_NOTAOVERLAYSURFACE;
3191 return WINED3D_OK;
3194 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
3196 /* IUnknown */
3197 IWineD3DSurfaceImpl_QueryInterface,
3198 IWineD3DSurfaceImpl_AddRef,
3199 IWineD3DSurfaceImpl_Release,
3200 /* IWineD3DResource */
3201 IWineD3DSurfaceImpl_GetParent,
3202 IWineD3DSurfaceImpl_GetDevice,
3203 IWineD3DSurfaceImpl_SetPrivateData,
3204 IWineD3DSurfaceImpl_GetPrivateData,
3205 IWineD3DSurfaceImpl_FreePrivateData,
3206 IWineD3DSurfaceImpl_SetPriority,
3207 IWineD3DSurfaceImpl_GetPriority,
3208 IWineD3DSurfaceImpl_PreLoad,
3209 IWineD3DSurfaceImpl_GetType,
3210 /* IWineD3DSurface */
3211 IWineD3DSurfaceImpl_GetContainer,
3212 IWineD3DSurfaceImpl_GetDesc,
3213 IWineD3DSurfaceImpl_LockRect,
3214 IWineD3DSurfaceImpl_UnlockRect,
3215 IWineD3DSurfaceImpl_GetDC,
3216 IWineD3DSurfaceImpl_ReleaseDC,
3217 IWineD3DSurfaceImpl_Flip,
3218 IWineD3DSurfaceImpl_Blt,
3219 IWineD3DSurfaceImpl_GetBltStatus,
3220 IWineD3DSurfaceImpl_GetFlipStatus,
3221 IWineD3DSurfaceImpl_IsLost,
3222 IWineD3DSurfaceImpl_Restore,
3223 IWineD3DSurfaceImpl_BltFast,
3224 IWineD3DSurfaceImpl_GetPalette,
3225 IWineD3DSurfaceImpl_SetPalette,
3226 IWineD3DSurfaceImpl_RealizePalette,
3227 IWineD3DSurfaceImpl_SetColorKey,
3228 IWineD3DSurfaceImpl_GetPitch,
3229 IWineD3DSurfaceImpl_SetMem,
3230 IWineD3DSurfaceImpl_SetOverlayPosition,
3231 IWineD3DSurfaceImpl_GetOverlayPosition,
3232 IWineD3DSurfaceImpl_UpdateOverlayZOrder,
3233 IWineD3DSurfaceImpl_UpdateOverlay,
3234 /* Internal use: */
3235 IWineD3DSurfaceImpl_CleanDirtyRect,
3236 IWineD3DSurfaceImpl_AddDirtyRect,
3237 IWineD3DSurfaceImpl_LoadTexture,
3238 IWineD3DSurfaceImpl_SaveSnapshot,
3239 IWineD3DSurfaceImpl_SetContainer,
3240 IWineD3DSurfaceImpl_SetPBufferState,
3241 IWineD3DSurfaceImpl_SetGlTextureDesc,
3242 IWineD3DSurfaceImpl_GetGlDesc,
3243 IWineD3DSurfaceImpl_GetData,
3244 IWineD3DSurfaceImpl_SetFormat,
3245 IWineD3DSurfaceImpl_PrivateSetup