push b72af2511d67bded8ece08d825ff0eb4a60c20a6
[wine/hacks.git] / dlls / wined3d / surface.c
blobc41e2ebda93f1af154423f26537f4c0d77eeaa3d
1 /*
2 * IWineD3DSurface Implementation
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006 Stefan Dösinger for CodeWeavers
11 * Copyright 2007 Henri Verbeet
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 #include "config.h"
29 #include "wine/port.h"
30 #include "wined3d_private.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
33 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
35 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf);
37 static void surface_download_data(IWineD3DSurfaceImpl *This) {
38 if (!This->resource.allocatedMemory) This->resource.allocatedMemory = HeapAlloc(GetProcessHeap(), 0, This->resource.size + 4);
39 if (This->resource.format == WINED3DFMT_DXT1 ||
40 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
41 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
42 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
43 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
44 } else {
45 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
46 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
48 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
49 checkGLcall("glGetCompressedTexImageARB()");
51 } else {
52 void *mem;
53 int src_pitch = 0;
54 int dst_pitch = 0;
56 if(This->Flags & SFLAG_CONVERTED) {
57 FIXME("Read back converted textures unsupported\n");
58 return;
61 if (This->Flags & SFLAG_NONPOW2) {
62 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
63 src_pitch = This->bytesPerPixel * This->pow2Width;
64 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
65 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
66 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
67 } else {
68 mem = This->resource.allocatedMemory;
71 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
72 This->glDescription.glFormat, This->glDescription.glType, mem);
74 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
75 This->glDescription.glType, mem);
76 checkGLcall("glGetTexImage()");
78 if (This->Flags & SFLAG_NONPOW2) {
79 LPBYTE src_data, dst_data;
80 int y;
82 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
83 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
84 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
86 * We're doing this...
88 * instead of boxing the texture :
89 * |<-texture width ->| -->pow2width| /\
90 * |111111111111111111| | |
91 * |222 Texture 222222| boxed empty | texture height
92 * |3333 Data 33333333| | |
93 * |444444444444444444| | \/
94 * ----------------------------------- |
95 * | boxed empty | boxed empty | pow2height
96 * | | | \/
97 * -----------------------------------
100 * we're repacking the data to the expected texture width
102 * |<-texture width ->| -->pow2width| /\
103 * |111111111111111111222222222222222| |
104 * |222333333333333333333444444444444| texture height
105 * |444444 | |
106 * | | \/
107 * | | |
108 * | empty | pow2height
109 * | | \/
110 * -----------------------------------
112 * == is the same as
114 * |<-texture width ->| /\
115 * |111111111111111111|
116 * |222222222222222222|texture height
117 * |333333333333333333|
118 * |444444444444444444| \/
119 * --------------------
121 * this also means that any references to allocatedMemory should work with the data as if were a
122 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
124 * internally the texture is still stored in a boxed format so any references to textureName will
125 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
127 * Performance should not be an issue, because applications normally do not lock the surfaces when
128 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
129 * and doesn't have to be re-read.
131 src_data = mem;
132 dst_data = This->resource.allocatedMemory;
133 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
134 for (y = 1 ; y < This->currentDesc.Height; y++) {
135 /* skip the first row */
136 src_data += src_pitch;
137 dst_data += dst_pitch;
138 memcpy(dst_data, src_data, dst_pitch);
141 HeapFree(GetProcessHeap(), 0, mem);
144 /* Surface has now been downloaded */
145 This->Flags |= SFLAG_INSYSMEM;
148 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
149 if (This->resource.format == WINED3DFMT_DXT1 ||
150 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
151 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
152 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
153 FIXME("Using DXT1/3/5 without advertized support\n");
154 } else {
155 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
156 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
157 This->Flags |= SFLAG_CLIENT;
160 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
161 ENTER_GL();
162 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
163 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
164 * function uses glCompressedTexImage2D instead of the SubImage call
166 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
167 width, height, 0 /* border */, This->resource.size, data));
168 checkGLcall("glCompressedTexSubImage2D");
169 LEAVE_GL();
171 } else {
172 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
173 ENTER_GL();
174 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
175 checkGLcall("glTexSubImage2D");
176 LEAVE_GL();
180 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
181 BOOL enable_client_storage = FALSE;
183 TRACE("(%p) : Creating surface (target %#x) level %d, d3d format %s, internal format %#x, width %d, height %d, gl format %#x, gl type=%#x\n", This,
184 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
186 if (This->resource.format == WINED3DFMT_DXT1 ||
187 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
188 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
189 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
190 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
191 return;
194 ENTER_GL();
196 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
197 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
198 /* In some cases we want to disable client storage.
199 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
200 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
201 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
202 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
203 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
205 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
206 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
207 This->Flags &= SFLAG_CLIENT;
208 enable_client_storage = TRUE;
209 } else {
210 This->Flags |= SFLAG_CLIENT;
211 /* Below point opengl to our allocated texture memory */
214 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type,
215 This->Flags & SFLAG_CLIENT ? This->resource.allocatedMemory : NULL);
216 checkGLcall("glTexImage2D");
218 if(enable_client_storage) {
219 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
220 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
222 LEAVE_GL();
224 This->Flags |= SFLAG_ALLOCATED;
227 /* In D3D the depth stencil dimensions have to be greater than or equal to the
228 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
229 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
230 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
231 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
232 renderbuffer_entry_t *entry;
233 GLuint renderbuffer = 0;
234 unsigned int src_width, src_height;
236 src_width = This->pow2Width;
237 src_height = This->pow2Height;
239 /* A depth stencil smaller than the render target is not valid */
240 if (width > src_width || height > src_height) return;
242 /* Remove any renderbuffer set if the sizes match */
243 if (width == src_width && height == src_height) {
244 This->current_renderbuffer = NULL;
245 return;
248 /* Look if we've already got a renderbuffer of the correct dimensions */
249 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
250 if (entry->width == width && entry->height == height) {
251 renderbuffer = entry->id;
252 This->current_renderbuffer = entry;
253 break;
257 if (!renderbuffer) {
258 const GlPixelFormatDesc *glDesc;
259 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
261 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
262 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
263 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, glDesc->glFormat, width, height));
265 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
266 entry->width = width;
267 entry->height = height;
268 entry->id = renderbuffer;
269 list_add_head(&This->renderbuffers, &entry->entry);
271 This->current_renderbuffer = entry;
274 checkGLcall("set_compatible_renderbuffer");
277 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
278 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
279 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
281 TRACE("(%p) : swapchain %p\n", This, swapchain);
283 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
284 TRACE("Returning GL_BACK\n");
285 return GL_BACK;
286 } else if (swapchain_impl->frontBuffer == iface) {
287 TRACE("Returning GL_FRONT\n");
288 return GL_FRONT;
291 FIXME("Higher back buffer, returning GL_BACK\n");
292 return GL_BACK;
295 /* *******************************************
296 IWineD3DSurface IUnknown parts follow
297 ******************************************* */
298 HRESULT WINAPI IWineD3DSurfaceImpl_QueryInterface(IWineD3DSurface *iface, REFIID riid, LPVOID *ppobj)
300 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
301 /* Warn ,but be nice about things */
302 TRACE("(%p)->(%s,%p)\n", This,debugstr_guid(riid),ppobj);
304 if (IsEqualGUID(riid, &IID_IUnknown)
305 || IsEqualGUID(riid, &IID_IWineD3DBase)
306 || IsEqualGUID(riid, &IID_IWineD3DResource)
307 || IsEqualGUID(riid, &IID_IWineD3DSurface)) {
308 IUnknown_AddRef((IUnknown*)iface);
309 *ppobj = This;
310 return S_OK;
312 *ppobj = NULL;
313 return E_NOINTERFACE;
316 ULONG WINAPI IWineD3DSurfaceImpl_AddRef(IWineD3DSurface *iface) {
317 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
318 ULONG ref = InterlockedIncrement(&This->resource.ref);
319 TRACE("(%p) : AddRef increasing from %d\n", This,ref - 1);
320 return ref;
323 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
324 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
325 ULONG ref = InterlockedDecrement(&This->resource.ref);
326 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
327 if (ref == 0) {
328 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) This->resource.wineD3DDevice;
329 renderbuffer_entry_t *entry, *entry2;
330 TRACE("(%p) : cleaning up\n", This);
332 if(iface == device->lastActiveRenderTarget) {
333 IWineD3DSwapChainImpl *swapchain = device->swapchains ? (IWineD3DSwapChainImpl *) device->swapchains[0] : NULL;
335 TRACE("Last active render target destroyed\n");
336 /* Find a replacement surface for the currently active back buffer. The context manager does not do NULL
337 * checks, so switch to a valid target as long as the currently set surface is still valid. Use the
338 * surface of the implicit swpchain. If that is the same as the destroyed surface the device is destroyed
339 * and the lastActiveRenderTarget member shouldn't matter
341 if(swapchain) {
342 if(swapchain->backBuffer && swapchain->backBuffer[0] != iface) {
343 TRACE("Activating primary back buffer\n");
344 ActivateContext(device, swapchain->backBuffer[0], CTXUSAGE_RESOURCELOAD);
345 } else if(!swapchain->backBuffer && swapchain->frontBuffer != iface) {
346 /* Single buffering environment */
347 TRACE("Activating primary front buffer\n");
348 ActivateContext(device, swapchain->frontBuffer, CTXUSAGE_RESOURCELOAD);
349 } else {
350 TRACE("Device is being destroyed, setting lastActiveRenderTarget = 0xdeadbabe\n");
351 /* Implicit render target destroyed, that means the device is being destroyed
352 * whatever we set here, it shouldn't matter
354 device->lastActiveRenderTarget = (IWineD3DSurface *) 0xdeadbabe;
356 } else {
357 /* May happen during ddraw uninitialization */
358 TRACE("Render target set, but swapchain does not exist!\n");
359 device->lastActiveRenderTarget = (IWineD3DSurface *) 0xdeadcafe;
363 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
365 /* Need a context to destroy the texture. Use the currently active render target, but only if
366 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
367 * When destroying the primary rt, Uninit3D will activate a context before doing anything
369 if(device->render_targets && device->render_targets[0]) {
370 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
373 TRACE("Deleting texture %d\n", This->glDescription.textureName);
374 ENTER_GL();
375 glDeleteTextures(1, &This->glDescription.textureName);
376 LEAVE_GL();
379 if(This->Flags & SFLAG_DIBSECTION) {
380 /* Release the DC */
381 SelectObject(This->hDC, This->dib.holdbitmap);
382 DeleteDC(This->hDC);
383 /* Release the DIB section */
384 DeleteObject(This->dib.DIBsection);
385 This->dib.bitmap_data = NULL;
386 This->resource.allocatedMemory = NULL;
388 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
390 HeapFree(GetProcessHeap(), 0, This->palette9);
392 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
393 if(iface == device->ddraw_primary)
394 device->ddraw_primary = NULL;
396 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
397 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
398 HeapFree(GetProcessHeap(), 0, entry);
401 TRACE("(%p) Released\n", This);
402 HeapFree(GetProcessHeap(), 0, This);
405 return ref;
408 /* ****************************************************
409 IWineD3DSurface IWineD3DResource parts follow
410 **************************************************** */
411 HRESULT WINAPI IWineD3DSurfaceImpl_GetDevice(IWineD3DSurface *iface, IWineD3DDevice** ppDevice) {
412 return IWineD3DResourceImpl_GetDevice((IWineD3DResource *)iface, ppDevice);
415 HRESULT WINAPI IWineD3DSurfaceImpl_SetPrivateData(IWineD3DSurface *iface, REFGUID refguid, CONST void* pData, DWORD SizeOfData, DWORD Flags) {
416 return IWineD3DResourceImpl_SetPrivateData((IWineD3DResource *)iface, refguid, pData, SizeOfData, Flags);
419 HRESULT WINAPI IWineD3DSurfaceImpl_GetPrivateData(IWineD3DSurface *iface, REFGUID refguid, void* pData, DWORD* pSizeOfData) {
420 return IWineD3DResourceImpl_GetPrivateData((IWineD3DResource *)iface, refguid, pData, pSizeOfData);
423 HRESULT WINAPI IWineD3DSurfaceImpl_FreePrivateData(IWineD3DSurface *iface, REFGUID refguid) {
424 return IWineD3DResourceImpl_FreePrivateData((IWineD3DResource *)iface, refguid);
427 DWORD WINAPI IWineD3DSurfaceImpl_SetPriority(IWineD3DSurface *iface, DWORD PriorityNew) {
428 return IWineD3DResourceImpl_SetPriority((IWineD3DResource *)iface, PriorityNew);
431 DWORD WINAPI IWineD3DSurfaceImpl_GetPriority(IWineD3DSurface *iface) {
432 return IWineD3DResourceImpl_GetPriority((IWineD3DResource *)iface);
435 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
436 /* TODO: check for locks */
437 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
438 IWineD3DBaseTexture *baseTexture = NULL;
439 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
441 TRACE("(%p)Checking to see if the container is a base texture\n", This);
442 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
443 TRACE("Passing to container\n");
444 IWineD3DBaseTexture_PreLoad(baseTexture);
445 IWineD3DBaseTexture_Release(baseTexture);
446 } else {
447 TRACE("(%p) : About to load surface\n", This);
449 if(!device->isInDraw) {
450 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
453 ENTER_GL();
454 glEnable(This->glDescription.target);/* make sure texture support is enabled in this context */
455 if (!This->glDescription.level) {
456 if (!This->glDescription.textureName) {
457 glGenTextures(1, &This->glDescription.textureName);
458 checkGLcall("glGenTextures");
459 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
461 glBindTexture(This->glDescription.target, This->glDescription.textureName);
462 checkGLcall("glBindTexture");
463 IWineD3DSurface_LoadTexture(iface, FALSE);
464 /* This is where we should be reducing the amount of GLMemoryUsed */
465 } else if (This->glDescription.textureName) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
466 /* assume this is a coding error not a real error for now */
467 FIXME("Mipmap surface has a glTexture bound to it!\n");
469 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
470 /* Tell opengl to try and keep this texture in video ram (well mostly) */
471 GLclampf tmp;
472 tmp = 0.9f;
473 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
475 LEAVE_GL();
477 return;
480 WINED3DRESOURCETYPE WINAPI IWineD3DSurfaceImpl_GetType(IWineD3DSurface *iface) {
481 TRACE("(%p) : calling resourceimpl_GetType\n", iface);
482 return IWineD3DResourceImpl_GetType((IWineD3DResource *)iface);
485 HRESULT WINAPI IWineD3DSurfaceImpl_GetParent(IWineD3DSurface *iface, IUnknown **pParent) {
486 TRACE("(%p) : calling resourceimpl_GetParent\n", iface);
487 return IWineD3DResourceImpl_GetParent((IWineD3DResource *)iface, pParent);
490 /* ******************************************************
491 IWineD3DSurface IWineD3DSurface parts follow
492 ****************************************************** */
494 HRESULT WINAPI IWineD3DSurfaceImpl_GetContainer(IWineD3DSurface* iface, REFIID riid, void** ppContainer) {
495 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
496 IWineD3DBase *container = 0;
498 TRACE("(This %p, riid %s, ppContainer %p)\n", This, debugstr_guid(riid), ppContainer);
500 if (!ppContainer) {
501 ERR("Called without a valid ppContainer.\n");
504 /** From MSDN:
505 * If the surface is created using CreateImageSurface/CreateOffscreenPlainSurface, CreateRenderTarget,
506 * or CreateDepthStencilSurface, the surface is considered stand alone. In this case,
507 * GetContainer will return the Direct3D device used to create the surface.
509 if (This->container) {
510 container = This->container;
511 } else {
512 container = (IWineD3DBase *)This->resource.wineD3DDevice;
515 TRACE("Relaying to QueryInterface\n");
516 return IUnknown_QueryInterface(container, riid, ppContainer);
519 HRESULT WINAPI IWineD3DSurfaceImpl_GetDesc(IWineD3DSurface *iface, WINED3DSURFACE_DESC *pDesc) {
520 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
522 TRACE("(%p) : copying into %p\n", This, pDesc);
523 if(pDesc->Format != NULL) *(pDesc->Format) = This->resource.format;
524 if(pDesc->Type != NULL) *(pDesc->Type) = This->resource.resourceType;
525 if(pDesc->Usage != NULL) *(pDesc->Usage) = This->resource.usage;
526 if(pDesc->Pool != NULL) *(pDesc->Pool) = This->resource.pool;
527 if(pDesc->Size != NULL) *(pDesc->Size) = This->resource.size; /* dx8 only */
528 if(pDesc->MultiSampleType != NULL) *(pDesc->MultiSampleType) = This->currentDesc.MultiSampleType;
529 if(pDesc->MultiSampleQuality != NULL) *(pDesc->MultiSampleQuality) = This->currentDesc.MultiSampleQuality;
530 if(pDesc->Width != NULL) *(pDesc->Width) = This->currentDesc.Width;
531 if(pDesc->Height != NULL) *(pDesc->Height) = This->currentDesc.Height;
532 return WINED3D_OK;
535 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
536 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
537 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
538 if (This->glDescription.textureName == 0 && textureName != 0) {
539 This->Flags &= ~SFLAG_INTEXTURE;
540 IWineD3DSurface_AddDirtyRect(iface, NULL);
542 This->glDescription.textureName = textureName;
543 This->glDescription.target = target;
544 This->Flags &= ~SFLAG_ALLOCATED;
547 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
548 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
549 TRACE("(%p) : returning %p\n", This, &This->glDescription);
550 *glDescription = &This->glDescription;
553 /* TODO: think about moving this down to resource? */
554 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
555 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
556 /* This should only be called for sysmem textures, it may be a good idea to extend this to all pools at some point in the future */
557 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
558 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
560 return (CONST void*)(This->resource.allocatedMemory);
563 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch, BOOL srcUpsideDown) {
564 BYTE *mem;
565 GLint fmt;
566 GLint type;
567 BYTE *row, *top, *bottom;
568 int i;
569 BOOL bpp;
571 switch(This->resource.format)
573 case WINED3DFMT_P8:
575 /* GL can't return palettized data, so read ARGB pixels into a
576 * separate block of memory and convert them into palettized format
577 * in software. Slow, but if the app means to use palettized render
578 * targets and locks it...
580 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
581 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
582 * for the color channels when palettizing the colors.
584 fmt = GL_RGB;
585 type = GL_UNSIGNED_BYTE;
586 pitch *= 3;
587 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
588 if(!mem) {
589 ERR("Out of memory\n");
590 return;
592 bpp = This->bytesPerPixel * 3;
594 break;
596 default:
597 mem = dest;
598 fmt = This->glDescription.glFormat;
599 type = This->glDescription.glType;
600 bpp = This->bytesPerPixel;
603 glReadPixels(rect->left, rect->top,
604 rect->right - rect->left,
605 rect->bottom - rect->top,
606 fmt, type, mem);
607 vcheckGLcall("glReadPixels");
609 /* TODO: Merge this with the palettization loop below for P8 targets */
611 if(!srcUpsideDown) {
612 UINT len, off;
613 /* glReadPixels returns the image upside down, and there is no way to prevent this.
614 Flip the lines in software */
615 len = (rect->right - rect->left) * bpp;
616 off = rect->left * bpp;
618 row = HeapAlloc(GetProcessHeap(), 0, len);
619 if(!row) {
620 ERR("Out of memory\n");
621 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
622 return;
625 top = mem + pitch * rect->top;
626 bottom = ((BYTE *) mem) + pitch * ( rect->bottom - rect->top - 1);
627 for(i = 0; i < (rect->bottom - rect->top) / 2; i++) {
628 memcpy(row, top + off, len);
629 memcpy(top + off, bottom + off, len);
630 memcpy(bottom + off, row, len);
631 top += pitch;
632 bottom -= pitch;
634 HeapFree(GetProcessHeap(), 0, row);
637 if(This->resource.format == WINED3DFMT_P8) {
638 PALETTEENTRY *pal;
639 DWORD width = pitch / 3;
640 int x, y, c;
641 if(This->palette) {
642 pal = This->palette->palents;
643 } else {
644 pal = This->resource.wineD3DDevice->palettes[This->resource.wineD3DDevice->currentPalette];
647 for(y = rect->top; y < rect->bottom; y++) {
648 for(x = rect->left; x < rect->right; x++) {
649 /* start lines pixels */
650 BYTE *blue = (BYTE *) ((BYTE *) mem) + y * pitch + x * (sizeof(BYTE) * 3);
651 BYTE *green = blue + 1;
652 BYTE *red = green + 1;
654 for(c = 0; c < 256; c++) {
655 if(*red == pal[c].peRed &&
656 *green == pal[c].peGreen &&
657 *blue == pal[c].peBlue)
659 *((BYTE *) dest + y * width + x) = c;
660 break;
665 HeapFree(GetProcessHeap(), 0, mem);
669 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
670 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
671 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
672 IWineD3DSwapChainImpl *swapchain = NULL;
674 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
676 if (This->Flags & SFLAG_LOCKED) {
677 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
678 return WINED3DERR_INVALIDCALL;
680 if (!(This->Flags & SFLAG_LOCKABLE)) {
681 /* Note: UpdateTextures calls CopyRects which calls this routine to populate the
682 texture regions, and since the destination is an unlockable region we need
683 to tolerate this */
684 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
685 /*return WINED3DERR_INVALIDCALL; */
688 pLockedRect->Pitch = IWineD3DSurface_GetPitch(iface);
690 /* Mark the surface locked */
691 This->Flags |= SFLAG_LOCKED;
693 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy */
694 if(!This->resource.allocatedMemory) {
695 This->resource.allocatedMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + 4);
696 This->Flags &= ~SFLAG_INSYSMEM; /* This is the marker that surface data has to be downloaded */
699 /* Calculate the dimensions of the locked rect */
700 if (NULL == pRect) {
701 This->lockedRect.left = 0;
702 This->lockedRect.top = 0;
703 This->lockedRect.right = This->currentDesc.Width;
704 This->lockedRect.bottom = This->currentDesc.Height;
705 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);
706 } else {
707 This->lockedRect.left = pRect->left;
708 This->lockedRect.top = pRect->top;
709 This->lockedRect.right = pRect->right;
710 This->lockedRect.bottom = pRect->bottom;
711 TRACE("Locked Rect (%p) = l %d, t %d, r %d, b %d\n", pRect, pRect->left, pRect->top, pRect->right, pRect->bottom);
714 if (This->Flags & SFLAG_NONPOW2) {
715 TRACE("Locking non-power 2 texture\n");
718 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
719 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
720 * changed
722 if(!(This->Flags & SFLAG_DYNLOCK)) {
723 This->lockCount++;
724 /* MAXLOCKCOUNT is defined in wined3d_private.h */
725 if(This->lockCount > MAXLOCKCOUNT) {
726 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
727 This->Flags |= SFLAG_DYNLOCK;
731 if (Flags & WINED3DLOCK_DISCARD) {
732 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
733 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
734 This->Flags |= SFLAG_INSYSMEM;
737 if (This->Flags & SFLAG_INSYSMEM) {
738 TRACE("Local copy is up to date, not downloading data\n");
739 goto lock_end;
742 /* Now download the surface content from opengl
743 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
744 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
746 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
747 if(swapchain || iface == myDevice->render_targets[0]) {
748 BOOL srcIsUpsideDown;
750 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
751 static BOOL warned = FALSE;
752 if(!warned) {
753 ERR("The application tries to lock the render target, but render target locking is disabled\n");
754 warned = TRUE;
756 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
757 return WINED3D_OK;
760 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
761 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
762 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
763 * context->last_was_blit set on the unlock.
765 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
766 ENTER_GL();
768 /* Select the correct read buffer, and give some debug output.
769 * There is no need to keep track of the current read buffer or reset it, every part of the code
770 * that reads sets the read buffer as desired.
772 if(!swapchain) {
773 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
774 * Read from the back buffer
776 TRACE("Locking offscreen render target\n");
777 glReadBuffer(myDevice->offscreenBuffer);
778 srcIsUpsideDown = TRUE;
779 } else {
780 GLenum buffer = surface_get_gl_buffer(iface, (IWineD3DSwapChain *)swapchain);
781 TRACE("Locking %#x buffer\n", buffer);
782 glReadBuffer(buffer);
783 checkGLcall("glReadBuffer");
785 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
786 srcIsUpsideDown = FALSE;
789 switch(wined3d_settings.rendertargetlock_mode) {
790 case RTL_AUTO:
791 case RTL_READDRAW:
792 case RTL_READTEX:
793 read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
794 break;
796 case RTL_TEXDRAW:
797 case RTL_TEXTEX:
798 read_from_framebuffer(This, &This->lockedRect, This->resource.allocatedMemory, pLockedRect->Pitch, srcIsUpsideDown);
799 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
800 break;
802 LEAVE_GL();
804 /* Mark the local copy up to date if a full download was done */
805 if(This->lockedRect.left == 0 &&
806 This->lockedRect.top == 0 &&
807 This->lockedRect.right == This->currentDesc.Width &&
808 This->lockedRect.bottom == This->currentDesc.Height) {
809 This->Flags |= SFLAG_INSYSMEM;
811 } else if(iface == myDevice->stencilBufferTarget) {
812 /** the depth stencil in openGL has a format of GL_FLOAT
813 * which should be good for WINED3DFMT_D16_LOCKABLE
814 * and WINED3DFMT_D16
815 * it is unclear what format the stencil buffer is in except.
816 * 'Each index is converted to fixed point...
817 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
818 * mappings in the table GL_PIXEL_MAP_S_TO_S.
819 * glReadPixels(This->lockedRect.left,
820 * This->lockedRect.bottom - j - 1,
821 * This->lockedRect.right - This->lockedRect.left,
822 * 1,
823 * GL_DEPTH_COMPONENT,
824 * type,
825 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
827 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
828 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
829 * none of that is the case the problem is not in this function :-)
830 ********************************************/
831 FIXME("Depth stencil locking not supported yet\n");
832 } else {
833 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
834 TRACE("locking an ordinary surface\n");
836 if (0 != This->glDescription.textureName) {
837 /* Now I have to copy thing bits back */
839 if(myDevice->createParms.BehaviorFlags & WINED3DCREATE_MULTITHREADED) {
840 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
843 ENTER_GL();
844 /* Make sure that a proper texture unit is selected, bind the texture and dirtify the sampler to restore the texture on the next draw */
845 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
846 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
847 checkGLcall("glActiveTextureARB");
849 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(0));
850 IWineD3DSurface_PreLoad(iface);
852 surface_download_data(This);
853 LEAVE_GL();
857 lock_end:
858 /* Calculate the correct start address to report */
859 if (NULL == pRect) {
860 pLockedRect->pBits = This->resource.allocatedMemory;
861 } else {
862 /* DXTn textures are based on compressed blocks of 4x4 pixels, each
863 * 16 bytes large (8 bytes in case of DXT1). Because of that Pitch has
864 * slightly different meaning compared to regular textures. For DXTn
865 * textures Pitch is the size of a row of blocks, 4 high and "width"
866 * long. The x offset is calculated differently as well, since moving 4
867 * pixels to the right actually moves an entire 4x4 block to right, ie
868 * 16 bytes (8 in case of DXT1). */
869 if (This->resource.format == WINED3DFMT_DXT1) {
870 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top / 4) + (pRect->left * 2);
871 } else if (This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3
872 || This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
873 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top / 4) + (pRect->left * 4);
874 } else {
875 pLockedRect->pBits = This->resource.allocatedMemory + (pLockedRect->Pitch * pRect->top) + (pRect->left * This->bytesPerPixel);
879 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
880 /* Don't dirtify */
881 } else {
882 IWineD3DBaseTexture *pBaseTexture;
884 * Dirtify on lock
885 * as seen in msdn docs
887 IWineD3DSurface_AddDirtyRect(iface, &This->lockedRect);
889 /** Dirtify Container if needed */
890 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
891 TRACE("Making container dirty\n");
892 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
893 IWineD3DBaseTexture_Release(pBaseTexture);
894 } else {
895 TRACE("Surface is standalone, no need to dirty the container\n");
899 TRACE("returning memory@%p, pitch(%d) dirtyfied(%d)\n", pLockedRect->pBits, pLockedRect->Pitch,
900 This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
901 return WINED3D_OK;
904 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This) {
905 GLint prev_store;
906 GLint prev_rasterpos[4];
907 GLint skipBytes = 0;
908 BOOL storechanged = FALSE, memory_allocated = FALSE;
909 GLint fmt, type;
910 BYTE *mem;
911 UINT bpp;
912 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
914 glDisable(GL_TEXTURE_2D);
915 vcheckGLcall("glDisable(GL_TEXTURE_2D)");
917 glFlush();
918 vcheckGLcall("glFlush");
919 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
920 vcheckGLcall("glIntegerv");
921 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
922 vcheckGLcall("glIntegerv");
923 glPixelZoom(1.0, -1.0);
924 vcheckGLcall("glPixelZoom");
926 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
927 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
928 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
930 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
931 vcheckGLcall("glRasterPos2f");
933 /* Some drivers(radeon dri, others?) don't like exceptions during
934 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
935 * after ReleaseDC. Reading it will cause an exception, which x11drv will
936 * catch to put the dib section in InSync mode, which leads to a crash
937 * and a blocked x server on my radeon card.
939 * The following lines read the dib section so it is put in inSync mode
940 * before glDrawPixels is called and the crash is prevented. There won't
941 * be any interfering gdi accesses, because UnlockRect is called from
942 * ReleaseDC, and the app won't use the dc any more afterwards.
944 if(This->Flags & SFLAG_DIBSECTION) {
945 volatile BYTE read;
946 read = This->resource.allocatedMemory[0];
949 switch (This->resource.format) {
950 /* No special care needed */
951 case WINED3DFMT_A4R4G4B4:
952 case WINED3DFMT_R5G6B5:
953 case WINED3DFMT_A1R5G5B5:
954 case WINED3DFMT_R8G8B8:
955 type = This->glDescription.glType;
956 fmt = This->glDescription.glFormat;
957 mem = This->resource.allocatedMemory;
958 bpp = This->bytesPerPixel;
959 break;
961 case WINED3DFMT_X4R4G4B4:
963 int size;
964 unsigned short *data;
965 data = (unsigned short *)This->resource.allocatedMemory;
966 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
967 while(size > 0) {
968 *data |= 0xF000;
969 data++;
970 size--;
972 type = This->glDescription.glType;
973 fmt = This->glDescription.glFormat;
974 mem = This->resource.allocatedMemory;
975 bpp = This->bytesPerPixel;
977 break;
979 case WINED3DFMT_X1R5G5B5:
981 int size;
982 unsigned short *data;
983 data = (unsigned short *)This->resource.allocatedMemory;
984 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
985 while(size > 0) {
986 *data |= 0x8000;
987 data++;
988 size--;
990 type = This->glDescription.glType;
991 fmt = This->glDescription.glFormat;
992 mem = This->resource.allocatedMemory;
993 bpp = This->bytesPerPixel;
995 break;
997 case WINED3DFMT_X8R8G8B8:
999 /* make sure the X byte is set to alpha on, since it
1000 could be any random value. This fixes the intro movie in Pirates! */
1001 int size;
1002 unsigned int *data;
1003 data = (unsigned int *)This->resource.allocatedMemory;
1004 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
1005 while(size > 0) {
1006 *data |= 0xFF000000;
1007 data++;
1008 size--;
1011 /* Fall through */
1013 case WINED3DFMT_A8R8G8B8:
1015 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
1016 vcheckGLcall("glPixelStorei");
1017 storechanged = TRUE;
1018 type = This->glDescription.glType;
1019 fmt = This->glDescription.glFormat;
1020 mem = This->resource.allocatedMemory;
1021 bpp = This->bytesPerPixel;
1023 break;
1025 case WINED3DFMT_A2R10G10B10:
1027 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
1028 vcheckGLcall("glPixelStorei");
1029 storechanged = TRUE;
1030 type = This->glDescription.glType;
1031 fmt = This->glDescription.glFormat;
1032 mem = This->resource.allocatedMemory;
1033 bpp = This->bytesPerPixel;
1035 break;
1037 case WINED3DFMT_P8:
1039 int height = This->glRect.bottom - This->glRect.top;
1040 type = GL_UNSIGNED_BYTE;
1041 fmt = GL_RGBA;
1043 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * sizeof(DWORD));
1044 if(!mem) {
1045 ERR("Out of memory\n");
1046 return;
1048 memory_allocated = TRUE;
1049 d3dfmt_convert_surface(This->resource.allocatedMemory,
1050 mem,
1051 pitch,
1052 pitch,
1053 height,
1054 pitch * 4,
1055 CONVERT_PALETTED,
1056 This);
1057 bpp = This->bytesPerPixel * 4;
1058 pitch *= 4;
1060 break;
1062 default:
1063 FIXME("Unsupported Format %u in locking func\n", This->resource.format);
1065 /* Give it a try */
1066 type = This->glDescription.glType;
1067 fmt = This->glDescription.glFormat;
1068 mem = This->resource.allocatedMemory;
1069 bpp = This->bytesPerPixel;
1072 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1073 (This->lockedRect.bottom - This->lockedRect.top)-1,
1074 fmt, type,
1075 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1076 checkGLcall("glDrawPixels");
1077 glPixelZoom(1.0,1.0);
1078 vcheckGLcall("glPixelZoom");
1080 glRasterPos3iv(&prev_rasterpos[0]);
1081 vcheckGLcall("glRasterPos3iv");
1083 /* Reset to previous pack row length */
1084 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1085 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1086 if(storechanged) {
1087 glPixelStorei(GL_PACK_SWAP_BYTES, prev_store);
1088 vcheckGLcall("glPixelStorei GL_PACK_SWAP_BYTES");
1091 /* Blitting environment requires that 2D texturing is enabled. It was turned off before,
1092 * turn it on again
1094 glEnable(GL_TEXTURE_2D);
1095 checkGLcall("glEnable(GL_TEXTURE_2D)");
1097 if(memory_allocated) HeapFree(GetProcessHeap(), 0, mem);
1098 return;
1101 static void flush_to_framebuffer_texture(IWineD3DSurfaceImpl *This) {
1102 float glTexCoord[4];
1104 glTexCoord[0] = (float) This->lockedRect.left / (float) This->pow2Width; /* left */
1105 glTexCoord[1] = (float) This->lockedRect.right / (float) This->pow2Width; /* right */
1106 glTexCoord[2] = (float) This->lockedRect.top / (float) This->pow2Height; /* top */
1107 glTexCoord[3] = (float) This->lockedRect.bottom / (float) This->pow2Height; /* bottom */
1109 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
1111 ENTER_GL();
1113 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
1114 checkGLcall("glEnable glBindTexture");
1116 /* No filtering for blts */
1117 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1118 checkGLcall("glTexParameteri");
1119 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1120 checkGLcall("glTexParameteri");
1122 /* Start drawing a quad */
1123 glBegin(GL_QUADS);
1125 glColor3d(1.0f, 1.0f, 1.0f);
1126 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
1127 glVertex3f(This->lockedRect.left, This->lockedRect.top, 0.0);
1129 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
1130 glVertex3f(This->lockedRect.left, This->lockedRect.bottom, 0.0);
1132 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
1133 glVertex3d(This->lockedRect.right, This->lockedRect.bottom, 0.0);
1135 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
1136 glVertex3f(This->lockedRect.right, This->lockedRect.top, 0.0);
1138 glEnd();
1139 checkGLcall("glEnd");
1141 /* Unbind the texture */
1142 glBindTexture(GL_TEXTURE_2D, 0);
1143 checkGLcall("glEnable glBindTexture");
1145 LEAVE_GL();
1148 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1149 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1150 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1151 IWineD3DSwapChainImpl *swapchain = NULL;
1153 if (!(This->Flags & SFLAG_LOCKED)) {
1154 WARN("trying to Unlock an unlocked surf@%p\n", This);
1155 return WINED3DERR_INVALIDCALL;
1158 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1160 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1161 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1162 goto unlock_end;
1165 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1166 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1167 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1168 static BOOL warned = FALSE;
1169 if(!warned) {
1170 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1171 warned = TRUE;
1173 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1174 goto unlock_end;
1177 /* Activate the correct context for the render target */
1178 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1179 ENTER_GL();
1181 if(!swapchain) {
1182 /* Primary offscreen render target */
1183 TRACE("Offscreen render target\n");
1184 glDrawBuffer(myDevice->offscreenBuffer);
1185 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1186 } else {
1187 GLenum buffer = surface_get_gl_buffer(iface, (IWineD3DSwapChain *)swapchain);
1188 TRACE("Unlocking %#x buffer\n", buffer);
1189 glDrawBuffer(buffer);
1190 checkGLcall("glDrawBuffer");
1192 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1195 switch(wined3d_settings.rendertargetlock_mode) {
1196 case RTL_AUTO:
1197 case RTL_READDRAW:
1198 case RTL_TEXDRAW:
1199 flush_to_framebuffer_drawpixels(This);
1200 break;
1202 case RTL_READTEX:
1203 case RTL_TEXTEX:
1204 flush_to_framebuffer_texture(This);
1205 break;
1207 if(!swapchain) {
1208 glDrawBuffer(myDevice->offscreenBuffer);
1209 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1210 } else if(swapchain->backBuffer) {
1211 glDrawBuffer(GL_BACK);
1212 checkGLcall("glDrawBuffer(GL_BACK)");
1213 } else {
1214 glDrawBuffer(GL_FRONT);
1215 checkGLcall("glDrawBuffer(GL_FRONT)");
1217 LEAVE_GL();
1219 This->dirtyRect.left = This->currentDesc.Width;
1220 This->dirtyRect.top = This->currentDesc.Height;
1221 This->dirtyRect.right = 0;
1222 This->dirtyRect.bottom = 0;
1223 This->Flags |= SFLAG_INDRAWABLE;
1224 } else if(iface == myDevice->stencilBufferTarget) {
1225 FIXME("Depth Stencil buffer locking is not implemented\n");
1226 } else {
1227 /* The rest should be a normal texture */
1228 IWineD3DBaseTextureImpl *impl;
1229 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1230 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1231 * states need resetting
1233 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1234 if(impl->baseTexture.bindCount) {
1235 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1237 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1241 unlock_end:
1242 This->Flags &= ~SFLAG_LOCKED;
1243 memset(&This->lockedRect, 0, sizeof(RECT));
1244 return WINED3D_OK;
1247 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1248 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1249 WINED3DLOCKED_RECT lock;
1250 UINT usage;
1251 BITMAPINFO* b_info;
1252 HDC ddc;
1253 DWORD *masks;
1254 HRESULT hr;
1255 RGBQUAD col[256];
1256 const StaticPixelFormatDesc *formatEntry = getFormatDescEntry(This->resource.format, NULL, NULL);
1258 TRACE("(%p)->(%p)\n",This,pHDC);
1260 if(This->Flags & SFLAG_USERPTR) {
1261 ERR("Not supported on surfaces with an application-provided surfaces\n");
1262 return WINEDDERR_NODC;
1265 /* Give more detailed info for ddraw */
1266 if (This->Flags & SFLAG_DCINUSE)
1267 return WINEDDERR_DCALREADYCREATED;
1269 /* Can't GetDC if the surface is locked */
1270 if (This->Flags & SFLAG_LOCKED)
1271 return WINED3DERR_INVALIDCALL;
1273 memset(&lock, 0, sizeof(lock)); /* To be sure */
1275 /* Create a DIB section if there isn't a hdc yet */
1276 if(!This->hDC) {
1277 int extraline = 0;
1278 SYSTEM_INFO sysInfo;
1279 void *oldmem = This->resource.allocatedMemory;
1281 switch (This->bytesPerPixel) {
1282 case 2:
1283 case 4:
1284 /* Allocate extra space to store the RGB bit masks. */
1285 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
1286 break;
1288 case 3:
1289 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
1290 break;
1292 default:
1293 /* Allocate extra space for a palette. */
1294 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1295 sizeof(BITMAPINFOHEADER)
1296 + sizeof(RGBQUAD)
1297 * (1 << (This->bytesPerPixel * 8)));
1298 break;
1301 if (!b_info)
1302 return E_OUTOFMEMORY;
1304 /* Some apps access the surface in via DWORDs, and do not take the necessary care at the end of the
1305 * surface. So we need at least extra 4 bytes at the end of the surface. Check against the page size,
1306 * if the last page used for the surface has at least 4 spare bytes we're safe, otherwise
1307 * add an extra line to the dib section
1309 GetSystemInfo(&sysInfo);
1310 if( ((This->resource.size + 3) % sysInfo.dwPageSize) < 4) {
1311 extraline = 1;
1312 TRACE("Adding an extra line to the dib section\n");
1315 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1316 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
1317 b_info->bmiHeader.biWidth = IWineD3DSurface_GetPitch(iface) / This->bytesPerPixel;
1318 b_info->bmiHeader.biHeight = -This->currentDesc.Height -extraline;
1319 b_info->bmiHeader.biSizeImage = ( This->currentDesc.Height + extraline) * IWineD3DSurface_GetPitch(iface);
1320 b_info->bmiHeader.biPlanes = 1;
1321 b_info->bmiHeader.biBitCount = This->bytesPerPixel * 8;
1323 b_info->bmiHeader.biXPelsPerMeter = 0;
1324 b_info->bmiHeader.biYPelsPerMeter = 0;
1325 b_info->bmiHeader.biClrUsed = 0;
1326 b_info->bmiHeader.biClrImportant = 0;
1328 /* Get the bit masks */
1329 masks = (DWORD *) &(b_info->bmiColors);
1330 switch (This->resource.format) {
1331 case WINED3DFMT_R8G8B8:
1332 usage = DIB_RGB_COLORS;
1333 b_info->bmiHeader.biCompression = BI_RGB;
1334 break;
1336 case WINED3DFMT_X1R5G5B5:
1337 case WINED3DFMT_A1R5G5B5:
1338 case WINED3DFMT_A4R4G4B4:
1339 case WINED3DFMT_X4R4G4B4:
1340 case WINED3DFMT_R3G3B2:
1341 case WINED3DFMT_A8R3G3B2:
1342 case WINED3DFMT_A2B10G10R10:
1343 case WINED3DFMT_A8B8G8R8:
1344 case WINED3DFMT_X8B8G8R8:
1345 case WINED3DFMT_A2R10G10B10:
1346 case WINED3DFMT_R5G6B5:
1347 case WINED3DFMT_A16B16G16R16:
1348 usage = 0;
1349 b_info->bmiHeader.biCompression = BI_BITFIELDS;
1350 masks[0] = formatEntry->redMask;
1351 masks[1] = formatEntry->greenMask;
1352 masks[2] = formatEntry->blueMask;
1353 break;
1355 default:
1356 /* Don't know palette */
1357 b_info->bmiHeader.biCompression = BI_RGB;
1358 usage = 0;
1359 break;
1362 ddc = GetDC(0);
1363 if (ddc == 0) {
1364 HeapFree(GetProcessHeap(), 0, b_info);
1365 return HRESULT_FROM_WIN32(GetLastError());
1368 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);
1369 This->dib.DIBsection = CreateDIBSection(ddc, b_info, usage, &This->dib.bitmap_data, 0 /* Handle */, 0 /* Offset */);
1370 ReleaseDC(0, ddc);
1372 if (!This->dib.DIBsection) {
1373 ERR("CreateDIBSection failed!\n");
1374 HeapFree(GetProcessHeap(), 0, b_info);
1375 return HRESULT_FROM_WIN32(GetLastError());
1378 TRACE("DIBSection at : %p\n", This->dib.bitmap_data);
1380 /* copy the existing surface to the dib section */
1381 if(This->resource.allocatedMemory) {
1382 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, b_info->bmiHeader.biSizeImage);
1383 /* We won't need that any more */
1384 } else {
1385 /* This is to make LockRect read the gl Texture although memory is allocated */
1386 This->Flags &= ~SFLAG_INSYSMEM;
1389 HeapFree(GetProcessHeap(), 0, b_info);
1391 /* Use the dib section from now on */
1392 This->resource.allocatedMemory = This->dib.bitmap_data;
1394 /* Now allocate a HDC */
1395 This->hDC = CreateCompatibleDC(0);
1396 This->dib.holdbitmap = SelectObject(This->hDC, This->dib.DIBsection);
1397 TRACE("using wined3d palette %p\n", This->palette);
1398 SelectPalette(This->hDC,
1399 This->palette ? This->palette->hpal : 0,
1400 FALSE);
1402 This->Flags |= SFLAG_DIBSECTION;
1404 if(This->Flags & SFLAG_CLIENT) {
1405 IWineD3DSurface_PreLoad(iface);
1407 HeapFree(GetProcessHeap(), 0, oldmem);
1410 /* Lock the surface */
1411 hr = IWineD3DSurface_LockRect(iface,
1412 &lock,
1413 NULL,
1415 if(FAILED(hr)) {
1416 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1417 /* keep the dib section */
1418 return hr;
1421 if(This->resource.format == WINED3DFMT_P8 ||
1422 This->resource.format == WINED3DFMT_A8P8) {
1423 unsigned int n;
1424 if(This->palette) {
1425 PALETTEENTRY ent[256];
1427 GetPaletteEntries(This->palette->hpal, 0, 256, ent);
1428 for (n=0; n<256; n++) {
1429 col[n].rgbRed = ent[n].peRed;
1430 col[n].rgbGreen = ent[n].peGreen;
1431 col[n].rgbBlue = ent[n].peBlue;
1432 col[n].rgbReserved = 0;
1434 } else {
1435 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1437 for (n=0; n<256; n++) {
1438 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
1439 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
1440 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
1441 col[n].rgbReserved = 0;
1445 SetDIBColorTable(This->hDC, 0, 256, col);
1448 *pHDC = This->hDC;
1449 TRACE("returning %p\n",*pHDC);
1450 This->Flags |= SFLAG_DCINUSE;
1452 return WINED3D_OK;
1455 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1456 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1458 TRACE("(%p)->(%p)\n",This,hDC);
1460 if (!(This->Flags & SFLAG_DCINUSE))
1461 return WINED3DERR_INVALIDCALL;
1463 if (This->hDC !=hDC) {
1464 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1465 return WINED3DERR_INVALIDCALL;
1468 /* we locked first, so unlock now */
1469 IWineD3DSurface_UnlockRect(iface);
1471 This->Flags &= ~SFLAG_DCINUSE;
1473 return WINED3D_OK;
1476 /* ******************************************************
1477 IWineD3DSurface Internal (No mapping to directx api) parts follow
1478 ****************************************************** */
1480 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) {
1481 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1482 const GlPixelFormatDesc *glDesc;
1483 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1485 /* Default values: From the surface */
1486 *format = glDesc->glFormat;
1487 *internal = srgb_mode?glDesc->glGammaInternal:glDesc->glInternal;
1488 *type = glDesc->glType;
1489 *convert = NO_CONVERSION;
1490 *target_bpp = This->bytesPerPixel;
1492 /* Ok, now look if we have to do any conversion */
1493 switch(This->resource.format) {
1494 case WINED3DFMT_P8:
1495 /* ****************
1496 Paletted Texture
1497 **************** */
1498 /* Use conversion when the paletted texture extension is not available, or when it is available make sure it is used
1499 * for texturing as it won't work for calls like glDraw-/glReadPixels and further also use conversion in case of color keying.
1501 if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) || colorkey_active || (!use_texturing && GL_SUPPORT(EXT_PALETTED_TEXTURE)) ) {
1502 *format = GL_RGBA;
1503 *internal = GL_RGBA;
1504 *type = GL_UNSIGNED_BYTE;
1505 *target_bpp = 4;
1506 if(colorkey_active) {
1507 *convert = CONVERT_PALETTED_CK;
1508 } else {
1509 *convert = CONVERT_PALETTED;
1513 break;
1515 case WINED3DFMT_R3G3B2:
1516 /* **********************
1517 GL_UNSIGNED_BYTE_3_3_2
1518 ********************** */
1519 if (colorkey_active) {
1520 /* This texture format will never be used.. So do not care about color keying
1521 up until the point in time it will be needed :-) */
1522 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1524 break;
1526 case WINED3DFMT_R5G6B5:
1527 if (colorkey_active) {
1528 *convert = CONVERT_CK_565;
1529 *format = GL_RGBA;
1530 *internal = GL_RGBA;
1531 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1533 break;
1535 case WINED3DFMT_X1R5G5B5:
1536 if (colorkey_active) {
1537 *convert = CONVERT_CK_5551;
1538 *format = GL_BGRA;
1539 *internal = GL_RGBA;
1540 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1542 break;
1544 case WINED3DFMT_R8G8B8:
1545 if (colorkey_active) {
1546 *convert = CONVERT_CK_RGB24;
1547 *format = GL_RGBA;
1548 *internal = GL_RGBA;
1549 *type = GL_UNSIGNED_INT_8_8_8_8;
1550 *target_bpp = 4;
1552 break;
1554 case WINED3DFMT_X8R8G8B8:
1555 if (colorkey_active) {
1556 *convert = CONVERT_RGB32_888;
1557 *format = GL_RGBA;
1558 *internal = GL_RGBA;
1559 *type = GL_UNSIGNED_INT_8_8_8_8;
1561 break;
1563 case WINED3DFMT_V8U8:
1564 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1565 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1566 *format = GL_DUDV_ATI;
1567 *internal = GL_DU8DV8_ATI;
1568 *type = GL_BYTE;
1569 /* No conversion - Just change the gl type */
1570 break;
1572 *convert = CONVERT_V8U8;
1573 *format = GL_BGR;
1574 *internal = GL_RGB8;
1575 *type = GL_UNSIGNED_BYTE;
1576 *target_bpp = 3;
1577 break;
1579 case WINED3DFMT_L6V5U5:
1580 *convert = CONVERT_L6V5U5;
1581 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1582 *target_bpp = 3;
1583 /* Use format and types from table */
1584 } else {
1585 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1586 *target_bpp = 2;
1587 *format = GL_RGB;
1588 *internal = GL_RGB5;
1589 *type = GL_UNSIGNED_SHORT_5_6_5;
1591 break;
1593 case WINED3DFMT_X8L8V8U8:
1594 *convert = CONVERT_X8L8V8U8;
1595 *target_bpp = 4;
1596 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1597 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1598 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1599 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1600 * the needed type and format parameter, so the internal format contains a
1601 * 4th component, which is returned as alpha
1603 } else {
1604 /* Not supported by GL_ATI_envmap_bumpmap */
1605 *format = GL_BGRA;
1606 *internal = GL_RGBA8;
1607 *type = GL_UNSIGNED_BYTE;
1609 break;
1611 case WINED3DFMT_Q8W8V8U8:
1612 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1613 *convert = CONVERT_Q8W8V8U8;
1614 *format = GL_BGRA;
1615 *internal = GL_RGBA8;
1616 *type = GL_UNSIGNED_BYTE;
1617 *target_bpp = 4;
1618 /* Not supported by GL_ATI_envmap_bumpmap */
1619 break;
1621 case WINED3DFMT_V16U16:
1622 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1623 *convert = CONVERT_V16U16;
1624 *format = GL_BGR;
1625 *internal = GL_RGB16;
1626 *type = GL_SHORT;
1627 *target_bpp = 6;
1628 /* What should I do here about GL_ATI_envmap_bumpmap?
1629 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1631 break;
1633 case WINED3DFMT_A4L4:
1634 /* A4L4 exists as an internal gl format, but for some reason there is not
1635 * format+type combination to load it. Thus convert it to A8L8, then load it
1636 * with A4L4 internal, but A8L8 format+type
1638 *convert = CONVERT_A4L4;
1639 *format = GL_LUMINANCE_ALPHA;
1640 *internal = GL_LUMINANCE4_ALPHA4;
1641 *type = GL_UNSIGNED_BYTE;
1642 *target_bpp = 2;
1643 break;
1645 case WINED3DFMT_R32F:
1646 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1647 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1648 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1649 * 1.0 instead.
1651 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1653 *convert = CONVERT_R32F;
1654 *format = GL_RGB;
1655 *internal = GL_RGB32F_ARB;
1656 *type = GL_FLOAT;
1657 *target_bpp = 12;
1658 break;
1660 case WINED3DFMT_R16F:
1661 /* Similar to R32F */
1662 *convert = CONVERT_R16F;
1663 *format = GL_RGB;
1664 *internal = GL_RGB16F_ARB;
1665 *type = GL_HALF_FLOAT_ARB;
1666 *target_bpp = 6;
1667 break;
1669 default:
1670 break;
1673 return WINED3D_OK;
1676 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This) {
1677 BYTE *source, *dest;
1678 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1680 switch (convert) {
1681 case NO_CONVERSION:
1683 memcpy(dst, src, pitch * height);
1684 break;
1686 case CONVERT_PALETTED:
1687 case CONVERT_PALETTED_CK:
1689 IWineD3DPaletteImpl* pal = This->palette;
1690 BYTE table[256][4];
1691 unsigned int i;
1692 unsigned int x, y;
1694 if( pal == NULL) {
1695 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1698 if (pal == NULL) {
1699 /* Still no palette? Use the device's palette */
1700 /* Get the surface's palette */
1701 for (i = 0; i < 256; i++) {
1702 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1704 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1705 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1706 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1707 if ((convert == CONVERT_PALETTED_CK) &&
1708 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1709 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1710 /* We should maybe here put a more 'neutral' color than the standard bright purple
1711 one often used by application to prevent the nice purple borders when bi-linear
1712 filtering is on */
1713 table[i][3] = 0x00;
1714 } else {
1715 table[i][3] = 0xFF;
1718 } else {
1719 TRACE("Using surface palette %p\n", pal);
1720 /* Get the surface's palette */
1721 for (i = 0; i < 256; i++) {
1722 table[i][0] = pal->palents[i].peRed;
1723 table[i][1] = pal->palents[i].peGreen;
1724 table[i][2] = pal->palents[i].peBlue;
1725 if ((convert == CONVERT_PALETTED_CK) &&
1726 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1727 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1728 /* We should maybe here put a more 'neutral' color than the standard bright purple
1729 one often used by application to prevent the nice purple borders when bi-linear
1730 filtering is on */
1731 table[i][3] = 0x00;
1732 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
1733 table[i][3] = pal->palents[i].peFlags;
1734 } else {
1735 table[i][3] = 0xFF;
1740 for (y = 0; y < height; y++)
1742 source = src + pitch * y;
1743 dest = dst + outpitch * y;
1744 /* This is an 1 bpp format, using the width here is fine */
1745 for (x = 0; x < width; x++) {
1746 BYTE color = *source++;
1747 *dest++ = table[color][0];
1748 *dest++ = table[color][1];
1749 *dest++ = table[color][2];
1750 *dest++ = table[color][3];
1754 break;
1756 case CONVERT_CK_565:
1758 /* Converting the 565 format in 5551 packed to emulate color-keying.
1760 Note : in all these conversion, it would be best to average the averaging
1761 pixels to get the color of the pixel that will be color-keyed to
1762 prevent 'color bleeding'. This will be done later on if ever it is
1763 too visible.
1765 Note2: Nvidia documents say that their driver does not support alpha + color keying
1766 on the same surface and disables color keying in such a case
1768 unsigned int x, y;
1769 WORD *Source;
1770 WORD *Dest;
1772 TRACE("Color keyed 565\n");
1774 for (y = 0; y < height; y++) {
1775 Source = (WORD *) (src + y * pitch);
1776 Dest = (WORD *) (dst + y * outpitch);
1777 for (x = 0; x < width; x++ ) {
1778 WORD color = *Source++;
1779 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1780 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1781 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1782 *Dest |= 0x0001;
1784 Dest++;
1788 break;
1790 case CONVERT_CK_5551:
1792 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1793 unsigned int x, y;
1794 WORD *Source;
1795 WORD *Dest;
1796 TRACE("Color keyed 5551\n");
1797 for (y = 0; y < height; y++) {
1798 Source = (WORD *) (src + y * pitch);
1799 Dest = (WORD *) (dst + y * outpitch);
1800 for (x = 0; x < width; x++ ) {
1801 WORD color = *Source++;
1802 *Dest = color;
1803 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1804 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1805 *Dest |= (1 << 15);
1807 else {
1808 *Dest &= ~(1 << 15);
1810 Dest++;
1814 break;
1816 case CONVERT_V8U8:
1818 unsigned int x, y;
1819 short *Source;
1820 unsigned char *Dest;
1821 for(y = 0; y < height; y++) {
1822 Source = (short *) (src + y * pitch);
1823 Dest = (unsigned char *) (dst + y * outpitch);
1824 for (x = 0; x < width; x++ ) {
1825 long color = (*Source++);
1826 /* B */ Dest[0] = 0xff;
1827 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1828 /* R */ Dest[2] = (color) + 128; /* U */
1829 Dest += 3;
1832 break;
1835 case CONVERT_Q8W8V8U8:
1837 unsigned int x, y;
1838 DWORD *Source;
1839 unsigned char *Dest;
1840 for(y = 0; y < height; y++) {
1841 Source = (DWORD *) (src + y * pitch);
1842 Dest = (unsigned char *) (dst + y * outpitch);
1843 for (x = 0; x < width; x++ ) {
1844 long color = (*Source++);
1845 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1846 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1847 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1848 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1849 Dest += 4;
1852 break;
1855 case CONVERT_L6V5U5:
1857 unsigned int x, y;
1858 WORD *Source;
1859 unsigned char *Dest;
1861 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1862 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1863 * fixed function and shaders without further conversion once the surface is
1864 * loaded
1866 for(y = 0; y < height; y++) {
1867 Source = (WORD *) (src + y * pitch);
1868 Dest = (unsigned char *) (dst + y * outpitch);
1869 for (x = 0; x < width; x++ ) {
1870 short color = (*Source++);
1871 unsigned char l = ((color >> 10) & 0xfc);
1872 char v = ((color >> 5) & 0x3e);
1873 char u = ((color ) & 0x1f);
1875 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1876 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1877 * shift. GL reads a signed value and converts it into an unsigned value.
1879 /* M */ Dest[2] = l << 1;
1881 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1882 * from 5 bit values to 8 bit values.
1884 /* V */ Dest[1] = v << 3;
1885 /* U */ Dest[0] = u << 3;
1886 Dest += 3;
1889 } else {
1890 FIXME("Add D3DFMT_L6V5U5 emulation using standard unsigned RGB and shaders\n");
1892 break;
1895 case CONVERT_X8L8V8U8:
1897 unsigned int x, y;
1898 DWORD *Source;
1899 unsigned char *Dest;
1901 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1902 /* This implementation works with the fixed function pipeline and shaders
1903 * without further modification after converting the surface.
1905 for(y = 0; y < height; y++) {
1906 Source = (DWORD *) (src + y * pitch);
1907 Dest = (unsigned char *) (dst + y * outpitch);
1908 for (x = 0; x < width; x++ ) {
1909 long color = (*Source++);
1910 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
1911 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
1912 /* U */ Dest[0] = (color & 0xff); /* U */
1913 /* I */ Dest[3] = 255; /* X */
1914 Dest += 4;
1917 } else {
1918 /* Doesn't work correctly with the fixed function pipeline, but can work in
1919 * shaders if the shader is adjusted. (There's no use for this format in gl's
1920 * standard fixed function pipeline anyway).
1922 FIXME("Implement CONVERT_X8L8V8U8 with standard unsigned GL_RGB\n");
1924 break;
1927 case CONVERT_A4L4:
1929 unsigned int x, y;
1930 unsigned char *Source;
1931 unsigned char *Dest;
1932 for(y = 0; y < height; y++) {
1933 Source = (unsigned char *) (src + y * pitch);
1934 Dest = (unsigned char *) (dst + y * outpitch);
1935 for (x = 0; x < width; x++ ) {
1936 unsigned char color = (*Source++);
1937 /* A */ Dest[1] = (color & 0xf0) << 0;
1938 /* L */ Dest[0] = (color & 0x0f) << 4;
1939 Dest += 2;
1942 break;
1945 case CONVERT_R32F:
1947 unsigned int x, y;
1948 float *Source;
1949 float *Dest;
1950 for(y = 0; y < height; y++) {
1951 Source = (float *) (src + y * pitch);
1952 Dest = (float *) (dst + y * outpitch);
1953 for (x = 0; x < width; x++ ) {
1954 float color = (*Source++);
1955 Dest[0] = color;
1956 Dest[1] = 1.0;
1957 Dest[2] = 1.0;
1958 Dest += 3;
1961 break;
1964 case CONVERT_R16F:
1966 unsigned int x, y;
1967 WORD *Source;
1968 WORD *Dest;
1969 WORD one = 0x3c00;
1970 for(y = 0; y < height; y++) {
1971 Source = (WORD *) (src + y * pitch);
1972 Dest = (WORD *) (dst + y * outpitch);
1973 for (x = 0; x < width; x++ ) {
1974 WORD color = (*Source++);
1975 Dest[0] = color;
1976 Dest[1] = one;
1977 Dest[2] = one;
1978 Dest += 3;
1981 break;
1983 default:
1984 ERR("Unsupported conversation type %d\n", convert);
1986 return WINED3D_OK;
1989 /* This function is used in case of 8bit paletted textures to upload the palette.
1990 For now it only supports GL_EXT_paletted_texture extension but support for other
1991 extensions like ARB_fragment_program and ATI_fragment_shaders will be added as well.
1993 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
1994 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1995 IWineD3DPaletteImpl* pal = This->palette;
1996 BYTE table[256][4];
1997 int i;
1999 if (pal == NULL) {
2000 /* Still no palette? Use the device's palette */
2001 /* Get the surface's palette */
2002 for (i = 0; i < 256; i++) {
2003 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2005 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2006 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2007 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2008 if ((convert == CONVERT_PALETTED_CK) &&
2009 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
2010 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
2011 /* We should maybe here put a more 'neutral' color than the standard bright purple
2012 one often used by application to prevent the nice purple borders when bi-linear
2013 filtering is on */
2014 table[i][3] = 0x00;
2015 } else {
2016 table[i][3] = 0xFF;
2019 } else {
2020 TRACE("Using surface palette %p\n", pal);
2021 /* Get the surface's palette */
2022 for (i = 0; i < 256; i++) {
2023 table[i][0] = pal->palents[i].peRed;
2024 table[i][1] = pal->palents[i].peGreen;
2025 table[i][2] = pal->palents[i].peBlue;
2026 if ((convert == CONVERT_PALETTED_CK) &&
2027 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
2028 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
2029 /* We should maybe here put a more 'neutral' color than the standard bright purple
2030 one often used by application to prevent the nice purple borders when bi-linear
2031 filtering is on */
2032 table[i][3] = 0x00;
2033 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
2034 table[i][3] = pal->palents[i].peFlags;
2035 } else {
2036 table[i][3] = 0xFF;
2040 GL_EXTCALL(glColorTableEXT(GL_TEXTURE_2D,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2043 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2044 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2046 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2047 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2048 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2050 return FALSE;
2053 if(This->palette9) {
2054 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2055 return FALSE;
2057 } else {
2058 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2060 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2061 return TRUE;
2064 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
2065 GLboolean oldwrite[4];
2067 /* Some formats have only some color channels, and the others are 1.0.
2068 * since our rendering renders to all channels, and those pixel formats
2069 * are emulated by using a full texture with the other channels set to 1.0
2070 * manually, clear the unused channels.
2072 * This could be done with hacking colorwriteenable to mask the colors,
2073 * but before drawing the buffer would have to be cleared too, so there's
2074 * no gain in that
2076 switch(This->resource.format) {
2077 case WINED3DFMT_R16F:
2078 case WINED3DFMT_R32F:
2079 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2080 /* Do not activate a context, the correct drawable is active already
2081 * though just the read buffer is set, make sure to have the correct draw
2082 * buffer too
2084 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2085 glDisable(GL_SCISSOR_TEST);
2086 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2087 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2088 glClearColor(0.0, 1.0, 1.0, 1.0);
2089 glClear(GL_COLOR_BUFFER_BIT);
2090 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2091 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2092 checkGLcall("Unused channel clear\n");
2093 break;
2095 default: break;
2099 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2100 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2101 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2102 GLenum format, internal, type;
2103 CONVERT_TYPES convert;
2104 int bpp;
2105 int width, pitch, outpitch;
2106 BYTE *mem;
2108 if (!(This->Flags & SFLAG_INTEXTURE)) {
2109 TRACE("Reloading because surface is dirty\n");
2110 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2111 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2112 /* Reload: vice versa OR */
2113 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2114 /* Also reload: Color key is active AND the color key has changed */
2115 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2116 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2117 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2118 TRACE("Reloading because of color keying\n");
2119 } else if(palette9_changed(This)) {
2120 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
2121 } else {
2122 TRACE("surface is already in texture\n");
2123 return WINED3D_OK;
2126 This->Flags |= SFLAG_INTEXTURE;
2128 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2129 * These resources are not bound by device size or format restrictions. Because of this,
2130 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2131 * However, these resources can always be created, locked, and copied.
2133 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2135 FIXME("(%p) Operation not supported for scratch textures\n",This);
2136 return WINED3DERR_INVALIDCALL;
2139 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb_mode);
2141 if (This->Flags & SFLAG_INDRAWABLE) {
2142 if (This->resource.format == WINED3DFMT_P8 || This->resource.format == WINED3DFMT_A8P8 ||
2143 This->resource.format == WINED3DFMT_DXT1 || This->resource.format == WINED3DFMT_DXT2 ||
2144 This->resource.format == WINED3DFMT_DXT3 || This->resource.format == WINED3DFMT_DXT4 ||
2145 This->resource.format == WINED3DFMT_DXT5)
2146 FIXME("Format %d not supported\n", This->resource.format);
2147 else {
2148 GLint prevRead;
2150 ENTER_GL();
2151 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2152 vcheckGLcall("glGetIntegerv");
2153 glReadBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2154 vcheckGLcall("glReadBuffer");
2156 if(!(This->Flags & SFLAG_ALLOCATED)) {
2157 surface_allocate_surface(This, internal, This->pow2Width,
2158 This->pow2Height, format, type);
2161 clear_unused_channels(This);
2163 glCopyTexSubImage2D(This->glDescription.target,
2164 This->glDescription.level,
2165 0, 0, 0, 0,
2166 This->currentDesc.Width,
2167 This->currentDesc.Height);
2168 checkGLcall("glCopyTexSubImage2D");
2170 glReadBuffer(prevRead);
2171 vcheckGLcall("glReadBuffer");
2173 LEAVE_GL();
2175 TRACE("Updated target %d\n", This->glDescription.target);
2177 return WINED3D_OK;
2178 } else
2179 /* The only place where LoadTexture() might get called when isInDraw=1
2180 * is ActivateContext where lastActiveRenderTarget is preloaded.
2182 if(iface == device->lastActiveRenderTarget && device->isInDraw)
2183 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
2185 /* Otherwise: System memory copy must be most up to date */
2187 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
2188 This->Flags |= SFLAG_GLCKEY;
2189 This->glCKey = This->SrcBltCKey;
2191 else This->Flags &= ~SFLAG_GLCKEY;
2193 /* The width is in 'length' not in bytes */
2194 width = This->currentDesc.Width;
2195 pitch = IWineD3DSurface_GetPitch(iface);
2197 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
2198 int height = This->currentDesc.Height;
2200 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
2201 outpitch = width * bpp;
2202 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
2204 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
2205 if(!mem) {
2206 ERR("Out of memory %d, %d!\n", outpitch, height);
2207 return WINED3DERR_OUTOFVIDEOMEMORY;
2209 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
2211 This->Flags |= SFLAG_CONVERTED;
2212 } else if (This->resource.format == WINED3DFMT_P8 && GL_SUPPORT(EXT_PALETTED_TEXTURE)) {
2213 d3dfmt_p8_upload_palette(iface, convert);
2214 This->Flags &= ~SFLAG_CONVERTED;
2215 mem = This->resource.allocatedMemory;
2216 } else {
2217 This->Flags &= ~SFLAG_CONVERTED;
2218 mem = This->resource.allocatedMemory;
2221 /* Make sure the correct pitch is used */
2222 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
2224 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
2225 TRACE("non power of two support\n");
2226 if(!(This->Flags & SFLAG_ALLOCATED)) {
2227 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
2229 if (mem) {
2230 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
2232 } else {
2233 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
2234 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
2236 if(!(This->Flags & SFLAG_ALLOCATED)) {
2237 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
2239 if (mem) {
2240 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
2244 /* Restore the default pitch */
2245 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
2247 if (mem != This->resource.allocatedMemory)
2248 HeapFree(GetProcessHeap(), 0, mem);
2250 #if 0
2252 static unsigned int gen = 0;
2253 char buffer[4096];
2254 ++gen;
2255 if ((gen % 10) == 0) {
2256 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2257 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2260 * debugging crash code
2261 if (gen == 250) {
2262 void** test = NULL;
2263 *test = 0;
2267 #endif
2269 if (!(This->Flags & SFLAG_DONOTFREE)) {
2270 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
2271 This->resource.allocatedMemory = NULL;
2272 This->Flags &= ~SFLAG_INSYSMEM;
2275 return WINED3D_OK;
2278 #include <errno.h>
2279 #include <stdio.h>
2280 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2281 FILE* f = NULL;
2282 UINT i, y;
2283 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2284 char *allocatedMemory;
2285 char *textureRow;
2286 IWineD3DSwapChain *swapChain = NULL;
2287 int width, height;
2288 GLuint tmpTexture = 0;
2289 DWORD color;
2290 /*FIXME:
2291 Textures may not be stored in ->allocatedgMemory and a GlTexture
2292 so we should lock the surface before saving a snapshot, or at least check that
2294 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2295 by calling GetTexImage and in compressed form by calling
2296 GetCompressedTexImageARB. Queried compressed images can be saved and
2297 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2298 texture images do not need to be processed by the GL and should
2299 significantly improve texture loading performance relative to uncompressed
2300 images. */
2302 /* Setup the width and height to be the internal texture width and height. */
2303 width = This->pow2Width;
2304 height = This->pow2Height;
2305 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2306 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2308 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2309 /* if were not a real texture then read the back buffer into a real texture */
2310 /* we don't want to interfere with the back buffer so read the data into a temporary
2311 * texture and then save the data out of the temporary texture
2313 GLint prevRead;
2314 ENTER_GL();
2315 TRACE("(%p) Reading render target into texture\n", This);
2316 glEnable(GL_TEXTURE_2D);
2318 glGenTextures(1, &tmpTexture);
2319 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2321 glTexImage2D(GL_TEXTURE_2D,
2323 GL_RGBA,
2324 width,
2325 height,
2326 0/*border*/,
2327 GL_RGBA,
2328 GL_UNSIGNED_INT_8_8_8_8_REV,
2329 NULL);
2331 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2332 vcheckGLcall("glGetIntegerv");
2333 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2334 vcheckGLcall("glReadBuffer");
2335 glCopyTexImage2D(GL_TEXTURE_2D,
2337 GL_RGBA,
2340 width,
2341 height,
2344 checkGLcall("glCopyTexImage2D");
2345 glReadBuffer(prevRead);
2346 LEAVE_GL();
2348 } else { /* bind the real texture, and make sure it up to date */
2349 IWineD3DSurface_PreLoad(iface);
2351 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2352 ENTER_GL();
2353 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2354 glGetTexImage(GL_TEXTURE_2D,
2355 This->glDescription.level,
2356 GL_RGBA,
2357 GL_UNSIGNED_INT_8_8_8_8_REV,
2358 allocatedMemory);
2359 checkGLcall("glTexImage2D");
2360 if (tmpTexture) {
2361 glBindTexture(GL_TEXTURE_2D, 0);
2362 glDeleteTextures(1, &tmpTexture);
2364 LEAVE_GL();
2366 f = fopen(filename, "w+");
2367 if (NULL == f) {
2368 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2369 return WINED3DERR_INVALIDCALL;
2371 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2372 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2373 /* TGA header */
2374 fputc(0,f);
2375 fputc(0,f);
2376 fputc(2,f);
2377 fputc(0,f);
2378 fputc(0,f);
2379 fputc(0,f);
2380 fputc(0,f);
2381 fputc(0,f);
2382 fputc(0,f);
2383 fputc(0,f);
2384 fputc(0,f);
2385 fputc(0,f);
2386 /* short width*/
2387 fwrite(&width,2,1,f);
2388 /* short height */
2389 fwrite(&height,2,1,f);
2390 /* format rgba */
2391 fputc(0x20,f);
2392 fputc(0x28,f);
2393 /* raw data */
2394 /* 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 */
2395 if(swapChain)
2396 textureRow = allocatedMemory + (width * (height - 1) *4);
2397 else
2398 textureRow = allocatedMemory;
2399 for (y = 0 ; y < height; y++) {
2400 for (i = 0; i < width; i++) {
2401 color = *((DWORD*)textureRow);
2402 fputc((color >> 16) & 0xFF, f); /* B */
2403 fputc((color >> 8) & 0xFF, f); /* G */
2404 fputc((color >> 0) & 0xFF, f); /* R */
2405 fputc((color >> 24) & 0xFF, f); /* A */
2406 textureRow += 4;
2408 /* take two rows of the pointer to the texture memory */
2409 if(swapChain)
2410 (textureRow-= width << 3);
2413 TRACE("Closing file\n");
2414 fclose(f);
2416 if(swapChain) {
2417 IWineD3DSwapChain_Release(swapChain);
2419 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2420 return WINED3D_OK;
2424 * Slightly inefficient way to handle multiple dirty rects but it works :)
2426 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2427 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2428 IWineD3DBaseTexture *baseTexture = NULL;
2429 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2430 surface_download_data(This);
2432 This->Flags &= ~(SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
2433 if (NULL != pDirtyRect) {
2434 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2435 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2436 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2437 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2438 } else {
2439 This->dirtyRect.left = 0;
2440 This->dirtyRect.top = 0;
2441 This->dirtyRect.right = This->currentDesc.Width;
2442 This->dirtyRect.bottom = This->currentDesc.Height;
2444 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2445 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2446 /* if the container is a basetexture then mark it dirty. */
2447 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2448 TRACE("Passing to container\n");
2449 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2450 IWineD3DBaseTexture_Release(baseTexture);
2452 return WINED3D_OK;
2455 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
2456 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2458 TRACE("This %p, container %p\n", This, container);
2460 /* We can't keep a reference to the container, since the container already keeps a reference to us. */
2462 TRACE("Setting container to %p from %p\n", container, This->container);
2463 This->container = container;
2465 return WINED3D_OK;
2468 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2469 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2470 const GlPixelFormatDesc *glDesc;
2471 const StaticPixelFormatDesc *formatEntry = getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2473 if (This->resource.format != WINED3DFMT_UNKNOWN) {
2474 FIXME("(%p) : The format of the surface must be WINED3DFORMAT_UNKNOWN\n", This);
2475 return WINED3DERR_INVALIDCALL;
2478 TRACE("(%p) : Setting texture format to (%d,%s)\n", This, format, debug_d3dformat(format));
2479 if (format == WINED3DFMT_UNKNOWN) {
2480 This->resource.size = 0;
2481 } else if (format == WINED3DFMT_DXT1) {
2482 /* DXT1 is half byte per pixel */
2483 This->resource.size = ((max(This->pow2Width, 4) * formatEntry->bpp) * max(This->pow2Height, 4)) >> 1;
2485 } else if (format == WINED3DFMT_DXT2 || format == WINED3DFMT_DXT3 ||
2486 format == WINED3DFMT_DXT4 || format == WINED3DFMT_DXT5) {
2487 This->resource.size = ((max(This->pow2Width, 4) * formatEntry->bpp) * max(This->pow2Height, 4));
2488 } else {
2489 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
2490 This->resource.size = ((This->pow2Width * formatEntry->bpp) + alignment - 1) & ~(alignment - 1);
2491 This->resource.size *= This->pow2Height;
2495 /* Setup some glformat defaults */
2496 This->glDescription.glFormat = glDesc->glFormat;
2497 This->glDescription.glFormatInternal = glDesc->glInternal;
2498 This->glDescription.glType = glDesc->glType;
2500 if (format != WINED3DFMT_UNKNOWN) {
2501 This->bytesPerPixel = formatEntry->bpp;
2502 } else {
2503 This->bytesPerPixel = 0;
2506 This->Flags |= (WINED3DFMT_D16_LOCKABLE == format) ? SFLAG_LOCKABLE : 0;
2507 This->Flags &= ~SFLAG_ALLOCATED;
2509 This->resource.format = format;
2511 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);
2513 return WINED3D_OK;
2516 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2517 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2519 /* Render targets depend on their hdc, and we can't create an hdc on a user pointer */
2520 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
2521 ERR("Not supported on render targets\n");
2522 return WINED3DERR_INVALIDCALL;
2525 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2526 WARN("Surface is locked or the HDC is in use\n");
2527 return WINED3DERR_INVALIDCALL;
2530 if(Mem && Mem != This->resource.allocatedMemory) {
2531 void *release = NULL;
2533 /* Do I have to copy the old surface content? */
2534 if(This->Flags & SFLAG_DIBSECTION) {
2535 /* Release the DC. No need to hold the critical section for the update
2536 * Thread because this thread runs only on front buffers, but this method
2537 * fails for render targets in the check above.
2539 SelectObject(This->hDC, This->dib.holdbitmap);
2540 DeleteDC(This->hDC);
2541 /* Release the DIB section */
2542 DeleteObject(This->dib.DIBsection);
2543 This->dib.bitmap_data = NULL;
2544 This->resource.allocatedMemory = NULL;
2545 This->hDC = NULL;
2546 This->Flags &= ~SFLAG_DIBSECTION;
2547 } else if(!(This->Flags & SFLAG_USERPTR)) {
2548 release = This->resource.allocatedMemory;
2550 This->resource.allocatedMemory = Mem;
2551 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2553 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2554 This->Flags &= ~(SFLAG_INDRAWABLE | SFLAG_INTEXTURE);
2556 /* For client textures opengl has to be notified */
2557 if(This->Flags & SFLAG_CLIENT) {
2558 This->Flags &= ~SFLAG_ALLOCATED;
2559 IWineD3DSurface_PreLoad(iface);
2560 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2563 /* Now free the old memory if any */
2564 HeapFree(GetProcessHeap(), 0, release);
2565 } else if(This->Flags & SFLAG_USERPTR) {
2566 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2567 This->resource.allocatedMemory = NULL;
2568 This->Flags &= ~SFLAG_USERPTR;
2570 if(This->Flags & SFLAG_CLIENT) {
2571 This->Flags &= ~SFLAG_ALLOCATED;
2572 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2573 IWineD3DSurface_PreLoad(iface);
2576 return WINED3D_OK;
2579 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2580 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2581 IWineD3DSwapChainImpl *swapchain = NULL;
2582 HRESULT hr;
2583 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2585 /* Flipping is only supported on RenderTargets */
2586 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2588 if(override) {
2589 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2590 * FIXME("(%p) Target override is not supported by now\n", This);
2591 * Additionally, it isn't really possible to support triple-buffering
2592 * properly on opengl at all
2596 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2597 if(!swapchain) {
2598 ERR("Flipped surface is not on a swapchain\n");
2599 return WINEDDERR_NOTFLIPPABLE;
2602 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2603 * and only d3d8 and d3d9 apps specify the presentation interval
2605 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2606 /* Most common case first to avoid wasting time on all the other cases */
2607 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2608 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2609 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2610 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2611 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2612 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2613 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2614 } else {
2615 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2618 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2619 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2620 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2621 return hr;
2624 /* Does a direct frame buffer -> texture copy. Stretching is done
2625 * with single pixel copy calls
2627 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2628 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2629 float xrel, yrel;
2630 UINT row;
2631 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2634 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2635 ENTER_GL();
2636 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2638 /* Bind the target texture */
2639 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
2640 checkGLcall("glBindTexture");
2641 if(!swapchain) {
2642 glReadBuffer(myDevice->offscreenBuffer);
2643 } else {
2644 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2645 glReadBuffer(buffer);
2647 checkGLcall("glReadBuffer");
2649 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2650 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2652 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2653 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2655 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2656 ERR("Texture filtering not supported in direct blit\n");
2658 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2659 ERR("Texture filtering not supported in direct blit\n");
2662 if(upsidedown &&
2663 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2664 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2665 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2667 glCopyTexSubImage2D(This->glDescription.target,
2668 This->glDescription.level,
2669 drect->x1, drect->y1, /* xoffset, yoffset */
2670 srect->x1, Src->currentDesc.Height - srect->y2,
2671 drect->x2 - drect->x1, drect->y2 - drect->y1);
2672 } else {
2673 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2674 /* I have to process this row by row to swap the image,
2675 * otherwise it would be upside down, so stretching in y direction
2676 * doesn't cost extra time
2678 * However, stretching in x direction can be avoided if not necessary
2680 for(row = drect->y1; row < drect->y2; row++) {
2681 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2682 /* Well, that stuff works, but it's very slow.
2683 * find a better way instead
2685 UINT col;
2687 for(col = drect->x1; col < drect->x2; col++) {
2688 glCopyTexSubImage2D(This->glDescription.target,
2689 This->glDescription.level,
2690 drect->x1 + col, row, /* xoffset, yoffset */
2691 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2692 1, 1);
2694 } else {
2695 glCopyTexSubImage2D(This->glDescription.target,
2696 This->glDescription.level,
2697 drect->x1, row, /* xoffset, yoffset */
2698 srect->x1, yoffset - (int) (row * yrel),
2699 drect->x2-drect->x1, 1);
2704 vcheckGLcall("glCopyTexSubImage2D");
2705 LEAVE_GL();
2708 /* Uses the hardware to stretch and flip the image */
2709 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2710 GLuint src, backup = 0;
2711 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2712 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2713 float left, right, top, bottom; /* Texture coordinates */
2714 UINT fbwidth = Src->currentDesc.Width;
2715 UINT fbheight = Src->currentDesc.Height;
2716 GLenum drawBuffer = GL_BACK;
2718 TRACE("Using hwstretch blit\n");
2719 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2720 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2721 ENTER_GL();
2722 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2724 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2725 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2727 if(GL_LIMITS(aux_buffers) >= 2) {
2728 /* Got more than one aux buffer? Use the 2nd aux buffer */
2729 drawBuffer = GL_AUX1;
2730 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2731 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2732 drawBuffer = GL_AUX0;
2735 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2736 glGenTextures(1, &backup);
2737 checkGLcall("glGenTextures\n");
2738 glBindTexture(GL_TEXTURE_2D, backup);
2739 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2740 } else {
2741 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2742 * we are reading from the back buffer, the backup can be used as source texture
2744 if(Src->glDescription.textureName == 0) {
2745 /* Get it a description */
2746 IWineD3DSurface_PreLoad(SrcSurface);
2748 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
2749 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2751 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2752 Src->Flags &= ~SFLAG_INTEXTURE;
2755 glReadBuffer(GL_BACK);
2756 checkGLcall("glReadBuffer(GL_BACK)");
2758 /* TODO: Only back up the part that will be overwritten */
2759 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2760 0, 0 /* read offsets */,
2761 0, 0,
2762 fbwidth,
2763 fbheight);
2765 checkGLcall("glCopyTexSubImage2D");
2767 /* No issue with overriding these - the sampler is dirty due to blit usage */
2768 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
2769 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2770 checkGLcall("glTexParameteri");
2771 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
2772 minMipLookup[Filter][WINED3DTEXF_NONE]);
2773 checkGLcall("glTexParameteri");
2775 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2776 src = backup ? backup : Src->glDescription.textureName;
2777 } else {
2778 glReadBuffer(GL_FRONT);
2779 checkGLcall("glReadBuffer(GL_FRONT)");
2781 glGenTextures(1, &src);
2782 checkGLcall("glGenTextures(1, &src)");
2783 glBindTexture(GL_TEXTURE_2D, src);
2784 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2786 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2787 * out for power of 2 sizes
2789 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2790 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2791 checkGLcall("glTexImage2D");
2792 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2793 0, 0 /* read offsets */,
2794 0, 0,
2795 fbwidth,
2796 fbheight);
2798 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2799 checkGLcall("glTexParameteri");
2800 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2801 checkGLcall("glTexParameteri");
2803 glReadBuffer(GL_BACK);
2804 checkGLcall("glReadBuffer(GL_BACK)");
2806 checkGLcall("glEnd and previous");
2808 left = (float) srect->x1 / (float) Src->pow2Width;
2809 right = (float) srect->x2 / (float) Src->pow2Width;
2811 if(upsidedown) {
2812 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2813 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2814 } else {
2815 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2816 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2819 /* draw the source texture stretched and upside down. The correct surface is bound already */
2820 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
2821 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
2823 glDrawBuffer(drawBuffer);
2824 glReadBuffer(drawBuffer);
2826 glBegin(GL_QUADS);
2827 /* bottom left */
2828 glTexCoord2f(left, bottom);
2829 glVertex2i(0, fbheight);
2831 /* top left */
2832 glTexCoord2f(left, top);
2833 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2835 /* top right */
2836 glTexCoord2f(right, top);
2837 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2839 /* bottom right */
2840 glTexCoord2f(right, bottom);
2841 glVertex2i(drect->x2 - drect->x1, fbheight);
2842 glEnd();
2843 checkGLcall("glEnd and previous");
2845 /* Now read the stretched and upside down image into the destination texture */
2846 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2847 checkGLcall("glBindTexture");
2848 glCopyTexSubImage2D(This->glDescription.target,
2850 drect->x1, drect->y1, /* xoffset, yoffset */
2851 0, 0, /* We blitted the image to the origin */
2852 drect->x2 - drect->x1, drect->y2 - drect->y1);
2853 checkGLcall("glCopyTexSubImage2D");
2855 /* Write the back buffer backup back */
2856 glBindTexture(GL_TEXTURE_2D, backup ? backup : Src->glDescription.textureName);
2857 checkGLcall("glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName)");
2859 if(drawBuffer == GL_BACK) {
2860 glBegin(GL_QUADS);
2861 /* top left */
2862 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2863 glVertex2i(0, 0);
2865 /* bottom left */
2866 glTexCoord2f(0.0, 0.0);
2867 glVertex2i(0, fbheight);
2869 /* bottom right */
2870 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2871 glVertex2i(fbwidth, Src->currentDesc.Height);
2873 /* top right */
2874 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2875 glVertex2i(fbwidth, 0);
2876 glEnd();
2877 } else {
2878 /* Restore the old draw buffer */
2879 glDrawBuffer(GL_BACK);
2882 /* Cleanup */
2883 if(src != Src->glDescription.textureName && src != backup) {
2884 glDeleteTextures(1, &src);
2885 checkGLcall("glDeleteTextures(1, &src)");
2887 if(backup) {
2888 glDeleteTextures(1, &backup);
2889 checkGLcall("glDeleteTextures(1, &backup)");
2891 LEAVE_GL();
2894 /* Not called from the VTable */
2895 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2896 WINED3DRECT rect;
2897 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2898 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2899 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2901 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2903 /* Get the swapchain. One of the surfaces has to be a primary surface */
2904 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2905 WARN("Destination is in sysmem, rejecting gl blt\n");
2906 return WINED3DERR_INVALIDCALL;
2908 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2909 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2910 if(Src) {
2911 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2912 WARN("Src is in sysmem, rejecting gl blt\n");
2913 return WINED3DERR_INVALIDCALL;
2915 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2916 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2919 /* Early sort out of cases where no render target is used */
2920 if(!dstSwapchain && !srcSwapchain &&
2921 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2922 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2923 return WINED3DERR_INVALIDCALL;
2926 /* No destination color keying supported */
2927 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
2928 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2929 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2930 return WINED3DERR_INVALIDCALL;
2933 if (DestRect) {
2934 rect.x1 = DestRect->left;
2935 rect.y1 = DestRect->top;
2936 rect.x2 = DestRect->right;
2937 rect.y2 = DestRect->bottom;
2938 } else {
2939 rect.x1 = 0;
2940 rect.y1 = 0;
2941 rect.x2 = This->currentDesc.Width;
2942 rect.y2 = This->currentDesc.Height;
2945 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2946 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
2947 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
2948 /* Half-life does a Blt from the back buffer to the front buffer,
2949 * Full surface size, no flags... Use present instead
2951 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
2954 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2955 while(1)
2957 RECT mySrcRect;
2958 TRACE("Looking if a Present can be done...\n");
2959 /* Source Rectangle must be full surface */
2960 if( SrcRect ) {
2961 if(SrcRect->left != 0 || SrcRect->top != 0 ||
2962 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
2963 TRACE("No, Source rectangle doesn't match\n");
2964 break;
2967 mySrcRect.left = 0;
2968 mySrcRect.top = 0;
2969 mySrcRect.right = Src->currentDesc.Width;
2970 mySrcRect.bottom = Src->currentDesc.Height;
2972 /* No stretching may occur */
2973 if(mySrcRect.right != rect.x2 - rect.x1 ||
2974 mySrcRect.bottom != rect.y2 - rect.y1) {
2975 TRACE("No, stretching is done\n");
2976 break;
2979 /* Destination must be full surface or match the clipping rectangle */
2980 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
2982 RECT cliprect;
2983 POINT pos[2];
2984 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
2985 pos[0].x = rect.x1;
2986 pos[0].y = rect.y1;
2987 pos[1].x = rect.x2;
2988 pos[1].y = rect.y2;
2989 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
2990 pos, 2);
2992 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
2993 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
2995 TRACE("No, dest rectangle doesn't match(clipper)\n");
2996 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
2997 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
2998 break;
3001 else
3003 if(rect.x1 != 0 || rect.y1 != 0 ||
3004 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3005 TRACE("No, dest rectangle doesn't match(surface size)\n");
3006 break;
3010 TRACE("Yes\n");
3012 /* These flags are unimportant for the flag check, remove them */
3013 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3014 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3016 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3017 * take very long, while a flip is fast.
3018 * This applies to Half-Life, which does such Blts every time it finished
3019 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3020 * menu. This is also used by all apps when they do windowed rendering
3022 * The problem is that flipping is not really the same as copying. After a
3023 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3024 * untouched. Therefore it's necessary to override the swap effect
3025 * and to set it back after the flip.
3027 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3028 * testcases.
3031 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3032 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3034 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3035 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3037 dstSwapchain->presentParms.SwapEffect = orig_swap;
3039 return WINED3D_OK;
3041 break;
3044 TRACE("Unsupported blit between buffers on the same swapchain\n");
3045 return WINED3DERR_INVALIDCALL;
3046 } else if((dstSwapchain || This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) &&
3047 (srcSwapchain || SrcSurface == myDevice->render_targets[0]) ) {
3048 ERR("Can't perform hardware blit between 2 different swapchains, falling back to software\n");
3049 return WINED3DERR_INVALIDCALL;
3052 if(srcSwapchain || SrcSurface == myDevice->render_targets[0]) {
3053 /* Blit from render target to texture */
3054 WINED3DRECT srect;
3055 BOOL upsideDown, stretchx;
3057 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3058 TRACE("Color keying not supported by frame buffer to texture blit\n");
3059 return WINED3DERR_INVALIDCALL;
3060 /* Destination color key is checked above */
3063 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3064 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3066 if(SrcRect) {
3067 if(SrcRect->top < SrcRect->bottom) {
3068 srect.y1 = SrcRect->top;
3069 srect.y2 = SrcRect->bottom;
3070 upsideDown = FALSE;
3071 } else {
3072 srect.y1 = SrcRect->bottom;
3073 srect.y2 = SrcRect->top;
3074 upsideDown = TRUE;
3076 srect.x1 = SrcRect->left;
3077 srect.x2 = SrcRect->right;
3078 } else {
3079 srect.x1 = 0;
3080 srect.y1 = 0;
3081 srect.x2 = Src->currentDesc.Width;
3082 srect.y2 = Src->currentDesc.Height;
3083 upsideDown = FALSE;
3085 if(rect.x1 > rect.x2) {
3086 UINT tmp = rect.x2;
3087 rect.x2 = rect.x1;
3088 rect.x1 = tmp;
3089 upsideDown = !upsideDown;
3091 if(!srcSwapchain) {
3092 TRACE("Reading from an offscreen target\n");
3093 upsideDown = !upsideDown;
3096 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3097 stretchx = TRUE;
3098 } else {
3099 stretchx = FALSE;
3102 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3103 * flip the image nor scale it.
3105 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3106 * -> If the app wants a image width an unscaled width, copy it line per line
3107 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3108 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3109 * back buffer. This is slower than reading line per line, thus not used for flipping
3110 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3111 * pixel by pixel
3113 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3114 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3115 * backends.
3117 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3118 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3119 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3120 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3121 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3122 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3123 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3124 } else {
3125 TRACE("Using hardware stretching to flip / stretch the texture\n");
3126 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3129 if(!(This->Flags & SFLAG_DONOTFREE)) {
3130 HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
3131 This->resource.allocatedMemory = NULL;
3132 } else {
3133 This->Flags &= ~SFLAG_INSYSMEM;
3135 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3136 * path is never entered
3138 This->Flags |= SFLAG_INTEXTURE;
3140 return WINED3D_OK;
3141 } else if(Src) {
3142 /* Blit from offscreen surface to render target */
3143 float glTexCoord[4];
3144 DWORD oldCKeyFlags = Src->CKeyFlags;
3145 WINEDDCOLORKEY oldBltCKey = This->SrcBltCKey;
3146 RECT SourceRectangle;
3148 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3150 if(SrcRect) {
3151 SourceRectangle.left = SrcRect->left;
3152 SourceRectangle.right = SrcRect->right;
3153 SourceRectangle.top = SrcRect->top;
3154 SourceRectangle.bottom = SrcRect->bottom;
3155 } else {
3156 SourceRectangle.left = 0;
3157 SourceRectangle.right = Src->currentDesc.Width;
3158 SourceRectangle.top = 0;
3159 SourceRectangle.bottom = Src->currentDesc.Height;
3162 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3163 /* Fall back to software */
3164 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3165 SourceRectangle.left, SourceRectangle.top,
3166 SourceRectangle.right, SourceRectangle.bottom);
3167 return WINED3DERR_INVALIDCALL;
3170 /* Color keying: Check if we have to do a color keyed blt,
3171 * and if not check if a color key is activated.
3173 * Just modify the color keying parameters in the surface and restore them afterwards
3174 * The surface keeps track of the color key last used to load the opengl surface.
3175 * PreLoad will catch the change to the flags and color key and reload if necessary.
3177 if(Flags & WINEDDBLT_KEYSRC) {
3178 /* Use color key from surface */
3179 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3180 /* Use color key from DDBltFx */
3181 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3182 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3183 } else {
3184 /* Do not use color key */
3185 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3188 /* Now load the surface */
3189 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3192 /* Activate the destination context, set it up for blitting */
3193 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3194 ENTER_GL();
3196 if(!dstSwapchain) {
3197 TRACE("Drawing to offscreen buffer\n");
3198 glDrawBuffer(myDevice->offscreenBuffer);
3199 checkGLcall("glDrawBuffer");
3200 } else {
3201 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
3202 TRACE("Drawing to %#x buffer\n", buffer);
3203 glDrawBuffer(buffer);
3204 checkGLcall("glDrawBuffer");
3207 /* Bind the texture */
3208 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
3209 checkGLcall("glBindTexture");
3211 /* Filtering for StretchRect */
3212 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
3213 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
3214 checkGLcall("glTexParameteri");
3215 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
3216 minMipLookup[Filter][WINED3DTEXF_NONE]);
3217 checkGLcall("glTexParameteri");
3218 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
3219 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
3220 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3221 checkGLcall("glTexEnvi");
3223 /* This is for color keying */
3224 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3225 glEnable(GL_ALPHA_TEST);
3226 checkGLcall("glEnable GL_ALPHA_TEST");
3227 glAlphaFunc(GL_NOTEQUAL, 0.0);
3228 checkGLcall("glAlphaFunc\n");
3229 } else {
3230 glDisable(GL_ALPHA_TEST);
3231 checkGLcall("glDisable GL_ALPHA_TEST");
3234 /* Draw a textured quad
3236 glBegin(GL_QUADS);
3238 glColor3d(1.0f, 1.0f, 1.0f);
3239 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3240 glVertex3f(rect.x1,
3241 rect.y1,
3242 0.0);
3244 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3245 glVertex3f(rect.x1, rect.y2, 0.0);
3247 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3248 glVertex3f(rect.x2,
3249 rect.y2,
3250 0.0);
3252 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3253 glVertex3f(rect.x2,
3254 rect.y1,
3255 0.0);
3256 glEnd();
3257 checkGLcall("glEnd");
3259 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3260 glDisable(GL_ALPHA_TEST);
3261 checkGLcall("glDisable(GL_ALPHA_TEST)");
3264 /* Unbind the texture */
3265 glBindTexture(GL_TEXTURE_2D, 0);
3266 checkGLcall("glEnable glBindTexture");
3268 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3269 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3271 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3272 glDrawBuffer(GL_BACK);
3273 checkGLcall("glDrawBuffer");
3275 /* Restore the color key parameters */
3276 Src->CKeyFlags = oldCKeyFlags;
3277 This->SrcBltCKey = oldBltCKey;
3279 LEAVE_GL();
3281 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3282 This->Flags &= ~SFLAG_INSYSMEM;
3283 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3284 * is outdated now
3286 if(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO) {
3287 This->Flags |= SFLAG_INDRAWABLE;
3288 This->Flags &= ~SFLAG_INTEXTURE;
3289 } else {
3290 This->Flags |= SFLAG_INTEXTURE;
3293 return WINED3D_OK;
3294 } else {
3295 /* Source-Less Blit to render target */
3296 if (Flags & WINEDDBLT_COLORFILL) {
3297 /* This is easy to handle for the D3D Device... */
3298 DWORD color;
3300 TRACE("Colorfill\n");
3302 /* The color as given in the Blt function is in the format of the frame-buffer...
3303 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3305 if (This->resource.format == WINED3DFMT_P8) {
3306 if (This->palette) {
3307 color = ((0xFF000000) |
3308 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3309 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3310 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3311 } else {
3312 color = 0xFF000000;
3315 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3316 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3317 color = 0xFFFFFFFF;
3318 } else {
3319 color = ((0xFF000000) |
3320 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3321 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3322 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3325 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3326 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3327 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3329 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3330 color = DDBltFx->u5.dwFillColor;
3332 else {
3333 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3334 return WINED3DERR_INVALIDCALL;
3337 TRACE("Calling GetSwapChain with mydevice = %p\n", myDevice);
3338 if(dstSwapchain && dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]) {
3339 glDrawBuffer(GL_BACK);
3340 checkGLcall("glDrawBuffer(GL_BACK)");
3341 } else if (dstSwapchain && This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer) {
3342 glDrawBuffer(GL_FRONT);
3343 checkGLcall("glDrawBuffer(GL_FRONT)");
3344 } else if(This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3345 glDrawBuffer(myDevice->offscreenBuffer);
3346 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer3)");
3347 } else {
3348 TRACE("Surface is higher back buffer, falling back to software\n");
3349 return WINED3DERR_INVALIDCALL;
3352 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3354 IWineD3DDevice_Clear( (IWineD3DDevice *) myDevice,
3355 1 /* Number of rectangles */,
3356 &rect,
3357 WINED3DCLEAR_TARGET,
3358 color,
3359 0.0 /* Z */,
3360 0 /* Stencil */);
3362 /* Restore the original draw buffer */
3363 if(!dstSwapchain) {
3364 glDrawBuffer(myDevice->offscreenBuffer);
3365 } else if(dstSwapchain->backBuffer && dstSwapchain->backBuffer[0]) {
3366 glDrawBuffer(GL_BACK);
3368 vcheckGLcall("glDrawBuffer");
3370 return WINED3D_OK;
3374 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3375 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3376 return WINED3DERR_INVALIDCALL;
3379 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3381 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3382 float depth;
3384 if (Flags & WINEDDBLT_DEPTHFILL) {
3385 switch(This->resource.format) {
3386 case WINED3DFMT_D16:
3387 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3388 break;
3389 case WINED3DFMT_D15S1:
3390 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3391 break;
3392 case WINED3DFMT_D24S8:
3393 case WINED3DFMT_D24X8:
3394 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3395 break;
3396 case WINED3DFMT_D32:
3397 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3398 break;
3399 default:
3400 depth = 0.0;
3401 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3404 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3405 DestRect == NULL ? 0 : 1,
3406 (WINED3DRECT *) DestRect,
3407 WINED3DCLEAR_ZBUFFER,
3408 0x00000000,
3409 depth,
3410 0x00000000);
3413 FIXME("(%p): Unsupp depthstencil blit\n", This);
3414 return WINED3DERR_INVALIDCALL;
3417 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3418 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3419 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3420 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3421 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3422 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3424 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3425 * except depth blits, which seem to work
3427 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3428 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3429 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3430 return WINED3DERR_INVALIDCALL;
3431 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3432 TRACE("Z Blit override handled the blit\n");
3433 return WINED3D_OK;
3437 /* Special cases for RenderTargets */
3438 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3439 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3440 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3443 /* For the rest call the X11 surface implementation.
3444 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3445 * other Blts are rather rare
3447 return IWineGDISurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3450 HRESULT WINAPI IWineD3DSurfaceImpl_GetBltStatus(IWineD3DSurface *iface, DWORD Flags) {
3451 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3452 TRACE("(%p)->(%x)\n", This, Flags);
3454 switch (Flags)
3456 case WINEDDGBS_CANBLT:
3457 case WINEDDGBS_ISBLTDONE:
3458 return WINED3D_OK;
3460 default:
3461 return WINED3DERR_INVALIDCALL;
3465 HRESULT WINAPI IWineD3DSurfaceImpl_GetFlipStatus(IWineD3DSurface *iface, DWORD Flags) {
3466 /* XXX: DDERR_INVALIDSURFACETYPE */
3468 TRACE("(%p)->(%08x)\n",iface,Flags);
3469 switch (Flags) {
3470 case WINEDDGFS_CANFLIP:
3471 case WINEDDGFS_ISFLIPDONE:
3472 return WINED3D_OK;
3474 default:
3475 return WINED3DERR_INVALIDCALL;
3479 HRESULT WINAPI IWineD3DSurfaceImpl_IsLost(IWineD3DSurface *iface) {
3480 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3481 TRACE("(%p)\n", This);
3483 /* D3D8 and 9 loose full devices, ddraw only surfaces */
3484 return This->Flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
3487 HRESULT WINAPI IWineD3DSurfaceImpl_Restore(IWineD3DSurface *iface) {
3488 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3489 TRACE("(%p)\n", This);
3491 /* So far we don't lose anything :) */
3492 This->Flags &= ~SFLAG_LOST;
3493 return WINED3D_OK;
3496 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3497 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3498 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3499 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3500 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3502 if(myDevice->inScene &&
3503 (iface == myDevice->stencilBufferTarget ||
3504 (Source && Source == myDevice->stencilBufferTarget))) {
3505 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3506 return WINED3DERR_INVALIDCALL;
3509 /* Special cases for RenderTargets */
3510 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3511 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3513 RECT SrcRect, DstRect;
3514 DWORD Flags=0;
3516 if(rsrc) {
3517 SrcRect.left = rsrc->left;
3518 SrcRect.top= rsrc->top;
3519 SrcRect.bottom = rsrc->bottom;
3520 SrcRect.right = rsrc->right;
3521 } else {
3522 SrcRect.left = 0;
3523 SrcRect.top = 0;
3524 SrcRect.right = srcImpl->currentDesc.Width;
3525 SrcRect.bottom = srcImpl->currentDesc.Height;
3528 DstRect.left = dstx;
3529 DstRect.top=dsty;
3530 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3531 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3533 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3534 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3535 Flags |= WINEDDBLT_KEYSRC;
3536 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3537 Flags |= WINEDDBLT_KEYDEST;
3538 if(trans & WINEDDBLTFAST_WAIT)
3539 Flags |= WINEDDBLT_WAIT;
3540 if(trans & WINEDDBLTFAST_DONOTWAIT)
3541 Flags |= WINEDDBLT_DONOTWAIT;
3543 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3547 return IWineGDISurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3550 HRESULT WINAPI IWineD3DSurfaceImpl_GetPalette(IWineD3DSurface *iface, IWineD3DPalette **Pal) {
3551 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3552 TRACE("(%p)->(%p)\n", This, Pal);
3554 *Pal = (IWineD3DPalette *) This->palette;
3555 return WINED3D_OK;
3558 HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface) {
3559 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3560 RGBQUAD col[256];
3561 IWineD3DPaletteImpl *pal = This->palette;
3562 unsigned int n;
3563 TRACE("(%p)\n", This);
3565 if(This->resource.format == WINED3DFMT_P8 ||
3566 This->resource.format == WINED3DFMT_A8P8)
3568 if(!This->Flags & SFLAG_INSYSMEM) {
3569 FIXME("Palette changed with surface that does not have an up to date system memory copy\n");
3571 TRACE("Dirtifying surface\n");
3572 This->Flags &= ~(SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3575 if(This->Flags & SFLAG_DIBSECTION) {
3576 TRACE("(%p): Updating the hdc's palette\n", This);
3577 for (n=0; n<256; n++) {
3578 if(pal) {
3579 col[n].rgbRed = pal->palents[n].peRed;
3580 col[n].rgbGreen = pal->palents[n].peGreen;
3581 col[n].rgbBlue = pal->palents[n].peBlue;
3582 } else {
3583 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3584 /* Use the default device palette */
3585 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
3586 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
3587 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
3589 col[n].rgbReserved = 0;
3591 SetDIBColorTable(This->hDC, 0, 256, col);
3594 return WINED3D_OK;
3597 HRESULT WINAPI IWineD3DSurfaceImpl_SetPalette(IWineD3DSurface *iface, IWineD3DPalette *Pal) {
3598 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3599 IWineD3DPaletteImpl *PalImpl = (IWineD3DPaletteImpl *) Pal;
3600 TRACE("(%p)->(%p)\n", This, Pal);
3602 if(This->palette != NULL)
3603 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3604 This->palette->Flags &= ~WINEDDPCAPS_PRIMARYSURFACE;
3606 if(PalImpl != NULL) {
3607 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3608 /* Set the device's main palette if the palette
3609 * wasn't a primary palette before
3611 if(!(PalImpl->Flags & WINEDDPCAPS_PRIMARYSURFACE)) {
3612 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3613 unsigned int i;
3615 for(i=0; i < 256; i++) {
3616 device->palettes[device->currentPalette][i] = PalImpl->palents[i];
3620 (PalImpl)->Flags |= WINEDDPCAPS_PRIMARYSURFACE;
3623 This->palette = PalImpl;
3625 return IWineD3DSurface_RealizePalette(iface);
3628 HRESULT WINAPI IWineD3DSurfaceImpl_SetColorKey(IWineD3DSurface *iface, DWORD Flags, WINEDDCOLORKEY *CKey) {
3629 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3630 TRACE("(%p)->(%08x,%p)\n", This, Flags, CKey);
3632 if ((Flags & WINEDDCKEY_COLORSPACE) != 0) {
3633 FIXME(" colorkey value not supported (%08x) !\n", Flags);
3634 return WINED3DERR_INVALIDCALL;
3637 /* Dirtify the surface, but only if a key was changed */
3638 if(CKey) {
3639 switch (Flags & ~WINEDDCKEY_COLORSPACE) {
3640 case WINEDDCKEY_DESTBLT:
3641 This->DestBltCKey = *CKey;
3642 This->CKeyFlags |= WINEDDSD_CKDESTBLT;
3643 break;
3645 case WINEDDCKEY_DESTOVERLAY:
3646 This->DestOverlayCKey = *CKey;
3647 This->CKeyFlags |= WINEDDSD_CKDESTOVERLAY;
3648 break;
3650 case WINEDDCKEY_SRCOVERLAY:
3651 This->SrcOverlayCKey = *CKey;
3652 This->CKeyFlags |= WINEDDSD_CKSRCOVERLAY;
3653 break;
3655 case WINEDDCKEY_SRCBLT:
3656 This->SrcBltCKey = *CKey;
3657 This->CKeyFlags |= WINEDDSD_CKSRCBLT;
3658 break;
3661 else {
3662 switch (Flags & ~WINEDDCKEY_COLORSPACE) {
3663 case WINEDDCKEY_DESTBLT:
3664 This->CKeyFlags &= ~WINEDDSD_CKDESTBLT;
3665 break;
3667 case WINEDDCKEY_DESTOVERLAY:
3668 This->CKeyFlags &= ~WINEDDSD_CKDESTOVERLAY;
3669 break;
3671 case WINEDDCKEY_SRCOVERLAY:
3672 This->CKeyFlags &= ~WINEDDSD_CKSRCOVERLAY;
3673 break;
3675 case WINEDDCKEY_SRCBLT:
3676 This->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3677 break;
3681 return WINED3D_OK;
3684 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3685 /** Check against the maximum texture sizes supported by the video card **/
3686 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3687 unsigned int pow2Width, pow2Height;
3688 const GlPixelFormatDesc *glDesc;
3690 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3691 /* Setup some glformat defaults */
3692 This->glDescription.glFormat = glDesc->glFormat;
3693 This->glDescription.glFormatInternal = glDesc->glInternal;
3694 This->glDescription.glType = glDesc->glType;
3696 This->glDescription.textureName = 0;
3697 This->glDescription.target = GL_TEXTURE_2D;
3699 /* Non-power2 support */
3700 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO)) {
3701 pow2Width = This->currentDesc.Width;
3702 pow2Height = This->currentDesc.Height;
3703 } else {
3704 /* Find the nearest pow2 match */
3705 pow2Width = pow2Height = 1;
3706 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3707 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3709 This->pow2Width = pow2Width;
3710 This->pow2Height = pow2Height;
3712 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3713 WINED3DFORMAT Format = This->resource.format;
3714 /** TODO: add support for non power two compressed textures **/
3715 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3716 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5) {
3717 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3718 This, This->currentDesc.Width, This->currentDesc.Height);
3719 return WINED3DERR_NOTAVAILABLE;
3723 if(pow2Width != This->currentDesc.Width ||
3724 pow2Height != This->currentDesc.Height) {
3725 This->Flags |= SFLAG_NONPOW2;
3728 TRACE("%p\n", This);
3729 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3730 /* one of three options
3731 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)
3732 2: Set the texture to the maximum size (bad idea)
3733 3: WARN and return WINED3DERR_NOTAVAILABLE;
3734 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.
3736 WARN("(%p) Creating an oversized surface\n", This);
3737 This->Flags |= SFLAG_OVERSIZE;
3739 /* This will be initialized on the first blt */
3740 This->glRect.left = 0;
3741 This->glRect.top = 0;
3742 This->glRect.right = 0;
3743 This->glRect.bottom = 0;
3744 } else {
3745 /* No oversize, gl rect is the full texture size */
3746 This->Flags &= ~SFLAG_OVERSIZE;
3747 This->glRect.left = 0;
3748 This->glRect.top = 0;
3749 This->glRect.right = This->pow2Width;
3750 This->glRect.bottom = This->pow2Height;
3753 if(This->resource.allocatedMemory == NULL) {
3754 /* Make sure memory exists from the start, and it is initialized properly. D3D initializes surfaces,
3755 * gl does not, so we need to upload zeroes to init the gl texture.
3757 This->resource.allocatedMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->resource.size + 4);
3759 This->Flags |= SFLAG_INSYSMEM;
3761 return WINED3D_OK;
3764 DWORD WINAPI IWineD3DSurfaceImpl_GetPitch(IWineD3DSurface *iface) {
3765 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3766 DWORD ret;
3767 TRACE("(%p)\n", This);
3769 /* DXTn formats don't have exact pitches as they are to the new row of blocks,
3770 where each block is 4x4 pixels, 8 bytes (dxt1) and 16 bytes (dxt2/3/4/5)
3771 ie pitch = (width/4) * bytes per block */
3772 if (This->resource.format == WINED3DFMT_DXT1) /* DXT1 is 8 bytes per block */
3773 ret = ((This->currentDesc.Width + 3) >> 2) << 3;
3774 else if (This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
3775 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) /* DXT2/3/4/5 is 16 bytes per block */
3776 ret = ((This->currentDesc.Width + 3) >> 2) << 4;
3777 else {
3778 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
3779 ret = This->bytesPerPixel * This->currentDesc.Width; /* Bytes / row */
3780 ret = (ret + alignment - 1) & ~(alignment - 1);
3782 TRACE("(%p) Returning %d\n", This, ret);
3783 return ret;
3786 HRESULT WINAPI IWineD3DSurfaceImpl_SetOverlayPosition(IWineD3DSurface *iface, LONG X, LONG Y) {
3787 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3789 FIXME("(%p)->(%d,%d) Stub!\n", This, X, Y);
3791 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3793 TRACE("(%p): Not an overlay surface\n", This);
3794 return WINEDDERR_NOTAOVERLAYSURFACE;
3797 return WINED3D_OK;
3800 HRESULT WINAPI IWineD3DSurfaceImpl_GetOverlayPosition(IWineD3DSurface *iface, LONG *X, LONG *Y) {
3801 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3803 FIXME("(%p)->(%p,%p) Stub!\n", This, X, Y);
3805 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3807 TRACE("(%p): Not an overlay surface\n", This);
3808 return WINEDDERR_NOTAOVERLAYSURFACE;
3811 return WINED3D_OK;
3814 HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlayZOrder(IWineD3DSurface *iface, DWORD Flags, IWineD3DSurface *Ref) {
3815 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3816 IWineD3DSurfaceImpl *RefImpl = (IWineD3DSurfaceImpl *) Ref;
3818 FIXME("(%p)->(%08x,%p) Stub!\n", This, Flags, RefImpl);
3820 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3822 TRACE("(%p): Not an overlay surface\n", This);
3823 return WINEDDERR_NOTAOVERLAYSURFACE;
3826 return WINED3D_OK;
3829 HRESULT WINAPI IWineD3DSurfaceImpl_UpdateOverlay(IWineD3DSurface *iface, RECT *SrcRect, IWineD3DSurface *DstSurface, RECT *DstRect, DWORD Flags, WINEDDOVERLAYFX *FX) {
3830 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3831 IWineD3DSurfaceImpl *Dst = (IWineD3DSurfaceImpl *) DstSurface;
3832 FIXME("(%p)->(%p, %p, %p, %08x, %p)\n", This, SrcRect, Dst, DstRect, Flags, FX);
3834 if(!(This->resource.usage & WINED3DUSAGE_OVERLAY))
3836 TRACE("(%p): Not an overlay surface\n", This);
3837 return WINEDDERR_NOTAOVERLAYSURFACE;
3840 return WINED3D_OK;
3843 HRESULT WINAPI IWineD3DSurfaceImpl_SetClipper(IWineD3DSurface *iface, IWineD3DClipper *clipper)
3845 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3846 TRACE("(%p)->(%p)\n", This, clipper);
3848 This->clipper = clipper;
3849 return WINED3D_OK;
3852 HRESULT WINAPI IWineD3DSurfaceImpl_GetClipper(IWineD3DSurface *iface, IWineD3DClipper **clipper)
3854 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3855 TRACE("(%p)->(%p)\n", This, clipper);
3857 *clipper = This->clipper;
3858 if(*clipper) {
3859 IWineD3DClipper_AddRef(*clipper);
3861 return WINED3D_OK;
3864 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
3866 /* IUnknown */
3867 IWineD3DSurfaceImpl_QueryInterface,
3868 IWineD3DSurfaceImpl_AddRef,
3869 IWineD3DSurfaceImpl_Release,
3870 /* IWineD3DResource */
3871 IWineD3DSurfaceImpl_GetParent,
3872 IWineD3DSurfaceImpl_GetDevice,
3873 IWineD3DSurfaceImpl_SetPrivateData,
3874 IWineD3DSurfaceImpl_GetPrivateData,
3875 IWineD3DSurfaceImpl_FreePrivateData,
3876 IWineD3DSurfaceImpl_SetPriority,
3877 IWineD3DSurfaceImpl_GetPriority,
3878 IWineD3DSurfaceImpl_PreLoad,
3879 IWineD3DSurfaceImpl_GetType,
3880 /* IWineD3DSurface */
3881 IWineD3DSurfaceImpl_GetContainer,
3882 IWineD3DSurfaceImpl_GetDesc,
3883 IWineD3DSurfaceImpl_LockRect,
3884 IWineD3DSurfaceImpl_UnlockRect,
3885 IWineD3DSurfaceImpl_GetDC,
3886 IWineD3DSurfaceImpl_ReleaseDC,
3887 IWineD3DSurfaceImpl_Flip,
3888 IWineD3DSurfaceImpl_Blt,
3889 IWineD3DSurfaceImpl_GetBltStatus,
3890 IWineD3DSurfaceImpl_GetFlipStatus,
3891 IWineD3DSurfaceImpl_IsLost,
3892 IWineD3DSurfaceImpl_Restore,
3893 IWineD3DSurfaceImpl_BltFast,
3894 IWineD3DSurfaceImpl_GetPalette,
3895 IWineD3DSurfaceImpl_SetPalette,
3896 IWineD3DSurfaceImpl_RealizePalette,
3897 IWineD3DSurfaceImpl_SetColorKey,
3898 IWineD3DSurfaceImpl_GetPitch,
3899 IWineD3DSurfaceImpl_SetMem,
3900 IWineD3DSurfaceImpl_SetOverlayPosition,
3901 IWineD3DSurfaceImpl_GetOverlayPosition,
3902 IWineD3DSurfaceImpl_UpdateOverlayZOrder,
3903 IWineD3DSurfaceImpl_UpdateOverlay,
3904 IWineD3DSurfaceImpl_SetClipper,
3905 IWineD3DSurfaceImpl_GetClipper,
3906 /* Internal use: */
3907 IWineD3DSurfaceImpl_AddDirtyRect,
3908 IWineD3DSurfaceImpl_LoadTexture,
3909 IWineD3DSurfaceImpl_SaveSnapshot,
3910 IWineD3DSurfaceImpl_SetContainer,
3911 IWineD3DSurfaceImpl_SetGlTextureDesc,
3912 IWineD3DSurfaceImpl_GetGlDesc,
3913 IWineD3DSurfaceImpl_GetData,
3914 IWineD3DSurfaceImpl_SetFormat,
3915 IWineD3DSurfaceImpl_PrivateSetup