wined3d: Initialize surfaces at creation.
[wine.git] / dlls / wined3d / surface.c
blob07467be3f1004f139b283c820786da568624be6d
1 /*
2 * IWineD3DSurface Implementation
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006 Stefan Dösinger for CodeWeavers
11 * Copyright 2007 Henri Verbeet
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 #include "config.h"
29 #include "wine/port.h"
30 #include "wined3d_private.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
33 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
35 typedef enum {
36 NO_CONVERSION,
37 CONVERT_PALETTED,
38 CONVERT_PALETTED_CK,
39 CONVERT_CK_565,
40 CONVERT_CK_5551,
41 CONVERT_CK_4444,
42 CONVERT_CK_4444_ARGB,
43 CONVERT_CK_1555,
44 CONVERT_555,
45 CONVERT_CK_RGB24,
46 CONVERT_CK_8888,
47 CONVERT_CK_8888_ARGB,
48 CONVERT_RGB32_888,
49 CONVERT_V8U8,
50 CONVERT_X8L8V8U8,
51 CONVERT_Q8W8V8U8,
52 CONVERT_V16U16
53 } CONVERT_TYPES;
55 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf);
57 static void surface_download_data(IWineD3DSurfaceImpl *This) {
58 if (!This->resource.allocatedMemory) This->resource.allocatedMemory = HeapAlloc(GetProcessHeap(), 0, This->resource.size + 4);
59 if (This->resource.format == WINED3DFMT_DXT1 ||
60 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
61 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
62 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
63 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
64 } else {
65 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
66 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
68 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
69 checkGLcall("glGetCompressedTexImageARB()");
71 } else {
72 void *mem;
73 int src_pitch = 0;
74 int dst_pitch = 0;
76 if(This->Flags & SFLAG_CONVERTED) {
77 FIXME("Read back converted textures unsupported\n");
78 return;
81 if (This->Flags & SFLAG_NONPOW2) {
82 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
83 src_pitch = This->bytesPerPixel * This->pow2Width;
84 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
85 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
86 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
87 } else {
88 mem = This->resource.allocatedMemory;
91 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
92 This->glDescription.glFormat, This->glDescription.glType, mem);
94 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
95 This->glDescription.glType, mem);
96 checkGLcall("glGetTexImage()");
98 if (This->Flags & SFLAG_NONPOW2) {
99 LPBYTE src_data, dst_data;
100 int y;
102 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
103 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
104 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
106 * We're doing this...
108 * instead of boxing the texture :
109 * |<-texture width ->| -->pow2width| /\
110 * |111111111111111111| | |
111 * |222 Texture 222222| boxed empty | texture height
112 * |3333 Data 33333333| | |
113 * |444444444444444444| | \/
114 * ----------------------------------- |
115 * | boxed empty | boxed empty | pow2height
116 * | | | \/
117 * -----------------------------------
120 * we're repacking the data to the expected texture width
122 * |<-texture width ->| -->pow2width| /\
123 * |111111111111111111222222222222222| |
124 * |222333333333333333333444444444444| texture height
125 * |444444 | |
126 * | | \/
127 * | | |
128 * | empty | pow2height
129 * | | \/
130 * -----------------------------------
132 * == is the same as
134 * |<-texture width ->| /\
135 * |111111111111111111|
136 * |222222222222222222|texture height
137 * |333333333333333333|
138 * |444444444444444444| \/
139 * --------------------
141 * this also means that any references to allocatedMemory should work with the data as if were a
142 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
144 * internally the texture is still stored in a boxed format so any references to textureName will
145 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
147 * Performance should not be an issue, because applications normally do not lock the surfaces when
148 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
149 * and doesn't have to be re-read.
151 src_data = mem;
152 dst_data = This->resource.allocatedMemory;
153 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
154 for (y = 1 ; y < This->currentDesc.Height; y++) {
155 /* skip the first row */
156 src_data += src_pitch;
157 dst_data += dst_pitch;
158 memcpy(dst_data, src_data, dst_pitch);
161 HeapFree(GetProcessHeap(), 0, mem);
164 /* Surface has now been downloaded */
165 This->Flags |= SFLAG_INSYSMEM;
168 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, 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 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
176 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
177 This->Flags |= SFLAG_CLIENT;
180 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
181 ENTER_GL();
182 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
183 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
184 * function uses glCompressedTexImage2D instead of the SubImage call
186 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
187 width, height, 0 /* border */, This->resource.size, data));
188 checkGLcall("glCompressedTexSubImage2D");
189 LEAVE_GL();
191 } else {
192 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
193 ENTER_GL();
194 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
195 checkGLcall("glTexSubImage2D");
196 LEAVE_GL();
200 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
201 BOOL enable_client_storage = FALSE;
203 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,
204 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
206 if (This->resource.format == WINED3DFMT_DXT1 ||
207 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
208 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
209 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
210 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
211 return;
214 ENTER_GL();
216 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
217 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
218 /* In some cases we want to disable client storage.
219 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
220 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
221 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
222 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
223 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
225 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
226 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
227 This->Flags &= SFLAG_CLIENT;
228 enable_client_storage = TRUE;
229 } else {
230 This->Flags |= SFLAG_CLIENT;
231 /* Below point opengl to our allocated texture memory */
234 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type,
235 This->Flags & SFLAG_CLIENT ? This->resource.allocatedMemory : NULL);
236 checkGLcall("glTexImage2D");
238 if(enable_client_storage) {
239 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
240 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
242 LEAVE_GL();
244 This->Flags |= SFLAG_ALLOCATED;
247 /* In D3D the depth stencil dimensions have to be greater than or equal to the
248 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
249 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
250 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
251 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
252 renderbuffer_entry_t *entry;
253 GLuint renderbuffer = 0;
254 unsigned int src_width, src_height;
256 src_width = This->pow2Width;
257 src_height = This->pow2Height;
259 /* A depth stencil smaller than the render target is not valid */
260 if (width > src_width || height > src_height) return;
262 /* Remove any renderbuffer set if the sizes match */
263 if (width == src_width && height == src_height) {
264 This->current_renderbuffer = NULL;
265 return;
268 /* Look if we've already got a renderbuffer of the correct dimensions */
269 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
270 if (entry->width == width && entry->height == height) {
271 renderbuffer = entry->id;
272 This->current_renderbuffer = entry;
273 break;
277 if (!renderbuffer) {
278 const PixelFormatDesc *format_entry = getFormatDescEntry(This->resource.format);
280 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
281 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
282 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, format_entry->glFormat, width, height));
284 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
285 entry->width = width;
286 entry->height = height;
287 entry->id = renderbuffer;
288 list_add_head(&This->renderbuffers, &entry->entry);
290 This->current_renderbuffer = entry;
293 checkGLcall("set_compatible_renderbuffer");
296 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
297 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
298 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
300 TRACE("(%p) : swapchain %p\n", This, swapchain);
302 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
303 TRACE("Returning GL_BACK\n");
304 return GL_BACK;
305 } else if (swapchain_impl->frontBuffer == iface) {
306 TRACE("Returning GL_FRONT\n");
307 return GL_FRONT;
310 FIXME("Higher back buffer, returning GL_BACK\n");
311 return GL_BACK;
314 /* *******************************************
315 IWineD3DSurface IUnknown parts follow
316 ******************************************* */
317 HRESULT WINAPI IWineD3DSurfaceImpl_QueryInterface(IWineD3DSurface *iface, REFIID riid, LPVOID *ppobj)
319 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
320 /* Warn ,but be nice about things */
321 TRACE("(%p)->(%s,%p)\n", This,debugstr_guid(riid),ppobj);
323 if (IsEqualGUID(riid, &IID_IUnknown)
324 || IsEqualGUID(riid, &IID_IWineD3DBase)
325 || IsEqualGUID(riid, &IID_IWineD3DResource)
326 || IsEqualGUID(riid, &IID_IWineD3DSurface)) {
327 IUnknown_AddRef((IUnknown*)iface);
328 *ppobj = This;
329 return S_OK;
331 *ppobj = NULL;
332 return E_NOINTERFACE;
335 ULONG WINAPI IWineD3DSurfaceImpl_AddRef(IWineD3DSurface *iface) {
336 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
337 ULONG ref = InterlockedIncrement(&This->resource.ref);
338 TRACE("(%p) : AddRef increasing from %d\n", This,ref - 1);
339 return ref;
342 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
343 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
344 ULONG ref = InterlockedDecrement(&This->resource.ref);
345 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
346 if (ref == 0) {
347 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) This->resource.wineD3DDevice;
348 renderbuffer_entry_t *entry, *entry2;
349 TRACE("(%p) : cleaning up\n", This);
351 if(iface == device->lastActiveRenderTarget) {
352 IWineD3DSwapChainImpl *swapchain = device->swapchains ? (IWineD3DSwapChainImpl *) device->swapchains[0] : NULL;
354 TRACE("Last active render target destroyed\n");
355 /* Find a replacement surface for the currently active back buffer. The context manager does not do NULL
356 * checks, so switch to a valid target as long as the currently set surface is still valid. Use the
357 * surface of the implicit swpchain. If that is the same as the destroyed surface the device is destroyed
358 * and the lastActiveRenderTarget member shouldn't matter
360 if(swapchain) {
361 ENTER_GL(); /* For ActivateContext */
362 if(swapchain->backBuffer && swapchain->backBuffer[0] != iface) {
363 TRACE("Activating primary back buffer\n");
364 ActivateContext(device, swapchain->backBuffer[0], CTXUSAGE_RESOURCELOAD);
365 } else if(!swapchain->backBuffer && swapchain->frontBuffer != iface) {
366 /* Single buffering environment */
367 TRACE("Activating primary front buffer\n");
368 ActivateContext(device, swapchain->frontBuffer, CTXUSAGE_RESOURCELOAD);
369 } else {
370 TRACE("Device is being destroyed, setting lastActiveRenderTarget = 0xdeadbabe\n");
371 /* Implicit render target destroyed, that means the device is being destroyed
372 * whatever we set here, it shouldn't matter
374 device->lastActiveRenderTarget = (IWineD3DSurface *) 0xdeadbabe;
376 LEAVE_GL();
377 } else {
378 /* May happen during ddraw uninitialization */
379 TRACE("Render target set, but swapchain does not exist!\n");
380 device->lastActiveRenderTarget = (IWineD3DSurface *) 0xdeadcafe;
384 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
385 ENTER_GL();
387 /* Need a context to destroy the texture. Use the currently active render target, but only if
388 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
389 * When destroying the primary rt, Uninit3D will activate a context before doing anything
391 if(device->render_targets[0]) {
392 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
395 TRACE("Deleting texture %d\n", This->glDescription.textureName);
396 glDeleteTextures(1, &This->glDescription.textureName);
397 LEAVE_GL();
400 if(This->Flags & SFLAG_DIBSECTION) {
401 /* Release the DC */
402 SelectObject(This->hDC, This->dib.holdbitmap);
403 DeleteDC(This->hDC);
404 /* Release the DIB section */
405 DeleteObject(This->dib.DIBsection);
406 This->dib.bitmap_data = NULL;
407 This->resource.allocatedMemory = NULL;
409 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
411 HeapFree(GetProcessHeap(), 0, This->palette9);
413 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
414 if(iface == device->ddraw_primary)
415 device->ddraw_primary = NULL;
417 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
418 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
419 HeapFree(GetProcessHeap(), 0, entry);
422 TRACE("(%p) Released\n", This);
423 HeapFree(GetProcessHeap(), 0, This);
426 return ref;
429 /* ****************************************************
430 IWineD3DSurface IWineD3DResource parts follow
431 **************************************************** */
432 HRESULT WINAPI IWineD3DSurfaceImpl_GetDevice(IWineD3DSurface *iface, IWineD3DDevice** ppDevice) {
433 return IWineD3DResourceImpl_GetDevice((IWineD3DResource *)iface, ppDevice);
436 HRESULT WINAPI IWineD3DSurfaceImpl_SetPrivateData(IWineD3DSurface *iface, REFGUID refguid, CONST void* pData, DWORD SizeOfData, DWORD Flags) {
437 return IWineD3DResourceImpl_SetPrivateData((IWineD3DResource *)iface, refguid, pData, SizeOfData, Flags);
440 HRESULT WINAPI IWineD3DSurfaceImpl_GetPrivateData(IWineD3DSurface *iface, REFGUID refguid, void* pData, DWORD* pSizeOfData) {
441 return IWineD3DResourceImpl_GetPrivateData((IWineD3DResource *)iface, refguid, pData, pSizeOfData);
444 HRESULT WINAPI IWineD3DSurfaceImpl_FreePrivateData(IWineD3DSurface *iface, REFGUID refguid) {
445 return IWineD3DResourceImpl_FreePrivateData((IWineD3DResource *)iface, refguid);
448 DWORD WINAPI IWineD3DSurfaceImpl_SetPriority(IWineD3DSurface *iface, DWORD PriorityNew) {
449 return IWineD3DResourceImpl_SetPriority((IWineD3DResource *)iface, PriorityNew);
452 DWORD WINAPI IWineD3DSurfaceImpl_GetPriority(IWineD3DSurface *iface) {
453 return IWineD3DResourceImpl_GetPriority((IWineD3DResource *)iface);
456 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
457 /* TODO: check for locks */
458 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
459 IWineD3DBaseTexture *baseTexture = NULL;
460 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
462 TRACE("(%p)Checking to see if the container is a base texture\n", This);
463 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
464 TRACE("Passing to container\n");
465 IWineD3DBaseTexture_PreLoad(baseTexture);
466 IWineD3DBaseTexture_Release(baseTexture);
467 } else {
468 TRACE("(%p) : About to load surface\n", This);
470 ENTER_GL();
471 if(!device->isInDraw) {
472 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
475 glEnable(This->glDescription.target);/* make sure texture support is enabled in this context */
476 if (!This->glDescription.level) {
477 if (!This->glDescription.textureName) {
478 glGenTextures(1, &This->glDescription.textureName);
479 checkGLcall("glGenTextures");
480 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
482 glBindTexture(This->glDescription.target, This->glDescription.textureName);
483 checkGLcall("glBindTexture");
484 IWineD3DSurface_LoadTexture(iface, FALSE);
485 /* This is where we should be reducing the amount of GLMemoryUsed */
486 } else if (This->glDescription.textureName) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
487 /* assume this is a coding error not a real error for now */
488 FIXME("Mipmap surface has a glTexture bound to it!\n");
490 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
491 /* Tell opengl to try and keep this texture in video ram (well mostly) */
492 GLclampf tmp;
493 tmp = 0.9f;
494 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
496 LEAVE_GL();
498 return;
501 WINED3DRESOURCETYPE WINAPI IWineD3DSurfaceImpl_GetType(IWineD3DSurface *iface) {
502 TRACE("(%p) : calling resourceimpl_GetType\n", iface);
503 return IWineD3DResourceImpl_GetType((IWineD3DResource *)iface);
506 HRESULT WINAPI IWineD3DSurfaceImpl_GetParent(IWineD3DSurface *iface, IUnknown **pParent) {
507 TRACE("(%p) : calling resourceimpl_GetParent\n", iface);
508 return IWineD3DResourceImpl_GetParent((IWineD3DResource *)iface, pParent);
511 /* ******************************************************
512 IWineD3DSurface IWineD3DSurface parts follow
513 ****************************************************** */
515 HRESULT WINAPI IWineD3DSurfaceImpl_GetContainer(IWineD3DSurface* iface, REFIID riid, void** ppContainer) {
516 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
517 IWineD3DBase *container = 0;
519 TRACE("(This %p, riid %s, ppContainer %p)\n", This, debugstr_guid(riid), ppContainer);
521 if (!ppContainer) {
522 ERR("Called without a valid ppContainer.\n");
525 /** From MSDN:
526 * If the surface is created using CreateImageSurface/CreateOffscreenPlainSurface, CreateRenderTarget,
527 * or CreateDepthStencilSurface, the surface is considered stand alone. In this case,
528 * GetContainer will return the Direct3D device used to create the surface.
530 if (This->container) {
531 container = This->container;
532 } else {
533 container = (IWineD3DBase *)This->resource.wineD3DDevice;
536 TRACE("Relaying to QueryInterface\n");
537 return IUnknown_QueryInterface(container, riid, ppContainer);
540 HRESULT WINAPI IWineD3DSurfaceImpl_GetDesc(IWineD3DSurface *iface, WINED3DSURFACE_DESC *pDesc) {
541 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
543 TRACE("(%p) : copying into %p\n", This, pDesc);
544 if(pDesc->Format != NULL) *(pDesc->Format) = This->resource.format;
545 if(pDesc->Type != NULL) *(pDesc->Type) = This->resource.resourceType;
546 if(pDesc->Usage != NULL) *(pDesc->Usage) = This->resource.usage;
547 if(pDesc->Pool != NULL) *(pDesc->Pool) = This->resource.pool;
548 if(pDesc->Size != NULL) *(pDesc->Size) = This->resource.size; /* dx8 only */
549 if(pDesc->MultiSampleType != NULL) *(pDesc->MultiSampleType) = This->currentDesc.MultiSampleType;
550 if(pDesc->MultiSampleQuality != NULL) *(pDesc->MultiSampleQuality) = This->currentDesc.MultiSampleQuality;
551 if(pDesc->Width != NULL) *(pDesc->Width) = This->currentDesc.Width;
552 if(pDesc->Height != NULL) *(pDesc->Height) = This->currentDesc.Height;
553 return WINED3D_OK;
556 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
557 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
558 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
559 if (This->glDescription.textureName == 0 && textureName != 0) {
560 This->Flags &= ~SFLAG_INTEXTURE;
561 IWineD3DSurface_AddDirtyRect(iface, NULL);
563 This->glDescription.textureName = textureName;
564 This->glDescription.target = target;
565 This->Flags &= ~SFLAG_ALLOCATED;
568 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
569 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
570 TRACE("(%p) : returning %p\n", This, &This->glDescription);
571 *glDescription = &This->glDescription;
574 /* TODO: think about moving this down to resource? */
575 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
576 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
577 /* This should only be called for sysmem textures, it may be a good idea to extend this to all pools at some point in the future */
578 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
579 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
581 return (CONST void*)(This->resource.allocatedMemory);
584 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch, BOOL srcUpsideDown) {
585 BYTE *mem;
586 GLint fmt;
587 GLint type;
588 BYTE *row, *top, *bottom;
589 int i;
590 BOOL bpp;
592 switch(This->resource.format)
594 case WINED3DFMT_P8:
596 /* GL can't return palettized data, so read ARGB pixels into a
597 * separate block of memory and convert them into palettized format
598 * in software. Slow, but if the app means to use palettized render
599 * targets and locks it...
601 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
602 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
603 * for the color channels when palettizing the colors.
605 fmt = GL_RGB;
606 type = GL_UNSIGNED_BYTE;
607 pitch *= 3;
608 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
609 if(!mem) {
610 ERR("Out of memory\n");
611 return;
613 bpp = This->bytesPerPixel * 3;
615 break;
617 default:
618 mem = dest;
619 fmt = This->glDescription.glFormat;
620 type = This->glDescription.glType;
621 bpp = This->bytesPerPixel;
624 glReadPixels(rect->left, rect->top,
625 rect->right - rect->left,
626 rect->bottom - rect->top,
627 fmt, type, mem);
628 vcheckGLcall("glReadPixels");
630 /* TODO: Merge this with the palettization loop below for P8 targets */
632 if(!srcUpsideDown) {
633 UINT len, off;
634 /* glReadPixels returns the image upside down, and there is no way to prevent this.
635 Flip the lines in software */
636 len = (rect->right - rect->left) * bpp;
637 off = rect->left * bpp;
639 row = HeapAlloc(GetProcessHeap(), 0, len);
640 if(!row) {
641 ERR("Out of memory\n");
642 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
643 return;
646 top = mem + pitch * rect->top;
647 bottom = ((BYTE *) mem) + pitch * ( rect->bottom - rect->top - 1);
648 for(i = 0; i < (rect->bottom - rect->top) / 2; i++) {
649 memcpy(row, top + off, len);
650 memcpy(top + off, bottom + off, len);
651 memcpy(bottom + off, row, len);
652 top += pitch;
653 bottom -= pitch;
655 HeapFree(GetProcessHeap(), 0, row);
658 if(This->resource.format == WINED3DFMT_P8) {
659 PALETTEENTRY *pal;
660 DWORD width = pitch / 3;
661 int x, y, c;
662 if(This->palette) {
663 pal = This->palette->palents;
664 } else {
665 pal = This->resource.wineD3DDevice->palettes[This->resource.wineD3DDevice->currentPalette];
668 for(y = rect->top; y < rect->bottom; y++) {
669 for(x = rect->left; x < rect->right; x++) {
670 /* start lines pixels */
671 BYTE *blue = (BYTE *) ((BYTE *) mem) + y * pitch + x * (sizeof(BYTE) * 3);
672 BYTE *green = blue + 1;
673 BYTE *red = green + 1;
675 for(c = 0; c < 256; c++) {
676 if(*red == pal[c].peRed &&
677 *green == pal[c].peGreen &&
678 *blue == pal[c].peBlue)
680 *((BYTE *) dest + y * width + x) = c;
681 break;
686 HeapFree(GetProcessHeap(), 0, mem);
690 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
691 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
692 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
693 IWineD3DSwapChainImpl *swapchain = NULL;
695 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
697 if (!(This->Flags & SFLAG_LOCKABLE)) {
698 /* Note: UpdateTextures calls CopyRects which calls this routine to populate the
699 texture regions, and since the destination is an unlockable region we need
700 to tolerate this */
701 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
702 /*return WINED3DERR_INVALIDCALL; */
705 pLockedRect->Pitch = IWineD3DSurface_GetPitch(iface);
707 /* Mark the surface locked */
708 This->Flags |= SFLAG_LOCKED;
710 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy */
711 if(!This->resource.allocatedMemory) {
712 This->resource.allocatedMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + 4);
713 This->Flags &= ~SFLAG_INSYSMEM; /* This is the marker that surface data has to be downloaded */
716 /* Calculate the correct start address to report */
717 if (NULL == pRect) {
718 pLockedRect->pBits = This->resource.allocatedMemory;
719 This->lockedRect.left = 0;
720 This->lockedRect.top = 0;
721 This->lockedRect.right = This->currentDesc.Width;
722 This->lockedRect.bottom = This->currentDesc.Height;
723 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);
724 } else {
725 TRACE("Lock Rect (%p) = l %d, t %d, r %d, b %d\n", pRect, pRect->left, pRect->top, pRect->right, pRect->bottom);
727 /* DXTn textures are based on compressed blocks of 4x4 pixels, each
728 * 16 bytes large (8 bytes in case of DXT1). Because of that Pitch has
729 * slightly different meaning compared to regular textures. For DXTn
730 * textures Pitch is the size of a row of blocks, 4 high and "width"
731 * long. The x offset is calculated differently as well, since moving 4
732 * pixels to the right actually moves an entire 4x4 block to right, ie
733 * 16 bytes (8 in case of DXT1). */
734 if (This->resource.format == WINED3DFMT_DXT1) {
735 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top / 4) + (pRect->left * 2);
736 } else if (This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3
737 || This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
738 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top / 4) + (pRect->left * 4);
739 } else {
740 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top) + (pRect->left * This->bytesPerPixel);
742 This->lockedRect.left = pRect->left;
743 This->lockedRect.top = pRect->top;
744 This->lockedRect.right = pRect->right;
745 This->lockedRect.bottom = pRect->bottom;
748 if (This->Flags & SFLAG_NONPOW2) {
749 TRACE("Locking non-power 2 texture\n");
752 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
753 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
754 * changed
756 if(!(This->Flags & SFLAG_DYNLOCK)) {
757 This->lockCount++;
758 /* MAXLOCKCOUNT is defined in wined3d_private.h */
759 if(This->lockCount > MAXLOCKCOUNT) {
760 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
761 This->Flags |= SFLAG_DYNLOCK;
765 if((Flags & WINED3DLOCK_DISCARD) || (This->Flags & SFLAG_INSYSMEM)) {
766 TRACE("WINED3DLOCK_DISCARD flag passed, or local copy is up to date, not downloading data\n");
767 goto lock_end;
770 /* Now download the surface content from opengl
771 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
772 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
774 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
775 if(swapchain || iface == myDevice->render_targets[0]) {
776 BOOL srcIsUpsideDown;
778 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
779 static BOOL warned = FALSE;
780 if(!warned) {
781 ERR("The application tries to lock the render target, but render target locking is disabled\n");
782 warned = TRUE;
784 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
785 return WINED3D_OK;
788 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
789 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
790 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
791 * context->last_was_blit set on the unlock.
793 ENTER_GL();
794 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
796 /* Select the correct read buffer, and give some debug output.
797 * There is no need to keep track of the current read buffer or reset it, every part of the code
798 * that reads sets the read buffer as desired.
800 if(!swapchain) {
801 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
802 * Read from the back buffer
804 TRACE("Locking offscreen render target\n");
805 glReadBuffer(myDevice->offscreenBuffer);
806 srcIsUpsideDown = TRUE;
807 } else {
808 GLenum buffer = surface_get_gl_buffer(iface, (IWineD3DSwapChain *)swapchain);
809 TRACE("Locking %#x buffer\n", buffer);
810 glReadBuffer(buffer);
811 checkGLcall("glReadBuffer");
813 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
814 srcIsUpsideDown = FALSE;
817 switch(wined3d_settings.rendertargetlock_mode) {
818 case RTL_AUTO:
819 case RTL_READDRAW:
820 case RTL_READTEX:
821 read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
822 break;
824 case RTL_TEXDRAW:
825 case RTL_TEXTEX:
826 read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
827 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
828 break;
830 LEAVE_GL();
832 /* Mark the local copy up to date if a full download was done */
833 if(This->lockedRect.left == 0 &&
834 This->lockedRect.top == 0 &&
835 This->lockedRect.right == This->currentDesc.Width &&
836 This->lockedRect.bottom == This->currentDesc.Height) {
837 This->Flags |= SFLAG_INSYSMEM;
839 } else if(iface == myDevice->stencilBufferTarget) {
840 /** the depth stencil in openGL has a format of GL_FLOAT
841 * which should be good for WINED3DFMT_D16_LOCKABLE
842 * and WINED3DFMT_D16
843 * it is unclear what format the stencil buffer is in except.
844 * 'Each index is converted to fixed point...
845 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
846 * mappings in the table GL_PIXEL_MAP_S_TO_S.
847 * glReadPixels(This->lockedRect.left,
848 * This->lockedRect.bottom - j - 1,
849 * This->lockedRect.right - This->lockedRect.left,
850 * 1,
851 * GL_DEPTH_COMPONENT,
852 * type,
853 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
855 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
856 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
857 * none of that is the case the problem is not in this function :-)
858 ********************************************/
859 FIXME("Depth stencil locking not supported yet\n");
860 } else {
861 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
862 TRACE("locking an ordinary surface\n");
864 if (0 != This->glDescription.textureName) {
865 /* Now I have to copy thing bits back */
867 ENTER_GL();
869 if(myDevice->createParms.BehaviorFlags & WINED3DCREATE_MULTITHREADED) {
870 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
873 /* Make sure that a proper texture unit is selected, bind the texture and dirtify the sampler to restore the texture on the next draw */
874 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
875 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
876 checkGLcall("glActiveTextureARB");
878 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(0));
879 IWineD3DSurface_PreLoad(iface);
881 surface_download_data(This);
882 LEAVE_GL();
886 lock_end:
887 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
888 /* Don't dirtify */
889 } else {
890 IWineD3DBaseTexture *pBaseTexture;
892 * Dirtify on lock
893 * as seen in msdn docs
895 IWineD3DSurface_AddDirtyRect(iface, &This->lockedRect);
897 /** Dirtify Container if needed */
898 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
899 TRACE("Making container dirty\n");
900 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
901 IWineD3DBaseTexture_Release(pBaseTexture);
902 } else {
903 TRACE("Surface is standalone, no need to dirty the container\n");
907 TRACE("returning memory@%p, pitch(%d) dirtyfied(%d)\n", pLockedRect->pBits, pLockedRect->Pitch,
908 This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
909 return WINED3D_OK;
912 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This) {
913 GLint prev_store;
914 GLint prev_rasterpos[4];
915 GLint skipBytes = 0;
916 BOOL storechanged = FALSE, memory_allocated = FALSE;
917 GLint fmt, type;
918 BYTE *mem;
919 UINT bpp;
920 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
922 glDisable(GL_TEXTURE_2D);
923 vcheckGLcall("glDisable(GL_TEXTURE_2D)");
925 glFlush();
926 vcheckGLcall("glFlush");
927 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
928 vcheckGLcall("glIntegerv");
929 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
930 vcheckGLcall("glIntegerv");
931 glPixelZoom(1.0, -1.0);
932 vcheckGLcall("glPixelZoom");
934 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
935 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
936 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
938 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
939 vcheckGLcall("glRasterPos2f");
941 /* Some drivers(radeon dri, others?) don't like exceptions during
942 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
943 * after ReleaseDC. Reading it will cause an exception, which x11drv will
944 * catch to put the dib section in InSync mode, which leads to a crash
945 * and a blocked x server on my radeon card.
947 * The following lines read the dib section so it is put in inSync mode
948 * before glDrawPixels is called and the crash is prevented. There won't
949 * be any interfering gdi accesses, because UnlockRect is called from
950 * ReleaseDC, and the app won't use the dc any more afterwards.
952 if(This->Flags & SFLAG_DIBSECTION) {
953 volatile BYTE read;
954 read = This->resource.allocatedMemory[0];
957 switch (This->resource.format) {
958 /* No special care needed */
959 case WINED3DFMT_A4R4G4B4:
960 case WINED3DFMT_R5G6B5:
961 case WINED3DFMT_A1R5G5B5:
962 case WINED3DFMT_R8G8B8:
963 type = This->glDescription.glType;
964 fmt = This->glDescription.glFormat;
965 mem = This->resource.allocatedMemory;
966 bpp = This->bytesPerPixel;
967 break;
969 case WINED3DFMT_X4R4G4B4:
971 int size;
972 unsigned short *data;
973 data = (unsigned short *)This->resource.allocatedMemory;
974 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
975 while(size > 0) {
976 *data |= 0xF000;
977 data++;
978 size--;
980 type = This->glDescription.glType;
981 fmt = This->glDescription.glFormat;
982 mem = This->resource.allocatedMemory;
983 bpp = This->bytesPerPixel;
985 break;
987 case WINED3DFMT_X1R5G5B5:
989 int size;
990 unsigned short *data;
991 data = (unsigned short *)This->resource.allocatedMemory;
992 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
993 while(size > 0) {
994 *data |= 0x8000;
995 data++;
996 size--;
998 type = This->glDescription.glType;
999 fmt = This->glDescription.glFormat;
1000 mem = This->resource.allocatedMemory;
1001 bpp = This->bytesPerPixel;
1003 break;
1005 case WINED3DFMT_X8R8G8B8:
1007 /* make sure the X byte is set to alpha on, since it
1008 could be any random value. This fixes the intro movie in Pirates! */
1009 int size;
1010 unsigned int *data;
1011 data = (unsigned int *)This->resource.allocatedMemory;
1012 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
1013 while(size > 0) {
1014 *data |= 0xFF000000;
1015 data++;
1016 size--;
1019 /* Fall through */
1021 case WINED3DFMT_A8R8G8B8:
1023 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
1024 vcheckGLcall("glPixelStorei");
1025 storechanged = TRUE;
1026 type = This->glDescription.glType;
1027 fmt = This->glDescription.glFormat;
1028 mem = This->resource.allocatedMemory;
1029 bpp = This->bytesPerPixel;
1031 break;
1033 case WINED3DFMT_A2R10G10B10:
1035 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
1036 vcheckGLcall("glPixelStorei");
1037 storechanged = TRUE;
1038 type = This->glDescription.glType;
1039 fmt = This->glDescription.glFormat;
1040 mem = This->resource.allocatedMemory;
1041 bpp = This->bytesPerPixel;
1043 break;
1045 case WINED3DFMT_P8:
1047 int height = This->glRect.bottom - This->glRect.top;
1048 type = GL_UNSIGNED_BYTE;
1049 fmt = GL_RGBA;
1051 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * sizeof(DWORD));
1052 if(!mem) {
1053 ERR("Out of memory\n");
1054 return;
1056 memory_allocated = TRUE;
1057 d3dfmt_convert_surface(This->resource.allocatedMemory,
1058 mem,
1059 pitch,
1060 pitch,
1061 height,
1062 pitch * 4,
1063 CONVERT_PALETTED,
1064 This);
1065 bpp = This->bytesPerPixel * 4;
1066 pitch *= 4;
1068 break;
1070 default:
1071 FIXME("Unsupported Format %u in locking func\n", This->resource.format);
1073 /* Give it a try */
1074 type = This->glDescription.glType;
1075 fmt = This->glDescription.glFormat;
1076 mem = This->resource.allocatedMemory;
1077 bpp = This->bytesPerPixel;
1080 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1081 (This->lockedRect.bottom - This->lockedRect.top)-1,
1082 fmt, type,
1083 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1084 checkGLcall("glDrawPixels");
1085 glPixelZoom(1.0,1.0);
1086 vcheckGLcall("glPixelZoom");
1088 glRasterPos3iv(&prev_rasterpos[0]);
1089 vcheckGLcall("glRasterPos3iv");
1091 /* Reset to previous pack row length */
1092 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1093 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1094 if(storechanged) {
1095 glPixelStorei(GL_PACK_SWAP_BYTES, prev_store);
1096 vcheckGLcall("glPixelStorei GL_PACK_SWAP_BYTES");
1099 /* Blitting environment requires that 2D texturing is enabled. It was turned off before,
1100 * turn it on again
1102 glEnable(GL_TEXTURE_2D);
1103 checkGLcall("glEnable(GL_TEXTURE_2D)");
1105 if(memory_allocated) HeapFree(GetProcessHeap(), 0, mem);
1106 return;
1109 static void flush_to_framebuffer_texture(IWineD3DSurfaceImpl *This) {
1110 float glTexCoord[4];
1112 glTexCoord[0] = (float) This->lockedRect.left / (float) This->pow2Width; /* left */
1113 glTexCoord[1] = (float) This->lockedRect.right / (float) This->pow2Width; /* right */
1114 glTexCoord[2] = (float) This->lockedRect.top / (float) This->pow2Height; /* top */
1115 glTexCoord[3] = (float) This->lockedRect.bottom / (float) This->pow2Height; /* bottom */
1117 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
1119 ENTER_GL();
1121 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
1122 checkGLcall("glEnable glBindTexture");
1124 /* No filtering for blts */
1125 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1126 checkGLcall("glTexParameteri");
1127 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1128 checkGLcall("glTexParameteri");
1130 /* Start drawing a quad */
1131 glBegin(GL_QUADS);
1133 glColor3d(1.0f, 1.0f, 1.0f);
1134 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
1135 glVertex3f(This->lockedRect.left, This->lockedRect.top, 0.0);
1137 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
1138 glVertex3f(This->lockedRect.left, This->lockedRect.bottom, 0.0);
1140 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
1141 glVertex3d(This->lockedRect.right, This->lockedRect.bottom, 0.0);
1143 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
1144 glVertex3f(This->lockedRect.right, This->lockedRect.top, 0.0);
1146 glEnd();
1147 checkGLcall("glEnd");
1149 /* Unbind the texture */
1150 glBindTexture(GL_TEXTURE_2D, 0);
1151 checkGLcall("glEnable glBindTexture");
1153 LEAVE_GL();
1156 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1157 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1158 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1159 IWineD3DSwapChainImpl *swapchain = NULL;
1161 if (!(This->Flags & SFLAG_LOCKED)) {
1162 WARN("trying to Unlock an unlocked surf@%p\n", This);
1163 return WINED3DERR_INVALIDCALL;
1166 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1168 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1169 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1170 goto unlock_end;
1173 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1174 if(swapchain || iface == myDevice->render_targets[0]) {
1175 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1176 static BOOL warned = FALSE;
1177 if(!warned) {
1178 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1179 warned = TRUE;
1181 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1182 goto unlock_end;
1185 /* Activate the correct context for the render target */
1186 ENTER_GL();
1187 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1189 if(!swapchain) {
1190 /* Primary offscreen render target */
1191 TRACE("Offscreen render target\n");
1192 glDrawBuffer(myDevice->offscreenBuffer);
1193 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1194 } else {
1195 GLenum buffer = surface_get_gl_buffer(iface, (IWineD3DSwapChain *)swapchain);
1196 TRACE("Unlocking %#x buffer\n", buffer);
1197 glDrawBuffer(buffer);
1198 checkGLcall("glDrawBuffer");
1200 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1203 switch(wined3d_settings.rendertargetlock_mode) {
1204 case RTL_AUTO:
1205 case RTL_READDRAW:
1206 case RTL_TEXDRAW:
1207 flush_to_framebuffer_drawpixels(This);
1208 break;
1210 case RTL_READTEX:
1211 case RTL_TEXTEX:
1212 flush_to_framebuffer_texture(This);
1213 break;
1215 if(!swapchain) {
1216 glDrawBuffer(myDevice->offscreenBuffer);
1217 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1218 } else if(swapchain->backBuffer) {
1219 glDrawBuffer(GL_BACK);
1220 checkGLcall("glDrawBuffer(GL_BACK)");
1221 } else {
1222 glDrawBuffer(GL_FRONT);
1223 checkGLcall("glDrawBuffer(GL_FRONT)");
1225 LEAVE_GL();
1227 This->dirtyRect.left = This->currentDesc.Width;
1228 This->dirtyRect.top = This->currentDesc.Height;
1229 This->dirtyRect.right = 0;
1230 This->dirtyRect.bottom = 0;
1231 This->Flags |= SFLAG_INDRAWABLE;
1232 } else if(iface == myDevice->stencilBufferTarget) {
1233 FIXME("Depth Stencil buffer locking is not implemented\n");
1234 } else {
1235 /* The rest should be a normal texture */
1236 IWineD3DBaseTextureImpl *impl;
1237 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1238 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1239 * states need resetting
1241 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1242 if(impl->baseTexture.bindCount) {
1243 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1245 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1249 unlock_end:
1250 This->Flags &= ~SFLAG_LOCKED;
1251 memset(&This->lockedRect, 0, sizeof(RECT));
1252 return WINED3D_OK;
1255 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1256 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1257 WINED3DLOCKED_RECT lock;
1258 UINT usage;
1259 BITMAPINFO* b_info;
1260 HDC ddc;
1261 DWORD *masks;
1262 HRESULT hr;
1263 RGBQUAD col[256];
1264 const PixelFormatDesc *formatEntry = getFormatDescEntry(This->resource.format);
1266 TRACE("(%p)->(%p)\n",This,pHDC);
1268 if(This->Flags & SFLAG_USERPTR) {
1269 ERR("Not supported on surfaces with an application-provided surfaces\n");
1270 return WINEDDERR_NODC;
1273 /* Give more detailed info for ddraw */
1274 if (This->Flags & SFLAG_DCINUSE)
1275 return WINEDDERR_DCALREADYCREATED;
1277 /* Can't GetDC if the surface is locked */
1278 if (This->Flags & SFLAG_LOCKED)
1279 return WINED3DERR_INVALIDCALL;
1281 memset(&lock, 0, sizeof(lock)); /* To be sure */
1283 /* Create a DIB section if there isn't a hdc yet */
1284 if(!This->hDC) {
1285 int extraline = 0;
1286 SYSTEM_INFO sysInfo;
1287 void *oldmem = This->resource.allocatedMemory;
1289 switch (This->bytesPerPixel) {
1290 case 2:
1291 case 4:
1292 /* Allocate extra space to store the RGB bit masks. */
1293 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
1294 break;
1296 case 3:
1297 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
1298 break;
1300 default:
1301 /* Allocate extra space for a palette. */
1302 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1303 sizeof(BITMAPINFOHEADER)
1304 + sizeof(RGBQUAD)
1305 * (1 << (This->bytesPerPixel * 8)));
1306 break;
1309 if (!b_info)
1310 return E_OUTOFMEMORY;
1312 /* Some apps access the surface in via DWORDs, and do not take the necessary care at the end of the
1313 * surface. So we need at least extra 4 bytes at the end of the surface. Check against the page size,
1314 * if the last page used for the surface has at least 4 spare bytes we're safe, otherwise
1315 * add an extra line to the dib section
1317 GetSystemInfo(&sysInfo);
1318 if( ((This->resource.size + 3) % sysInfo.dwPageSize) < 4) {
1319 extraline = 1;
1320 TRACE("Adding an extra line to the dib section\n");
1323 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1324 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
1325 b_info->bmiHeader.biWidth = IWineD3DSurface_GetPitch(iface) / This->bytesPerPixel;
1326 b_info->bmiHeader.biHeight = -This->currentDesc.Height -extraline;
1327 b_info->bmiHeader.biSizeImage = ( This->currentDesc.Height + extraline) * IWineD3DSurface_GetPitch(iface);
1328 b_info->bmiHeader.biPlanes = 1;
1329 b_info->bmiHeader.biBitCount = This->bytesPerPixel * 8;
1331 b_info->bmiHeader.biXPelsPerMeter = 0;
1332 b_info->bmiHeader.biYPelsPerMeter = 0;
1333 b_info->bmiHeader.biClrUsed = 0;
1334 b_info->bmiHeader.biClrImportant = 0;
1336 /* Get the bit masks */
1337 masks = (DWORD *) &(b_info->bmiColors);
1338 switch (This->resource.format) {
1339 case WINED3DFMT_R8G8B8:
1340 usage = DIB_RGB_COLORS;
1341 b_info->bmiHeader.biCompression = BI_RGB;
1342 break;
1344 case WINED3DFMT_X1R5G5B5:
1345 case WINED3DFMT_A1R5G5B5:
1346 case WINED3DFMT_A4R4G4B4:
1347 case WINED3DFMT_X4R4G4B4:
1348 case WINED3DFMT_R3G3B2:
1349 case WINED3DFMT_A8R3G3B2:
1350 case WINED3DFMT_A2B10G10R10:
1351 case WINED3DFMT_A8B8G8R8:
1352 case WINED3DFMT_X8B8G8R8:
1353 case WINED3DFMT_A2R10G10B10:
1354 case WINED3DFMT_R5G6B5:
1355 case WINED3DFMT_A16B16G16R16:
1356 usage = 0;
1357 b_info->bmiHeader.biCompression = BI_BITFIELDS;
1358 masks[0] = formatEntry->redMask;
1359 masks[1] = formatEntry->greenMask;
1360 masks[2] = formatEntry->blueMask;
1361 break;
1363 default:
1364 /* Don't know palette */
1365 b_info->bmiHeader.biCompression = BI_RGB;
1366 usage = 0;
1367 break;
1370 ddc = GetDC(0);
1371 if (ddc == 0) {
1372 HeapFree(GetProcessHeap(), 0, b_info);
1373 return HRESULT_FROM_WIN32(GetLastError());
1376 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);
1377 This->dib.DIBsection = CreateDIBSection(ddc, b_info, usage, &This->dib.bitmap_data, 0 /* Handle */, 0 /* Offset */);
1378 ReleaseDC(0, ddc);
1380 if (!This->dib.DIBsection) {
1381 ERR("CreateDIBSection failed!\n");
1382 HeapFree(GetProcessHeap(), 0, b_info);
1383 return HRESULT_FROM_WIN32(GetLastError());
1386 TRACE("DIBSection at : %p\n", This->dib.bitmap_data);
1388 /* copy the existing surface to the dib section */
1389 if(This->resource.allocatedMemory) {
1390 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, b_info->bmiHeader.biSizeImage);
1391 /* We won't need that any more */
1392 } else {
1393 /* This is to make LockRect read the gl Texture although memory is allocated */
1394 This->Flags &= ~SFLAG_INSYSMEM;
1397 HeapFree(GetProcessHeap(), 0, b_info);
1399 /* Use the dib section from now on */
1400 This->resource.allocatedMemory = This->dib.bitmap_data;
1402 /* Now allocate a HDC */
1403 This->hDC = CreateCompatibleDC(0);
1404 This->dib.holdbitmap = SelectObject(This->hDC, This->dib.DIBsection);
1405 TRACE("using wined3d palette %p\n", This->palette);
1406 SelectPalette(This->hDC,
1407 This->palette ? This->palette->hpal : 0,
1408 FALSE);
1410 This->Flags |= SFLAG_DIBSECTION;
1412 if(This->Flags & SFLAG_CLIENT) {
1413 IWineD3DSurface_PreLoad(iface);
1415 HeapFree(GetProcessHeap(), 0, oldmem);
1418 /* Lock the surface */
1419 hr = IWineD3DSurface_LockRect(iface,
1420 &lock,
1421 NULL,
1423 if(FAILED(hr)) {
1424 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1425 /* keep the dib section */
1426 return hr;
1429 if(This->resource.format == WINED3DFMT_P8 ||
1430 This->resource.format == WINED3DFMT_A8P8) {
1431 unsigned int n;
1432 if(This->palette) {
1433 PALETTEENTRY ent[256];
1435 GetPaletteEntries(This->palette->hpal, 0, 256, ent);
1436 for (n=0; n<256; n++) {
1437 col[n].rgbRed = ent[n].peRed;
1438 col[n].rgbGreen = ent[n].peGreen;
1439 col[n].rgbBlue = ent[n].peBlue;
1440 col[n].rgbReserved = 0;
1442 } else {
1443 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1445 for (n=0; n<256; n++) {
1446 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
1447 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
1448 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
1449 col[n].rgbReserved = 0;
1453 SetDIBColorTable(This->hDC, 0, 256, col);
1456 *pHDC = This->hDC;
1457 TRACE("returning %p\n",*pHDC);
1458 This->Flags |= SFLAG_DCINUSE;
1460 return WINED3D_OK;
1463 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1464 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1466 TRACE("(%p)->(%p)\n",This,hDC);
1468 if (!(This->Flags & SFLAG_DCINUSE))
1469 return WINED3DERR_INVALIDCALL;
1471 /* we locked first, so unlock now */
1472 IWineD3DSurface_UnlockRect(iface);
1474 This->Flags &= ~SFLAG_DCINUSE;
1476 return WINED3D_OK;
1479 /* ******************************************************
1480 IWineD3DSurface Internal (No mapping to directx api) parts follow
1481 ****************************************************** */
1483 static HRESULT d3dfmt_get_conv(IWineD3DSurfaceImpl *This, BOOL need_alpha_ck, BOOL use_texturing, GLenum *format, GLenum *internal, GLenum *type, CONVERT_TYPES *convert, int *target_bpp, BOOL srgb_mode) {
1484 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1485 const PixelFormatDesc *formatEntry = getFormatDescEntry(This->resource.format);
1487 /* Default values: From the surface */
1488 *format = formatEntry->glFormat;
1489 *internal = srgb_mode?formatEntry->glGammaInternal:formatEntry->glInternal;
1490 *type = formatEntry->glType;
1491 *convert = NO_CONVERSION;
1492 *target_bpp = This->bytesPerPixel;
1494 /* Ok, now look if we have to do any conversion */
1495 switch(This->resource.format) {
1496 case WINED3DFMT_P8:
1497 /* ****************
1498 Paletted Texture
1499 **************** */
1500 /* Use conversion when the paletted texture extension is not available, or when it is available make sure it is used
1501 * for texturing as it won't work for calls like glDraw-/glReadPixels and further also use conversion in case of color keying.
1503 if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) || colorkey_active || (!use_texturing && GL_SUPPORT(EXT_PALETTED_TEXTURE)) ) {
1504 *format = GL_RGBA;
1505 *internal = GL_RGBA;
1506 *type = GL_UNSIGNED_BYTE;
1507 *target_bpp = 4;
1508 if(colorkey_active) {
1509 *convert = CONVERT_PALETTED_CK;
1510 } else {
1511 *convert = CONVERT_PALETTED;
1515 break;
1517 case WINED3DFMT_R3G3B2:
1518 /* **********************
1519 GL_UNSIGNED_BYTE_3_3_2
1520 ********************** */
1521 if (colorkey_active) {
1522 /* This texture format will never be used.. So do not care about color keying
1523 up until the point in time it will be needed :-) */
1524 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1526 break;
1528 case WINED3DFMT_R5G6B5:
1529 if (colorkey_active) {
1530 *convert = CONVERT_CK_565;
1531 *format = GL_RGBA;
1532 *internal = GL_RGBA;
1533 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1535 break;
1537 case WINED3DFMT_X1R5G5B5:
1538 if (colorkey_active) {
1539 *convert = CONVERT_CK_5551;
1540 *format = GL_BGRA;
1541 *internal = GL_RGBA;
1542 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1544 break;
1546 case WINED3DFMT_R8G8B8:
1547 if (colorkey_active) {
1548 *convert = CONVERT_CK_RGB24;
1549 *format = GL_RGBA;
1550 *internal = GL_RGBA;
1551 *type = GL_UNSIGNED_INT_8_8_8_8;
1552 *target_bpp = 4;
1554 break;
1556 case WINED3DFMT_X8R8G8B8:
1557 if (colorkey_active) {
1558 *convert = CONVERT_RGB32_888;
1559 *format = GL_RGBA;
1560 *internal = GL_RGBA;
1561 *type = GL_UNSIGNED_INT_8_8_8_8;
1563 break;
1565 case WINED3DFMT_V8U8:
1566 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1567 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1568 *format = GL_DUDV_ATI;
1569 *internal = GL_DU8DV8_ATI;
1570 *type = GL_BYTE;
1571 /* No conversion - Just change the gl type */
1572 break;
1574 *convert = CONVERT_V8U8;
1575 *format = GL_BGR;
1576 *internal = GL_RGB8;
1577 *type = GL_UNSIGNED_BYTE;
1578 *target_bpp = 3;
1579 break;
1581 case WINED3DFMT_X8L8V8U8:
1582 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1583 *convert = CONVERT_X8L8V8U8;
1584 *format = GL_BGRA;
1585 *internal = GL_RGBA8;
1586 *type = GL_UNSIGNED_BYTE;
1587 *target_bpp = 4;
1588 /* Not supported by GL_ATI_envmap_bumpmap */
1589 break;
1591 case WINED3DFMT_Q8W8V8U8:
1592 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1593 *convert = CONVERT_Q8W8V8U8;
1594 *format = GL_BGRA;
1595 *internal = GL_RGBA8;
1596 *type = GL_UNSIGNED_BYTE;
1597 *target_bpp = 4;
1598 /* Not supported by GL_ATI_envmap_bumpmap */
1599 break;
1601 case WINED3DFMT_V16U16:
1602 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1603 *convert = CONVERT_V16U16;
1604 *format = GL_BGR;
1605 *internal = GL_RGB16;
1606 *type = GL_SHORT;
1607 *target_bpp = 6;
1608 /* What should I do here about GL_ATI_envmap_bumpmap?
1609 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1611 break;
1613 default:
1614 break;
1617 return WINED3D_OK;
1620 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf) {
1621 BYTE *source, *dest;
1622 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert, surf);
1624 switch (convert) {
1625 case NO_CONVERSION:
1627 memcpy(dst, src, pitch * height);
1628 break;
1630 case CONVERT_PALETTED:
1631 case CONVERT_PALETTED_CK:
1633 IWineD3DPaletteImpl* pal = surf->palette;
1634 BYTE table[256][4];
1635 unsigned int i;
1636 unsigned int x, y;
1638 if( pal == NULL) {
1639 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1642 if (pal == NULL) {
1643 /* Still no palette? Use the device's palette */
1644 /* Get the surface's palette */
1645 for (i = 0; i < 256; i++) {
1646 IWineD3DDeviceImpl *device = surf->resource.wineD3DDevice;
1648 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1649 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1650 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1651 if ((convert == CONVERT_PALETTED_CK) &&
1652 (i >= surf->SrcBltCKey.dwColorSpaceLowValue) &&
1653 (i <= surf->SrcBltCKey.dwColorSpaceHighValue)) {
1654 /* We should maybe here put a more 'neutral' color than the standard bright purple
1655 one often used by application to prevent the nice purple borders when bi-linear
1656 filtering is on */
1657 table[i][3] = 0x00;
1658 } else {
1659 table[i][3] = 0xFF;
1662 } else {
1663 TRACE("Using surface palette %p\n", pal);
1664 /* Get the surface's palette */
1665 for (i = 0; i < 256; i++) {
1666 table[i][0] = pal->palents[i].peRed;
1667 table[i][1] = pal->palents[i].peGreen;
1668 table[i][2] = pal->palents[i].peBlue;
1669 if ((convert == CONVERT_PALETTED_CK) &&
1670 (i >= surf->SrcBltCKey.dwColorSpaceLowValue) &&
1671 (i <= surf->SrcBltCKey.dwColorSpaceHighValue)) {
1672 /* We should maybe here put a more 'neutral' color than the standard bright purple
1673 one often used by application to prevent the nice purple borders when bi-linear
1674 filtering is on */
1675 table[i][3] = 0x00;
1676 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
1677 table[i][3] = pal->palents[i].peFlags;
1678 } else {
1679 table[i][3] = 0xFF;
1684 for (y = 0; y < height; y++)
1686 source = src + pitch * y;
1687 dest = dst + outpitch * y;
1688 /* This is an 1 bpp format, using the width here is fine */
1689 for (x = 0; x < width; x++) {
1690 BYTE color = *source++;
1691 *dest++ = table[color][0];
1692 *dest++ = table[color][1];
1693 *dest++ = table[color][2];
1694 *dest++ = table[color][3];
1698 break;
1700 case CONVERT_CK_565:
1702 /* Converting the 565 format in 5551 packed to emulate color-keying.
1704 Note : in all these conversion, it would be best to average the averaging
1705 pixels to get the color of the pixel that will be color-keyed to
1706 prevent 'color bleeding'. This will be done later on if ever it is
1707 too visible.
1709 Note2: Nvidia documents say that their driver does not support alpha + color keying
1710 on the same surface and disables color keying in such a case
1712 unsigned int x, y;
1713 WORD *Source;
1714 WORD *Dest;
1716 TRACE("Color keyed 565\n");
1718 for (y = 0; y < height; y++) {
1719 Source = (WORD *) (src + y * pitch);
1720 Dest = (WORD *) (dst + y * outpitch);
1721 for (x = 0; x < width; x++ ) {
1722 WORD color = *Source++;
1723 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1724 if ((color < surf->SrcBltCKey.dwColorSpaceLowValue) ||
1725 (color > surf->SrcBltCKey.dwColorSpaceHighValue)) {
1726 *Dest |= 0x0001;
1728 Dest++;
1732 break;
1734 case CONVERT_CK_5551:
1736 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1737 unsigned int x, y;
1738 WORD *Source;
1739 WORD *Dest;
1740 TRACE("Color keyed 5551\n");
1741 for (y = 0; y < height; y++) {
1742 Source = (WORD *) (src + y * pitch);
1743 Dest = (WORD *) (dst + y * outpitch);
1744 for (x = 0; x < width; x++ ) {
1745 WORD color = *Source++;
1746 *Dest = color;
1747 if ((color < surf->SrcBltCKey.dwColorSpaceLowValue) ||
1748 (color > surf->SrcBltCKey.dwColorSpaceHighValue)) {
1749 *Dest |= (1 << 15);
1751 else {
1752 *Dest &= ~(1 << 15);
1754 Dest++;
1758 break;
1760 case CONVERT_V8U8:
1762 unsigned int x, y;
1763 short *Source;
1764 unsigned char *Dest;
1765 for(y = 0; y < height; y++) {
1766 Source = (short *) (src + y * pitch);
1767 Dest = (unsigned char *) (dst + y * outpitch);
1768 for (x = 0; x < width; x++ ) {
1769 long color = (*Source++);
1770 /* B */ Dest[0] = 0xff;
1771 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1772 /* R */ Dest[2] = (color) + 128; /* U */
1773 Dest += 3;
1776 break;
1779 case CONVERT_Q8W8V8U8:
1781 unsigned int x, y;
1782 DWORD *Source;
1783 unsigned char *Dest;
1784 for(y = 0; y < height; y++) {
1785 Source = (DWORD *) (src + y * pitch);
1786 Dest = (unsigned char *) (dst + y * outpitch);
1787 for (x = 0; x < width; x++ ) {
1788 long color = (*Source++);
1789 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1790 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1791 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1792 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1793 Dest += 4;
1796 break;
1799 default:
1800 ERR("Unsupported conversation type %d\n", convert);
1802 return WINED3D_OK;
1805 /* This function is used in case of 8bit paletted textures to upload the palette.
1806 For now it only supports GL_EXT_paletted_texture extension but support for other
1807 extensions like ARB_fragment_program and ATI_fragment_shaders will be added as well.
1809 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
1810 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1811 IWineD3DPaletteImpl* pal = This->palette;
1812 BYTE table[256][4];
1813 int i;
1815 if (pal == NULL) {
1816 /* Still no palette? Use the device's palette */
1817 /* Get the surface's palette */
1818 for (i = 0; i < 256; i++) {
1819 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1821 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1822 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1823 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1824 if ((convert == CONVERT_PALETTED_CK) &&
1825 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1826 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1827 /* We should maybe here put a more 'neutral' color than the standard bright purple
1828 one often used by application to prevent the nice purple borders when bi-linear
1829 filtering is on */
1830 table[i][3] = 0x00;
1831 } else {
1832 table[i][3] = 0xFF;
1835 } else {
1836 TRACE("Using surface palette %p\n", pal);
1837 /* Get the surface's palette */
1838 for (i = 0; i < 256; i++) {
1839 table[i][0] = pal->palents[i].peRed;
1840 table[i][1] = pal->palents[i].peGreen;
1841 table[i][2] = pal->palents[i].peBlue;
1842 if ((convert == CONVERT_PALETTED_CK) &&
1843 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1844 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1845 /* We should maybe here put a more 'neutral' color than the standard bright purple
1846 one often used by application to prevent the nice purple borders when bi-linear
1847 filtering is on */
1848 table[i][3] = 0x00;
1849 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
1850 table[i][3] = pal->palents[i].peFlags;
1851 } else {
1852 table[i][3] = 0xFF;
1856 GL_EXTCALL(glColorTableEXT(GL_TEXTURE_2D,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
1859 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
1860 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1862 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
1863 /* If a ddraw-style palette is attached assume no d3d9 palette change.
1864 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
1866 return FALSE;
1869 if(This->palette9) {
1870 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
1871 return FALSE;
1873 } else {
1874 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
1876 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
1877 return TRUE;
1880 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
1881 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1882 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1883 GLenum format, internal, type;
1884 CONVERT_TYPES convert;
1885 int bpp;
1886 int width, pitch, outpitch;
1887 BYTE *mem;
1889 if (!(This->Flags & SFLAG_INTEXTURE)) {
1890 TRACE("Reloading because surface is dirty\n");
1891 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
1892 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
1893 /* Reload: vice versa OR */
1894 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
1895 /* Also reload: Color key is active AND the color key has changed */
1896 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
1897 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
1898 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
1899 TRACE("Reloading because of color keying\n");
1900 } else if(palette9_changed(This)) {
1901 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
1902 } else {
1903 TRACE("surface is already in texture\n");
1904 return WINED3D_OK;
1907 This->Flags |= SFLAG_INTEXTURE;
1909 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
1910 * These resources are not bound by device size or format restrictions. Because of this,
1911 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
1912 * However, these resources can always be created, locked, and copied.
1914 if (This->resource.pool == WINED3DPOOL_SCRATCH )
1916 FIXME("(%p) Operation not supported for scratch textures\n",This);
1917 return WINED3DERR_INVALIDCALL;
1920 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb_mode);
1922 if (This->Flags & SFLAG_INDRAWABLE) {
1923 if (This->glDescription.level != 0)
1924 FIXME("Surface in texture is only supported for level 0\n");
1925 else if (This->resource.format == WINED3DFMT_P8 || This->resource.format == WINED3DFMT_A8P8 ||
1926 This->resource.format == WINED3DFMT_DXT1 || This->resource.format == WINED3DFMT_DXT2 ||
1927 This->resource.format == WINED3DFMT_DXT3 || This->resource.format == WINED3DFMT_DXT4 ||
1928 This->resource.format == WINED3DFMT_DXT5)
1929 FIXME("Format %d not supported\n", This->resource.format);
1930 else {
1931 GLint prevRead;
1933 ENTER_GL();
1934 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1935 vcheckGLcall("glGetIntegerv");
1936 glReadBuffer(This->resource.wineD3DDevice->offscreenBuffer);
1937 vcheckGLcall("glReadBuffer");
1939 if(!(This->Flags & SFLAG_ALLOCATED)) {
1940 surface_allocate_surface(This, internal, This->pow2Width,
1941 This->pow2Height, format, type);
1944 glCopyTexSubImage2D(This->glDescription.target,
1945 This->glDescription.level,
1946 0, 0, 0, 0,
1947 This->currentDesc.Width,
1948 This->currentDesc.Height);
1949 checkGLcall("glCopyTexSubImage2D");
1951 glReadBuffer(prevRead);
1952 vcheckGLcall("glReadBuffer");
1954 LEAVE_GL();
1956 TRACE("Updated target %d\n", This->glDescription.target);
1958 return WINED3D_OK;
1959 } else
1960 /* The only place where LoadTexture() might get called when isInDraw=1
1961 * is ActivateContext where lastActiveRenderTarget is preloaded.
1963 if(iface == device->lastActiveRenderTarget && device->isInDraw)
1964 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
1966 /* Otherwise: System memory copy must be most up to date */
1968 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
1969 This->Flags |= SFLAG_GLCKEY;
1970 This->glCKey = This->SrcBltCKey;
1972 else This->Flags &= ~SFLAG_GLCKEY;
1974 /* The width is in 'length' not in bytes */
1975 width = This->currentDesc.Width;
1976 pitch = IWineD3DSurface_GetPitch(iface);
1978 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
1979 int height = This->currentDesc.Height;
1981 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
1982 outpitch = width * bpp;
1983 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
1985 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
1986 if(!mem) {
1987 ERR("Out of memory %d, %d!\n", outpitch, height);
1988 return WINED3DERR_OUTOFVIDEOMEMORY;
1990 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
1992 This->Flags |= SFLAG_CONVERTED;
1993 } else if (This->resource.format == WINED3DFMT_P8 && GL_SUPPORT(EXT_PALETTED_TEXTURE)) {
1994 d3dfmt_p8_upload_palette(iface, convert);
1995 This->Flags &= ~SFLAG_CONVERTED;
1996 mem = This->resource.allocatedMemory;
1997 } else {
1998 This->Flags &= ~SFLAG_CONVERTED;
1999 mem = This->resource.allocatedMemory;
2002 /* Make sure the correct pitch is used */
2003 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
2005 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
2006 TRACE("non power of two support\n");
2007 if(!(This->Flags & SFLAG_ALLOCATED)) {
2008 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
2010 if (mem) {
2011 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
2013 } else {
2014 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
2015 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
2017 if(!(This->Flags & SFLAG_ALLOCATED)) {
2018 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
2020 if (mem) {
2021 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
2025 /* Restore the default pitch */
2026 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2028 if (mem != This->resource.allocatedMemory)
2029 HeapFree(GetProcessHeap(), 0, mem);
2031 #if 0
2033 static unsigned int gen = 0;
2034 char buffer[4096];
2035 ++gen;
2036 if ((gen % 10) == 0) {
2037 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2038 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2041 * debugging crash code
2042 if (gen == 250) {
2043 void** test = NULL;
2044 *test = 0;
2048 #endif
2050 if (!(This->Flags & SFLAG_DONOTFREE)) {
2051 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
2052 This->resource.allocatedMemory = NULL;
2053 This->Flags &= ~SFLAG_INSYSMEM;
2056 return WINED3D_OK;
2059 #include <errno.h>
2060 #include <stdio.h>
2061 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2062 FILE* f = NULL;
2063 UINT i, y;
2064 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2065 char *allocatedMemory;
2066 char *textureRow;
2067 IWineD3DSwapChain *swapChain = NULL;
2068 int width, height;
2069 GLuint tmpTexture = 0;
2070 DWORD color;
2071 /*FIXME:
2072 Textures may not be stored in ->allocatedgMemory and a GlTexture
2073 so we should lock the surface before saving a snapshot, or at least check that
2075 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2076 by calling GetTexImage and in compressed form by calling
2077 GetCompressedTexImageARB. Queried compressed images can be saved and
2078 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2079 texture images do not need to be processed by the GL and should
2080 significantly improve texture loading performance relative to uncompressed
2081 images. */
2083 /* Setup the width and height to be the internal texture width and height. */
2084 width = This->pow2Width;
2085 height = This->pow2Height;
2086 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2087 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2089 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2090 /* if were not a real texture then read the back buffer into a real texture */
2091 /* we don't want to interfere with the back buffer so read the data into a temporary
2092 * texture and then save the data out of the temporary texture
2094 GLint prevRead;
2095 ENTER_GL();
2096 TRACE("(%p) Reading render target into texture\n", This);
2097 glEnable(GL_TEXTURE_2D);
2099 glGenTextures(1, &tmpTexture);
2100 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2102 glTexImage2D(GL_TEXTURE_2D,
2104 GL_RGBA,
2105 width,
2106 height,
2107 0/*border*/,
2108 GL_RGBA,
2109 GL_UNSIGNED_INT_8_8_8_8_REV,
2110 NULL);
2112 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2113 vcheckGLcall("glGetIntegerv");
2114 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2115 vcheckGLcall("glReadBuffer");
2116 glCopyTexImage2D(GL_TEXTURE_2D,
2118 GL_RGBA,
2121 width,
2122 height,
2125 checkGLcall("glCopyTexImage2D");
2126 glReadBuffer(prevRead);
2127 LEAVE_GL();
2129 } else { /* bind the real texture, and make sure it up to date */
2130 IWineD3DSurface_PreLoad(iface);
2132 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2133 ENTER_GL();
2134 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2135 glGetTexImage(GL_TEXTURE_2D,
2136 This->glDescription.level,
2137 GL_RGBA,
2138 GL_UNSIGNED_INT_8_8_8_8_REV,
2139 allocatedMemory);
2140 checkGLcall("glTexImage2D");
2141 if (tmpTexture) {
2142 glBindTexture(GL_TEXTURE_2D, 0);
2143 glDeleteTextures(1, &tmpTexture);
2145 LEAVE_GL();
2147 f = fopen(filename, "w+");
2148 if (NULL == f) {
2149 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2150 return WINED3DERR_INVALIDCALL;
2152 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2153 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2154 /* TGA header */
2155 fputc(0,f);
2156 fputc(0,f);
2157 fputc(2,f);
2158 fputc(0,f);
2159 fputc(0,f);
2160 fputc(0,f);
2161 fputc(0,f);
2162 fputc(0,f);
2163 fputc(0,f);
2164 fputc(0,f);
2165 fputc(0,f);
2166 fputc(0,f);
2167 /* short width*/
2168 fwrite(&width,2,1,f);
2169 /* short height */
2170 fwrite(&height,2,1,f);
2171 /* format rgba */
2172 fputc(0x20,f);
2173 fputc(0x28,f);
2174 /* raw data */
2175 /* 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 */
2176 if(swapChain)
2177 textureRow = allocatedMemory + (width * (height - 1) *4);
2178 else
2179 textureRow = allocatedMemory;
2180 for (y = 0 ; y < height; y++) {
2181 for (i = 0; i < width; i++) {
2182 color = *((DWORD*)textureRow);
2183 fputc((color >> 16) & 0xFF, f); /* B */
2184 fputc((color >> 8) & 0xFF, f); /* G */
2185 fputc((color >> 0) & 0xFF, f); /* R */
2186 fputc((color >> 24) & 0xFF, f); /* A */
2187 textureRow += 4;
2189 /* take two rows of the pointer to the texture memory */
2190 if(swapChain)
2191 (textureRow-= width << 3);
2194 TRACE("Closing file\n");
2195 fclose(f);
2197 if(swapChain) {
2198 IWineD3DSwapChain_Release(swapChain);
2200 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2201 return WINED3D_OK;
2205 * Slightly inefficient way to handle multiple dirty rects but it works :)
2207 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2208 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2209 IWineD3DBaseTexture *baseTexture = NULL;
2210 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2211 surface_download_data(This);
2213 This->Flags &= ~(SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
2214 if (NULL != pDirtyRect) {
2215 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2216 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2217 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2218 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2219 } else {
2220 This->dirtyRect.left = 0;
2221 This->dirtyRect.top = 0;
2222 This->dirtyRect.right = This->currentDesc.Width;
2223 This->dirtyRect.bottom = This->currentDesc.Height;
2225 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2226 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2227 /* if the container is a basetexture then mark it dirty. */
2228 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2229 TRACE("Passing to container\n");
2230 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2231 IWineD3DBaseTexture_Release(baseTexture);
2233 return WINED3D_OK;
2236 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
2237 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2239 TRACE("This %p, container %p\n", This, container);
2241 /* We can't keep a reference to the container, since the container already keeps a reference to us. */
2243 TRACE("Setting container to %p from %p\n", container, This->container);
2244 This->container = container;
2246 return WINED3D_OK;
2249 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2250 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2251 const PixelFormatDesc *formatEntry = getFormatDescEntry(format);
2253 if (This->resource.format != WINED3DFMT_UNKNOWN) {
2254 FIXME("(%p) : The format of the surface must be WINED3DFORMAT_UNKNOWN\n", This);
2255 return WINED3DERR_INVALIDCALL;
2258 TRACE("(%p) : Setting texture format to (%d,%s)\n", This, format, debug_d3dformat(format));
2259 if (format == WINED3DFMT_UNKNOWN) {
2260 This->resource.size = 0;
2261 } else if (format == WINED3DFMT_DXT1) {
2262 /* DXT1 is half byte per pixel */
2263 This->resource.size = ((max(This->pow2Width, 4) * formatEntry->bpp) * max(This->pow2Height, 4)) >> 1;
2265 } else if (format == WINED3DFMT_DXT2 || format == WINED3DFMT_DXT3 ||
2266 format == WINED3DFMT_DXT4 || format == WINED3DFMT_DXT5) {
2267 This->resource.size = ((max(This->pow2Width, 4) * formatEntry->bpp) * max(This->pow2Height, 4));
2268 } else {
2269 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
2270 This->resource.size = ((This->pow2Width * formatEntry->bpp) + alignment - 1) & ~(alignment - 1);
2271 This->resource.size *= This->pow2Height;
2275 /* Setup some glformat defaults */
2276 This->glDescription.glFormat = formatEntry->glFormat;
2277 This->glDescription.glFormatInternal = formatEntry->glInternal;
2278 This->glDescription.glType = formatEntry->glType;
2280 if (format != WINED3DFMT_UNKNOWN) {
2281 This->bytesPerPixel = formatEntry->bpp;
2282 } else {
2283 This->bytesPerPixel = 0;
2286 This->Flags |= (WINED3DFMT_D16_LOCKABLE == format) ? SFLAG_LOCKABLE : 0;
2287 This->Flags &= ~SFLAG_ALLOCATED;
2289 This->resource.format = format;
2291 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);
2293 return WINED3D_OK;
2296 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2297 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2299 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer */
2300 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
2301 ERR("Not supported on render targets\n");
2302 return WINED3DERR_INVALIDCALL;
2305 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2306 WARN("Surface is locked or the HDC is in use\n");
2307 return WINED3DERR_INVALIDCALL;
2310 if(Mem && Mem != This->resource.allocatedMemory) {
2311 void *release = NULL;
2313 /* Do I have to copy the old surface content? */
2314 if(This->Flags & SFLAG_DIBSECTION) {
2315 /* Release the DC. No need to hold the critical section for the update
2316 * Thread because this thread runs only on front buffers, but this method
2317 * fails for render targets in the check above.
2319 SelectObject(This->hDC, This->dib.holdbitmap);
2320 DeleteDC(This->hDC);
2321 /* Release the DIB section */
2322 DeleteObject(This->dib.DIBsection);
2323 This->dib.bitmap_data = NULL;
2324 This->resource.allocatedMemory = NULL;
2325 This->hDC = NULL;
2326 This->Flags &= ~SFLAG_DIBSECTION;
2327 } else if(!(This->Flags & SFLAG_USERPTR)) {
2328 release = This->resource.allocatedMemory;
2330 This->resource.allocatedMemory = Mem;
2331 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2333 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2334 This->Flags &= ~(SFLAG_INDRAWABLE | SFLAG_INTEXTURE);
2336 /* For client textures opengl has to be notified */
2337 if(This->Flags & SFLAG_CLIENT) {
2338 This->Flags &= ~SFLAG_ALLOCATED;
2339 IWineD3DSurface_PreLoad(iface);
2340 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2343 /* Now free the old memory if any */
2344 HeapFree(GetProcessHeap(), 0, release);
2345 } else if(This->Flags & SFLAG_USERPTR) {
2346 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2347 This->resource.allocatedMemory = NULL;
2348 This->Flags &= ~SFLAG_USERPTR;
2350 if(This->Flags & SFLAG_CLIENT) {
2351 This->Flags &= ~SFLAG_ALLOCATED;
2352 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2353 IWineD3DSurface_PreLoad(iface);
2356 return WINED3D_OK;
2359 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2360 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2361 IWineD3DSwapChainImpl *swapchain = NULL;
2362 HRESULT hr;
2363 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2365 /* Flipping is only supported on RenderTargets */
2366 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2368 if(override) {
2369 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2370 * FIXME("(%p) Target override is not supported by now\n", This);
2371 * Additionally, it isn't really possible to support triple-buffering
2372 * properly on opengl at all
2376 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2377 if(!swapchain) {
2378 ERR("Flipped surface is not on a swapchain\n");
2379 return WINEDDERR_NOTFLIPPABLE;
2382 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2383 * and only d3d8 and d3d9 apps specify the presentation interval
2385 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2386 /* Most common case first to avoid wasting time on all the other cases */
2387 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2388 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2389 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2390 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2391 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2392 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2393 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2394 } else {
2395 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2398 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2399 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2400 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2401 return hr;
2404 /* Does a direct frame buffer -> texture copy. Stretching is done
2405 * with single pixel copy calls
2407 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2408 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2409 float xrel, yrel;
2410 UINT row;
2411 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2413 ENTER_GL();
2415 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2416 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2418 /* Bind the target texture */
2419 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
2420 checkGLcall("glBindTexture");
2421 if(!swapchain) {
2422 glReadBuffer(myDevice->offscreenBuffer);
2423 } else {
2424 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2425 glReadBuffer(buffer);
2427 checkGLcall("glReadBuffer");
2429 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2430 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2432 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2433 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2435 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2436 ERR("Texture filtering not supported in direct blit\n");
2438 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2439 ERR("Texture filtering not supported in direct blit\n");
2442 if(upsidedown &&
2443 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2444 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2445 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2447 glCopyTexSubImage2D(This->glDescription.target,
2448 This->glDescription.level,
2449 drect->x1, drect->y1, /* xoffset, yoffset */
2450 srect->x1, Src->currentDesc.Height - srect->y2,
2451 drect->x2 - drect->x1, drect->y2 - drect->y1);
2452 } else {
2453 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2454 /* I have to process this row by row to swap the image,
2455 * otherwise it would be upside down, so stretching in y direction
2456 * doesn't cost extra time
2458 * However, stretching in x direction can be avoided if not necessary
2460 for(row = drect->y1; row < drect->y2; row++) {
2461 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2462 /* Well, that stuff works, but it's very slow.
2463 * find a better way instead
2465 UINT col;
2467 for(col = drect->x1; col < drect->x2; col++) {
2468 glCopyTexSubImage2D(This->glDescription.target,
2469 This->glDescription.level,
2470 drect->x1 + col, row, /* xoffset, yoffset */
2471 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2472 1, 1);
2474 } else {
2475 glCopyTexSubImage2D(This->glDescription.target,
2476 This->glDescription.level,
2477 drect->x1, row, /* xoffset, yoffset */
2478 srect->x1, yoffset - (int) (row * yrel),
2479 drect->x2-drect->x1, 1);
2484 vcheckGLcall("glCopyTexSubImage2D");
2485 LEAVE_GL();
2488 /* Uses the hardware to stretch and flip the image */
2489 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2490 GLuint src, backup = 0;
2491 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2492 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2493 float left, right, top, bottom; /* Texture coordinates */
2494 UINT fbwidth = Src->currentDesc.Width;
2495 UINT fbheight = Src->currentDesc.Height;
2496 GLenum drawBuffer = GL_BACK;
2498 TRACE("Using hwstretch blit\n");
2499 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2500 ENTER_GL();
2501 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2502 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2504 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2505 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2507 if(GL_LIMITS(aux_buffers) >= 2) {
2508 /* Got more than one aux buffer? Use the 2nd aux buffer */
2509 drawBuffer = GL_AUX1;
2510 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2511 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2512 drawBuffer = GL_AUX0;
2515 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2516 glGenTextures(1, &backup);
2517 checkGLcall("glGenTextures\n");
2518 glBindTexture(GL_TEXTURE_2D, backup);
2519 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2520 } else {
2521 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2522 * we are reading from the back buffer, the backup can be used as source texture
2524 if(Src->glDescription.textureName == 0) {
2525 /* Get it a description */
2526 IWineD3DSurface_PreLoad(SrcSurface);
2528 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
2529 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2531 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2532 Src->Flags &= ~SFLAG_INTEXTURE;
2535 glReadBuffer(GL_BACK);
2536 checkGLcall("glReadBuffer(GL_BACK)");
2538 /* TODO: Only back up the part that will be overwritten */
2539 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2540 0, 0 /* read offsets */,
2541 0, 0,
2542 fbwidth,
2543 fbheight);
2545 checkGLcall("glCopyTexSubImage2D");
2547 /* No issue with overriding these - the sampler is dirty due to blit usage */
2548 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
2549 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2550 checkGLcall("glTexParameteri");
2551 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
2552 minMipLookup[Filter][WINED3DTEXF_NONE]);
2553 checkGLcall("glTexParameteri");
2555 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2556 src = backup ? backup : Src->glDescription.textureName;
2557 } else {
2558 glReadBuffer(GL_FRONT);
2559 checkGLcall("glReadBuffer(GL_FRONT)");
2561 glGenTextures(1, &src);
2562 checkGLcall("glGenTextures(1, &src)");
2563 glBindTexture(GL_TEXTURE_2D, src);
2564 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2566 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2567 * out for power of 2 sizes
2569 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2570 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2571 checkGLcall("glTexImage2D");
2572 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2573 0, 0 /* read offsets */,
2574 0, 0,
2575 fbwidth,
2576 fbheight);
2578 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2579 checkGLcall("glTexParameteri");
2580 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2581 checkGLcall("glTexParameteri");
2583 glReadBuffer(GL_BACK);
2584 checkGLcall("glReadBuffer(GL_BACK)");
2586 checkGLcall("glEnd and previous");
2588 left = (float) srect->x1 / (float) Src->pow2Width;
2589 right = (float) srect->x2 / (float) Src->pow2Width;
2591 if(upsidedown) {
2592 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2593 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2594 } else {
2595 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2596 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2599 /* draw the source texture stretched and upside down. The correct surface is bound already */
2600 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
2601 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
2603 glDrawBuffer(drawBuffer);
2604 glReadBuffer(drawBuffer);
2606 glBegin(GL_QUADS);
2607 /* bottom left */
2608 glTexCoord2f(left, bottom);
2609 glVertex2i(0, fbheight);
2611 /* top left */
2612 glTexCoord2f(left, top);
2613 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2615 /* top right */
2616 glTexCoord2f(right, top);
2617 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2619 /* bottom right */
2620 glTexCoord2f(right, bottom);
2621 glVertex2i(drect->x2 - drect->x1, fbheight);
2622 glEnd();
2623 checkGLcall("glEnd and previous");
2625 /* Now read the stretched and upside down image into the destination texture */
2626 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2627 checkGLcall("glBindTexture");
2628 glCopyTexSubImage2D(This->glDescription.target,
2630 drect->x1, drect->y1, /* xoffset, yoffset */
2631 0, 0, /* We blitted the image to the origin */
2632 drect->x2 - drect->x1, drect->y2 - drect->y1);
2633 checkGLcall("glCopyTexSubImage2D");
2635 /* Write the back buffer backup back */
2636 glBindTexture(GL_TEXTURE_2D, backup ? backup : Src->glDescription.textureName);
2637 checkGLcall("glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName)");
2639 if(drawBuffer == GL_BACK) {
2640 glBegin(GL_QUADS);
2641 /* top left */
2642 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2643 glVertex2i(0, 0);
2645 /* bottom left */
2646 glTexCoord2f(0.0, 0.0);
2647 glVertex2i(0, fbheight);
2649 /* bottom right */
2650 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2651 glVertex2i(fbwidth, Src->currentDesc.Height);
2653 /* top right */
2654 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2655 glVertex2i(fbwidth, 0);
2656 glEnd();
2657 } else {
2658 /* Restore the old draw buffer */
2659 glDrawBuffer(GL_BACK);
2662 /* Cleanup */
2663 if(src != Src->glDescription.textureName && src != backup) {
2664 glDeleteTextures(1, &src);
2665 checkGLcall("glDeleteTextures(1, &src)");
2667 if(backup) {
2668 glDeleteTextures(1, &backup);
2669 checkGLcall("glDeleteTextures(1, &backup)");
2671 LEAVE_GL();
2674 /* Not called from the VTable */
2675 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2676 WINED3DRECT rect;
2677 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2678 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2679 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2681 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2683 /* Get the swapchain. One of the surfaces has to be a primary surface */
2684 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2685 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2686 if(Src) {
2687 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2688 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2691 /* Early sort out of cases where no render target is used */
2692 if(!dstSwapchain && !srcSwapchain &&
2693 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2694 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2695 return WINED3DERR_INVALIDCALL;
2698 /* No destination color keying supported */
2699 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
2700 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2701 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2702 return WINED3DERR_INVALIDCALL;
2705 if (DestRect) {
2706 rect.x1 = DestRect->left;
2707 rect.y1 = DestRect->top;
2708 rect.x2 = DestRect->right;
2709 rect.y2 = DestRect->bottom;
2710 } else {
2711 rect.x1 = 0;
2712 rect.y1 = 0;
2713 rect.x2 = This->currentDesc.Width;
2714 rect.y2 = This->currentDesc.Height;
2717 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2718 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
2719 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
2720 /* Half-life does a Blt from the back buffer to the front buffer,
2721 * Full surface size, no flags... Use present instead
2723 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
2726 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2727 while(1)
2729 RECT mySrcRect;
2730 TRACE("Looking if a Present can be done...\n");
2731 /* Source Rectangle must be full surface */
2732 if( SrcRect ) {
2733 if(SrcRect->left != 0 || SrcRect->top != 0 ||
2734 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
2735 TRACE("No, Source rectangle doesn't match\n");
2736 break;
2739 mySrcRect.left = 0;
2740 mySrcRect.top = 0;
2741 mySrcRect.right = Src->currentDesc.Width;
2742 mySrcRect.bottom = Src->currentDesc.Height;
2744 /* No stretching may occur */
2745 if(mySrcRect.right != rect.x2 - rect.x1 ||
2746 mySrcRect.bottom != rect.y2 - rect.y1) {
2747 TRACE("No, stretching is done\n");
2748 break;
2751 /* Destination must be full surface or match the clipping rectangle */
2752 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
2754 RECT cliprect;
2755 POINT pos[2];
2756 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
2757 pos[0].x = rect.x1;
2758 pos[0].y = rect.y1;
2759 pos[1].x = rect.x2;
2760 pos[1].y = rect.y2;
2761 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
2762 pos, 2);
2764 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
2765 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
2767 TRACE("No, dest rectangle doesn't match(clipper)\n");
2768 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
2769 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
2770 break;
2773 else
2775 if(rect.x1 != 0 || rect.y1 != 0 ||
2776 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
2777 TRACE("No, dest rectangle doesn't match(surface size)\n");
2778 break;
2782 TRACE("Yes\n");
2784 /* These flags are unimportant for the flag check, remove them */
2785 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
2786 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
2788 /* The idea behind this is that a glReadPixels and a glDrawPixels call
2789 * take very long, while a flip is fast.
2790 * This applies to Half-Life, which does such Blts every time it finished
2791 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
2792 * menu. This is also used by all apps when they do windowed rendering
2794 * The problem is that flipping is not really the same as copying. After a
2795 * Blt the front buffer is a copy of the back buffer, and the back buffer is
2796 * untouched. Therefore it's necessary to override the swap effect
2797 * and to set it back after the flip.
2799 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
2800 * testcases.
2803 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
2804 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2806 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
2807 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
2809 dstSwapchain->presentParms.SwapEffect = orig_swap;
2811 return WINED3D_OK;
2813 break;
2816 TRACE("Unsupported blit between buffers on the same swapchain\n");
2817 return WINED3DERR_INVALIDCALL;
2818 } else if((dstSwapchain || This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) &&
2819 (srcSwapchain || SrcSurface == myDevice->render_targets[0]) ) {
2820 ERR("Can't perform hardware blit between 2 different swapchains, falling back to software\n");
2821 return WINED3DERR_INVALIDCALL;
2824 if(srcSwapchain || SrcSurface == myDevice->render_targets[0]) {
2825 /* Blit from render target to texture */
2826 WINED3DRECT srect;
2827 BOOL upsideDown, stretchx;
2829 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
2830 TRACE("Color keying not supported by frame buffer to texture blit\n");
2831 return WINED3DERR_INVALIDCALL;
2832 /* Destination color key is checked above */
2835 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2836 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2838 if(SrcRect) {
2839 if(SrcRect->top < SrcRect->bottom) {
2840 srect.y1 = SrcRect->top;
2841 srect.y2 = SrcRect->bottom;
2842 upsideDown = FALSE;
2843 } else {
2844 srect.y1 = SrcRect->bottom;
2845 srect.y2 = SrcRect->top;
2846 upsideDown = TRUE;
2848 srect.x1 = SrcRect->left;
2849 srect.x2 = SrcRect->right;
2850 } else {
2851 srect.x1 = 0;
2852 srect.y1 = 0;
2853 srect.x2 = Src->currentDesc.Width;
2854 srect.y2 = Src->currentDesc.Height;
2855 upsideDown = FALSE;
2857 if(rect.x1 > rect.x2) {
2858 UINT tmp = rect.x2;
2859 rect.x2 = rect.x1;
2860 rect.x1 = tmp;
2861 upsideDown = !upsideDown;
2863 if(!srcSwapchain) {
2864 TRACE("Reading from an offscreen target\n");
2865 upsideDown = !upsideDown;
2868 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
2869 stretchx = TRUE;
2870 } else {
2871 stretchx = FALSE;
2874 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2875 * flip the image nor scale it.
2877 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2878 * -> If the app wants a image width an unscaled width, copy it line per line
2879 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
2880 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2881 * back buffer. This is slower than reading line per line, thus not used for flipping
2882 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2883 * pixel by pixel
2885 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
2886 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
2887 * backends.
2889 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
2890 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
2891 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
2892 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
2893 rect.y2 - rect.y1 > Src->currentDesc.Height) {
2894 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
2895 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
2896 } else {
2897 TRACE("Using hardware stretching to flip / stretch the texture\n");
2898 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
2901 if(!(This->Flags & SFLAG_DONOTFREE)) {
2902 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
2903 This->resource.allocatedMemory = NULL;
2904 } else {
2905 This->Flags &= ~SFLAG_INSYSMEM;
2907 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
2908 * path is never entered
2910 This->Flags |= SFLAG_INTEXTURE;
2912 return WINED3D_OK;
2913 } else if(Src) {
2914 /* Blit from offscreen surface to render target */
2915 float glTexCoord[4];
2916 DWORD oldCKeyFlags = Src->CKeyFlags;
2917 WINEDDCOLORKEY oldBltCKey = This->SrcBltCKey;
2918 RECT SourceRectangle;
2920 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
2922 if(SrcRect) {
2923 SourceRectangle.left = SrcRect->left;
2924 SourceRectangle.right = SrcRect->right;
2925 SourceRectangle.top = SrcRect->top;
2926 SourceRectangle.bottom = SrcRect->bottom;
2927 } else {
2928 SourceRectangle.left = 0;
2929 SourceRectangle.right = Src->currentDesc.Width;
2930 SourceRectangle.top = 0;
2931 SourceRectangle.bottom = Src->currentDesc.Height;
2934 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
2935 /* Fall back to software */
2936 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
2937 SourceRectangle.left, SourceRectangle.top,
2938 SourceRectangle.right, SourceRectangle.bottom);
2939 return WINED3DERR_INVALIDCALL;
2942 /* Color keying: Check if we have to do a color keyed blt,
2943 * and if not check if a color key is activated.
2945 * Just modify the color keying parameters in the surface and restore them afterwards
2946 * The surface keeps track of the color key last used to load the opengl surface.
2947 * PreLoad will catch the change to the flags and color key and reload if necessary.
2949 if(Flags & WINEDDBLT_KEYSRC) {
2950 /* Use color key from surface */
2951 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
2952 /* Use color key from DDBltFx */
2953 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
2954 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
2955 } else {
2956 /* Do not use color key */
2957 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
2960 /* Now load the surface */
2961 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
2963 ENTER_GL();
2965 /* Activate the destination context, set it up for blitting */
2966 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
2968 if(!dstSwapchain) {
2969 TRACE("Drawing to offscreen buffer\n");
2970 glDrawBuffer(myDevice->offscreenBuffer);
2971 checkGLcall("glDrawBuffer");
2972 } else {
2973 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
2974 TRACE("Drawing to %#x buffer\n", buffer);
2975 glDrawBuffer(buffer);
2976 checkGLcall("glDrawBuffer");
2979 /* Bind the texture */
2980 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
2981 checkGLcall("glBindTexture");
2983 /* Filtering for StretchRect */
2984 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
2985 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2986 checkGLcall("glTexParameteri");
2987 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
2988 minMipLookup[Filter][WINED3DTEXF_NONE]);
2989 checkGLcall("glTexParameteri");
2990 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
2991 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
2992 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2993 checkGLcall("glTexEnvi");
2995 /* This is for color keying */
2996 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
2997 glEnable(GL_ALPHA_TEST);
2998 checkGLcall("glEnable GL_ALPHA_TEST");
2999 glAlphaFunc(GL_NOTEQUAL, 0.0);
3000 checkGLcall("glAlphaFunc\n");
3001 } else {
3002 glDisable(GL_ALPHA_TEST);
3003 checkGLcall("glDisable GL_ALPHA_TEST");
3006 /* Draw a textured quad
3008 glBegin(GL_QUADS);
3010 glColor3d(1.0f, 1.0f, 1.0f);
3011 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3012 glVertex3f(rect.x1,
3013 rect.y1,
3014 0.0);
3016 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3017 glVertex3f(rect.x1, rect.y2, 0.0);
3019 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3020 glVertex3f(rect.x2,
3021 rect.y2,
3022 0.0);
3024 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3025 glVertex3f(rect.x2,
3026 rect.y1,
3027 0.0);
3028 glEnd();
3029 checkGLcall("glEnd");
3031 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3032 glDisable(GL_ALPHA_TEST);
3033 checkGLcall("glDisable(GL_ALPHA_TEST)");
3036 /* Unbind the texture */
3037 glBindTexture(GL_TEXTURE_2D, 0);
3038 checkGLcall("glEnable glBindTexture");
3040 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3041 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3043 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3044 glDrawBuffer(GL_BACK);
3045 checkGLcall("glDrawBuffer");
3047 /* Restore the color key parameters */
3048 Src->CKeyFlags = oldCKeyFlags;
3049 This->SrcBltCKey = oldBltCKey;
3051 LEAVE_GL();
3053 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3054 This->Flags &= ~SFLAG_INSYSMEM;
3055 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3056 * is outdated now
3058 if(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO) {
3059 This->Flags |= SFLAG_INDRAWABLE;
3060 This->Flags &= ~SFLAG_INTEXTURE;
3061 } else {
3062 This->Flags |= SFLAG_INTEXTURE;
3065 return WINED3D_OK;
3066 } else {
3067 /* Source-Less Blit to render target */
3068 if (Flags & WINEDDBLT_COLORFILL) {
3069 /* This is easy to handle for the D3D Device... */
3070 DWORD color;
3072 TRACE("Colorfill\n");
3074 /* The color as given in the Blt function is in the format of the frame-buffer...
3075 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3077 if (This->resource.format == WINED3DFMT_P8) {
3078 if (This->palette) {
3079 color = ((0xFF000000) |
3080 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3081 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3082 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3083 } else {
3084 color = 0xFF000000;
3087 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3088 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3089 color = 0xFFFFFFFF;
3090 } else {
3091 color = ((0xFF000000) |
3092 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3093 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3094 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3097 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3098 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3099 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3101 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3102 color = DDBltFx->u5.dwFillColor;
3104 else {
3105 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3106 return WINED3DERR_INVALIDCALL;
3109 TRACE("Calling GetSwapChain with mydevice = %p\n", myDevice);
3110 if(dstSwapchain && dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]) {
3111 glDrawBuffer(GL_BACK);
3112 checkGLcall("glDrawBuffer(GL_BACK)");
3113 } else if (dstSwapchain && This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer) {
3114 glDrawBuffer(GL_FRONT);
3115 checkGLcall("glDrawBuffer(GL_FRONT)");
3116 } else if(This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3117 glDrawBuffer(myDevice->offscreenBuffer);
3118 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer3)");
3119 } else {
3120 TRACE("Surface is higher back buffer, falling back to software\n");
3121 return WINED3DERR_INVALIDCALL;
3124 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3126 IWineD3DDevice_Clear( (IWineD3DDevice *) myDevice,
3127 1 /* Number of rectangles */,
3128 &rect,
3129 WINED3DCLEAR_TARGET,
3130 color,
3131 0.0 /* Z */,
3132 0 /* Stencil */);
3134 /* Restore the original draw buffer */
3135 if(!dstSwapchain) {
3136 glDrawBuffer(myDevice->offscreenBuffer);
3137 } else if(dstSwapchain->backBuffer && dstSwapchain->backBuffer[0]) {
3138 glDrawBuffer(GL_BACK);
3140 vcheckGLcall("glDrawBuffer");
3142 return WINED3D_OK;
3146 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3147 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3148 return WINED3DERR_INVALIDCALL;
3151 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3153 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3155 if (Flags & WINEDDBLT_DEPTHFILL) {
3156 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3157 DestRect == NULL ? 0 : 1,
3158 (WINED3DRECT *) DestRect,
3159 WINED3DCLEAR_ZBUFFER,
3160 0x00000000,
3161 (float) DDBltFx->u5.dwFillDepth / (float) MAXDWORD,
3162 0x00000000);
3164 return WINED3D_OK;
3167 FIXME("(%p): Unsupp depthstencil blit\n", This);
3168 return WINED3DERR_INVALIDCALL;
3171 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3172 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3173 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3174 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3175 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3176 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3178 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3179 * except depth blits, which seem to work
3181 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3182 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3183 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3184 return WINED3DERR_INVALIDCALL;
3185 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3186 TRACE("Z Blit override handled the blit\n");
3187 return WINED3D_OK;
3191 /* Special cases for RenderTargets */
3192 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3193 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3194 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3197 /* For the rest call the X11 surface implementation.
3198 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3199 * other Blts are rather rare
3201 return IWineGDISurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3204 HRESULT WINAPI IWineD3DSurfaceImpl_GetBltStatus(IWineD3DSurface *iface, DWORD Flags) {
3205 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3206 TRACE("(%p)->(%x)\n", This, Flags);
3208 switch (Flags)
3210 case WINEDDGBS_CANBLT:
3211 case WINEDDGBS_ISBLTDONE:
3212 return WINED3D_OK;
3214 default:
3215 return WINED3DERR_INVALIDCALL;
3219 HRESULT WINAPI IWineD3DSurfaceImpl_GetFlipStatus(IWineD3DSurface *iface, DWORD Flags) {
3220 /* XXX: DDERR_INVALIDSURFACETYPE */
3222 TRACE("(%p)->(%08x)\n",iface,Flags);
3223 switch (Flags) {
3224 case WINEDDGFS_CANFLIP:
3225 case WINEDDGFS_ISFLIPDONE:
3226 return WINED3D_OK;
3228 default:
3229 return WINED3DERR_INVALIDCALL;
3233 HRESULT WINAPI IWineD3DSurfaceImpl_IsLost(IWineD3DSurface *iface) {
3234 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3235 TRACE("(%p)\n", This);
3237 /* D3D8 and 9 loose full devices, ddraw only surfaces */
3238 return This->Flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
3241 HRESULT WINAPI IWineD3DSurfaceImpl_Restore(IWineD3DSurface *iface) {
3242 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3243 TRACE("(%p)\n", This);
3245 /* So far we don't lose anything :) */
3246 This->Flags &= ~SFLAG_LOST;
3247 return WINED3D_OK;
3250 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3251 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3252 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3253 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3254 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3256 if(myDevice->inScene &&
3257 (iface == myDevice->stencilBufferTarget ||
3258 (Source && Source == myDevice->stencilBufferTarget))) {
3259 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3260 return WINED3DERR_INVALIDCALL;
3263 /* Special cases for RenderTargets */
3264 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3265 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3267 RECT SrcRect, DstRect;
3268 DWORD Flags=0;
3270 if(rsrc) {
3271 SrcRect.left = rsrc->left;
3272 SrcRect.top= rsrc->top;
3273 SrcRect.bottom = rsrc->bottom;
3274 SrcRect.right = rsrc->right;
3275 } else {
3276 SrcRect.left = 0;
3277 SrcRect.top = 0;
3278 SrcRect.right = srcImpl->currentDesc.Width;
3279 SrcRect.bottom = srcImpl->currentDesc.Height;
3282 DstRect.left = dstx;
3283 DstRect.top=dsty;
3284 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3285 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3287 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3288 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3289 Flags |= WINEDDBLT_KEYSRC;
3290 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3291 Flags |= WINEDDBLT_KEYDEST;
3292 if(trans & WINEDDBLTFAST_WAIT)
3293 Flags |= WINEDDBLT_WAIT;
3294 if(trans & WINEDDBLTFAST_DONOTWAIT)
3295 Flags |= WINEDDBLT_DONOTWAIT;
3297 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3301 return IWineGDISurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3304 HRESULT WINAPI IWineD3DSurfaceImpl_GetPalette(IWineD3DSurface *iface, IWineD3DPalette **Pal) {
3305 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3306 TRACE("(%p)->(%p)\n", This, Pal);
3308 *Pal = (IWineD3DPalette *) This->palette;
3309 return WINED3D_OK;
3312 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
3313 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3314 RGBQUAD col[256];
3315 IWineD3DPaletteImpl *pal = This->palette;
3316 unsigned int n;
3317 TRACE("(%p)\n", This);
3319 if(This->resource.format == WINED3DFMT_P8 ||
3320 This->resource.format == WINED3DFMT_A8P8)
3322 if(!This->Flags & SFLAG_INSYSMEM) {
3323 FIXME("Palette changed with surface that does not have an up to date system memory copy\n");
3325 TRACE("Dirtifying surface\n");
3326 This->Flags &= ~(SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3329 if(This->Flags & SFLAG_DIBSECTION) {
3330 TRACE("(%p): Updating the hdc's palette\n", This);
3331 for (n=0; n<256; n++) {
3332 if(pal) {
3333 col[n].rgbRed = pal->palents[n].peRed;
3334 col[n].rgbGreen = pal->palents[n].peGreen;
3335 col[n].rgbBlue = pal->palents[n].peBlue;
3336 } else {
3337 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3338 /* Use the default device palette */
3339 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
3340 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
3341 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
3343 col[n].rgbReserved = 0;
3345 SetDIBColorTable(This->hDC, 0, 256, col);
3348 return WINED3D_OK;
3351 HRESULT WINAPI IWineD3DSurfaceImpl_SetPalette(IWineD3DSurface *iface, IWineD3DPalette *Pal) {
3352 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3353 IWineD3DPaletteImpl *PalImpl = (IWineD3DPaletteImpl *) Pal;
3354 TRACE("(%p)->(%p)\n", This, Pal);
3356 if(This->palette != NULL)
3357 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3358 This->palette->Flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
3360 if(PalImpl != NULL) {
3361 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3362 /* Set the device's main palette if the palette
3363 * wasn't a primary palette before
3365 if(!(PalImpl->Flags & WINEDDPCAPS_PRIMARYSURFACE)) {
3366 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3367 unsigned int i;
3369 for(i=0; i < 256; i++) {
3370 device->palettes[device->currentPalette][i] = PalImpl->palents[i];
3374 (PalImpl)->Flags |= WINEDDPCAPS_PRIMARYSURFACE;
3377 This->palette = PalImpl;
3379 return IWineD3DSurface_RealizePalette(iface);
3382 HRESULT WINAPI IWineD3DSurfaceImpl_SetColorKey(IWineD3DSurface *iface, DWORD Flags, WINEDDCOLORKEY *CKey) {
3383 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3384 TRACE("(%p)->(%08x,%p)\n", This, Flags, CKey);
3386 if ((Flags & WINEDDCKEY_COLORSPACE) != 0) {
3387 FIXME(" colorkey value not supported (%08x) !\n", Flags);
3388 return WINED3DERR_INVALIDCALL;
3391 /* Dirtify the surface, but only if a key was changed */
3392 if(CKey) {
3393 switch (Flags & ~WINEDDCKEY_COLORSPACE) {
3394 case WINEDDCKEY_DESTBLT:
3395 This->DestBltCKey = *CKey;
3396 This->CKeyFlags |= WINEDDSD_CKDESTBLT;
3397 break;
3399 case WINEDDCKEY_DESTOVERLAY:
3400 This->DestOverlayCKey = *CKey;
3401 This->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3402 break;
3404 case WINEDDCKEY_SRCOVERLAY:
3405 This->SrcOverlayCKey = *CKey;
3406 This->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3407 break;
3409 case WINEDDCKEY_SRCBLT:
3410 This->SrcBltCKey = *CKey;
3411 This->CKeyFlags |= WINEDDSD_CKSRCBLT;
3412 break;
3415 else {
3416 switch (Flags & ~WINEDDCKEY_COLORSPACE) {
3417 case WINEDDCKEY_DESTBLT:
3418 This->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3419 break;
3421 case WINEDDCKEY_DESTOVERLAY:
3422 This->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3423 break;
3425 case WINEDDCKEY_SRCOVERLAY:
3426 This->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3427 break;
3429 case WINEDDCKEY_SRCBLT:
3430 This->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3431 break;
3435 return WINED3D_OK;
3438 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3439 /** Check against the maximum texture sizes supported by the video card **/
3440 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3442 TRACE("%p\n", This);
3443 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3444 /* one of three options
3445 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)
3446 2: Set the texture to the maximum size (bad idea)
3447 3: WARN and return WINED3DERR_NOTAVAILABLE;
3448 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.
3450 WARN("(%p) Creating an oversized surface\n", This);
3451 This->Flags |= SFLAG_OVERSIZE;
3453 /* This will be initialized on the first blt */
3454 This->glRect.left = 0;
3455 This->glRect.top = 0;
3456 This->glRect.right = 0;
3457 This->glRect.bottom = 0;
3458 } else {
3459 /* No oversize, gl rect is the full texture size */
3460 This->Flags &= ~SFLAG_OVERSIZE;
3461 This->glRect.left = 0;
3462 This->glRect.top = 0;
3463 This->glRect.right = This->pow2Width;
3464 This->glRect.bottom = This->pow2Height;
3467 if(This->resource.allocatedMemory == NULL) {
3468 /* Make sure memory exists from the start, and it is initialized properly. D3D initializes surfaces,
3469 * gl does not, so we need to upload zeroes to init the gl texture.
3471 This->resource.allocatedMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->resource.size + 4);
3473 This->Flags |= SFLAG_INSYSMEM;
3475 return WINED3D_OK;
3478 DWORD WINAPI IWineD3DSurfaceImpl_GetPitch(IWineD3DSurface *iface) {
3479 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3480 DWORD ret;
3481 TRACE("(%p)\n", This);
3483 /* DXTn formats don't have exact pitches as they are to the new row of blocks,
3484 where each block is 4x4 pixels, 8 bytes (dxt1) and 16 bytes (dxt2/3/4/5)
3485 ie pitch = (width/4) * bytes per block */
3486 if (This->resource.format == WINED3DFMT_DXT1) /* DXT1 is 8 bytes per block */
3487 ret = ((This->currentDesc.Width + 3) >> 2) << 3;
3488 else if (This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
3489 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) /* DXT2/3/4/5 is 16 bytes per block */
3490 ret = ((This->currentDesc.Width + 3) >> 2) << 4;
3491 else {
3492 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
3493 ret = This->bytesPerPixel * This->currentDesc.Width; /* Bytes / row */
3494 ret = (ret + alignment - 1) & ~(alignment - 1);
3496 TRACE("(%p) Returning %d\n", This, ret);
3497 return ret;
3500 HRESULT WINAPI IWineD3DSurfaceImpl_SetOverlayPosition(IWineD3DSurface *iface, LONG X, LONG Y) {
3501 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3503 FIXME("(%p)->(%d,%d) Stub!\n", This, X, Y);
3505 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3507 TRACE("(%p): Not an overlay surface\n", This);
3508 return WINEDDERR_NOTAOVERLAYSURFACE;
3511 return WINED3D_OK;
3514 HRESULT WINAPI IWineD3DSurfaceImpl_GetOverlayPosition(IWineD3DSurface *iface, LONG *X, LONG *Y) {
3515 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3517 FIXME("(%p)->(%p,%p) Stub!\n", This, X, Y);
3519 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3521 TRACE("(%p): Not an overlay surface\n", This);
3522 return WINEDDERR_NOTAOVERLAYSURFACE;
3525 return WINED3D_OK;
3528 HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlayZOrder(IWineD3DSurface *iface, DWORD Flags, IWineD3DSurface *Ref) {
3529 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3530 IWineD3DSurfaceImpl *RefImpl = (IWineD3DSurfaceImpl *) Ref;
3532 FIXME("(%p)->(%08x,%p) Stub!\n", This, Flags, RefImpl);
3534 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3536 TRACE("(%p): Not an overlay surface\n", This);
3537 return WINEDDERR_NOTAOVERLAYSURFACE;
3540 return WINED3D_OK;
3543 HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlay(IWineD3DSurface *iface, RECT *SrcRect, IWineD3DSurface *DstSurface, RECT *DstRect, DWORD Flags, WINEDDOVERLAYFX *FX) {
3544 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3545 IWineD3DSurfaceImpl *Dst = (IWineD3DSurfaceImpl *) DstSurface;
3546 FIXME("(%p)->(%p, %p, %p, %08x, %p)\n", This, SrcRect, Dst, DstRect, Flags, FX);
3548 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3550 TRACE("(%p): Not an overlay surface\n", This);
3551 return WINEDDERR_NOTAOVERLAYSURFACE;
3554 return WINED3D_OK;
3557 HRESULT WINAPI IWineD3DSurfaceImpl_SetClipper(IWineD3DSurface *iface, IWineD3DClipper *clipper)
3559 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3560 TRACE("(%p)->(%p)\n", This, clipper);
3562 This->clipper = clipper;
3563 return WINED3D_OK;
3566 HRESULT WINAPI IWineD3DSurfaceImpl_GetClipper(IWineD3DSurface *iface, IWineD3DClipper **clipper)
3568 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3569 TRACE("(%p)->(%p)\n", This, clipper);
3571 *clipper = This->clipper;
3572 if(*clipper) {
3573 IWineD3DClipper_AddRef(*clipper);
3575 return WINED3D_OK;
3578 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
3580 /* IUnknown */
3581 IWineD3DSurfaceImpl_QueryInterface,
3582 IWineD3DSurfaceImpl_AddRef,
3583 IWineD3DSurfaceImpl_Release,
3584 /* IWineD3DResource */
3585 IWineD3DSurfaceImpl_GetParent,
3586 IWineD3DSurfaceImpl_GetDevice,
3587 IWineD3DSurfaceImpl_SetPrivateData,
3588 IWineD3DSurfaceImpl_GetPrivateData,
3589 IWineD3DSurfaceImpl_FreePrivateData,
3590 IWineD3DSurfaceImpl_SetPriority,
3591 IWineD3DSurfaceImpl_GetPriority,
3592 IWineD3DSurfaceImpl_PreLoad,
3593 IWineD3DSurfaceImpl_GetType,
3594 /* IWineD3DSurface */
3595 IWineD3DSurfaceImpl_GetContainer,
3596 IWineD3DSurfaceImpl_GetDesc,
3597 IWineD3DSurfaceImpl_LockRect,
3598 IWineD3DSurfaceImpl_UnlockRect,
3599 IWineD3DSurfaceImpl_GetDC,
3600 IWineD3DSurfaceImpl_ReleaseDC,
3601 IWineD3DSurfaceImpl_Flip,
3602 IWineD3DSurfaceImpl_Blt,
3603 IWineD3DSurfaceImpl_GetBltStatus,
3604 IWineD3DSurfaceImpl_GetFlipStatus,
3605 IWineD3DSurfaceImpl_IsLost,
3606 IWineD3DSurfaceImpl_Restore,
3607 IWineD3DSurfaceImpl_BltFast,
3608 IWineD3DSurfaceImpl_GetPalette,
3609 IWineD3DSurfaceImpl_SetPalette,
3610 IWineD3DSurfaceImpl_RealizePalette,
3611 IWineD3DSurfaceImpl_SetColorKey,
3612 IWineD3DSurfaceImpl_GetPitch,
3613 IWineD3DSurfaceImpl_SetMem,
3614 IWineD3DSurfaceImpl_SetOverlayPosition,
3615 IWineD3DSurfaceImpl_GetOverlayPosition,
3616 IWineD3DSurfaceImpl_UpdateOverlayZOrder,
3617 IWineD3DSurfaceImpl_UpdateOverlay,
3618 IWineD3DSurfaceImpl_SetClipper,
3619 IWineD3DSurfaceImpl_GetClipper,
3620 /* Internal use: */
3621 IWineD3DSurfaceImpl_AddDirtyRect,
3622 IWineD3DSurfaceImpl_LoadTexture,
3623 IWineD3DSurfaceImpl_SaveSnapshot,
3624 IWineD3DSurfaceImpl_SetContainer,
3625 IWineD3DSurfaceImpl_SetGlTextureDesc,
3626 IWineD3DSurfaceImpl_GetGlDesc,
3627 IWineD3DSurfaceImpl_GetData,
3628 IWineD3DSurfaceImpl_SetFormat,
3629 IWineD3DSurfaceImpl_PrivateSetup