wined3d: Move texture->sysmem specific code into its special function.
[wine/multimedia.git] / dlls / wined3d / surface.c
blob41fd41ac17cf7868533de520eba431f09a7b9050
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-2007 Stefan Dösinger for CodeWeavers
11 * Copyright 2007 Henri Verbeet
12 * Copyright 2006-2007 Roderick Colenbrander
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
36 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *surf);
37 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey);
39 static void surface_download_data(IWineD3DSurfaceImpl *This) {
40 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
42 if (0 == This->glDescription.textureName) {
43 ERR("Surface does not have a texture, but SFLAG_INTEXTURE is set\n");
44 return;
47 if(myDevice->createParms.BehaviorFlags & WINED3DCREATE_MULTITHREADED) {
48 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
51 ENTER_GL();
52 /* Make sure that a proper texture unit is selected, bind the texture
53 * and dirtify the sampler to restore the texture on the next draw
55 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
56 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
57 checkGLcall("glActiveTextureARB");
59 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(0));
60 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
62 if (This->resource.format == WINED3DFMT_DXT1 ||
63 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
64 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
65 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
66 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
67 } else {
68 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
69 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
71 if(This->Flags & SFLAG_PBO) {
72 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
73 checkGLcall("glBindBufferARB");
74 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, NULL));
75 checkGLcall("glGetCompressedTexImageARB()");
76 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
77 checkGLcall("glBindBufferARB");
78 } else {
79 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
80 checkGLcall("glGetCompressedTexImageARB()");
83 LEAVE_GL();
84 } else {
85 void *mem;
86 int src_pitch = 0;
87 int dst_pitch = 0;
89 if(This->Flags & SFLAG_CONVERTED) {
90 FIXME("Read back converted textures unsupported\n");
91 LEAVE_GL();
92 return;
95 if (This->Flags & SFLAG_NONPOW2) {
96 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
97 src_pitch = This->bytesPerPixel * This->pow2Width;
98 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
99 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
100 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
101 } else {
102 mem = This->resource.allocatedMemory;
105 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
106 This->glDescription.glFormat, This->glDescription.glType, mem);
108 if(This->Flags & SFLAG_PBO) {
109 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
110 checkGLcall("glBindBufferARB");
112 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
113 This->glDescription.glType, NULL);
114 checkGLcall("glGetTexImage()");
116 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
117 checkGLcall("glBindBufferARB");
118 } else {
119 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
120 This->glDescription.glType, mem);
121 checkGLcall("glGetTexImage()");
123 LEAVE_GL();
125 if (This->Flags & SFLAG_NONPOW2) {
126 LPBYTE src_data, dst_data;
127 int y;
129 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
130 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
131 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
133 * We're doing this...
135 * instead of boxing the texture :
136 * |<-texture width ->| -->pow2width| /\
137 * |111111111111111111| | |
138 * |222 Texture 222222| boxed empty | texture height
139 * |3333 Data 33333333| | |
140 * |444444444444444444| | \/
141 * ----------------------------------- |
142 * | boxed empty | boxed empty | pow2height
143 * | | | \/
144 * -----------------------------------
147 * we're repacking the data to the expected texture width
149 * |<-texture width ->| -->pow2width| /\
150 * |111111111111111111222222222222222| |
151 * |222333333333333333333444444444444| texture height
152 * |444444 | |
153 * | | \/
154 * | | |
155 * | empty | pow2height
156 * | | \/
157 * -----------------------------------
159 * == is the same as
161 * |<-texture width ->| /\
162 * |111111111111111111|
163 * |222222222222222222|texture height
164 * |333333333333333333|
165 * |444444444444444444| \/
166 * --------------------
168 * this also means that any references to allocatedMemory should work with the data as if were a
169 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
171 * internally the texture is still stored in a boxed format so any references to textureName will
172 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
174 * Performance should not be an issue, because applications normally do not lock the surfaces when
175 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
176 * and doesn't have to be re-read.
178 src_data = mem;
179 dst_data = This->resource.allocatedMemory;
180 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
181 for (y = 1 ; y < This->currentDesc.Height; y++) {
182 /* skip the first row */
183 src_data += src_pitch;
184 dst_data += dst_pitch;
185 memcpy(dst_data, src_data, dst_pitch);
188 HeapFree(GetProcessHeap(), 0, mem);
192 /* Surface has now been downloaded */
193 This->Flags |= SFLAG_INSYSMEM;
196 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
197 if (This->resource.format == WINED3DFMT_DXT1 ||
198 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
199 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
200 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
201 FIXME("Using DXT1/3/5 without advertized support\n");
202 } else {
203 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
204 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
205 This->Flags |= SFLAG_CLIENT;
208 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
209 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
210 * function uses glCompressedTexImage2D instead of the SubImage call
212 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
213 ENTER_GL();
215 if(This->Flags & SFLAG_PBO) {
216 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
217 checkGLcall("glBindBufferARB");
218 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
220 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
221 width, height, 0 /* border */, This->resource.size, NULL));
222 checkGLcall("glCompressedTexSubImage2D");
224 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
225 checkGLcall("glBindBufferARB");
226 } else {
227 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
228 width, height, 0 /* border */, This->resource.size, data));
229 checkGLcall("glCompressedTexSubImage2D");
231 LEAVE_GL();
233 } else {
234 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
235 ENTER_GL();
237 if(This->Flags & SFLAG_PBO) {
238 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
239 checkGLcall("glBindBufferARB");
240 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
242 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
243 checkGLcall("glTexSubImage2D");
245 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
246 checkGLcall("glBindBufferARB");
248 else {
249 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
250 checkGLcall("glTexSubImage2D");
253 LEAVE_GL();
257 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
258 BOOL enable_client_storage = FALSE;
260 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,
261 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
263 if (This->resource.format == WINED3DFMT_DXT1 ||
264 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
265 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
266 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
267 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
268 return;
271 ENTER_GL();
273 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
274 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
275 /* In some cases we want to disable client storage.
276 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
277 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
278 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
279 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
280 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
282 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
283 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
284 This->Flags &= ~SFLAG_CLIENT;
285 enable_client_storage = TRUE;
286 } else {
287 This->Flags |= SFLAG_CLIENT;
288 /* Below point opengl to our allocated texture memory */
291 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type,
292 This->Flags & SFLAG_CLIENT ? This->resource.allocatedMemory : NULL);
293 checkGLcall("glTexImage2D");
295 if(enable_client_storage) {
296 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
297 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
299 LEAVE_GL();
301 This->Flags |= SFLAG_ALLOCATED;
304 /* In D3D the depth stencil dimensions have to be greater than or equal to the
305 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
306 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
307 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
308 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
309 renderbuffer_entry_t *entry;
310 GLuint renderbuffer = 0;
311 unsigned int src_width, src_height;
313 src_width = This->pow2Width;
314 src_height = This->pow2Height;
316 /* A depth stencil smaller than the render target is not valid */
317 if (width > src_width || height > src_height) return;
319 /* Remove any renderbuffer set if the sizes match */
320 if (width == src_width && height == src_height) {
321 This->current_renderbuffer = NULL;
322 return;
325 /* Look if we've already got a renderbuffer of the correct dimensions */
326 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
327 if (entry->width == width && entry->height == height) {
328 renderbuffer = entry->id;
329 This->current_renderbuffer = entry;
330 break;
334 if (!renderbuffer) {
335 const GlPixelFormatDesc *glDesc;
336 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
338 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
339 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
340 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, glDesc->glFormat, width, height));
342 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
343 entry->width = width;
344 entry->height = height;
345 entry->id = renderbuffer;
346 list_add_head(&This->renderbuffers, &entry->entry);
348 This->current_renderbuffer = entry;
351 checkGLcall("set_compatible_renderbuffer");
354 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
355 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
356 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
358 TRACE("(%p) : swapchain %p\n", This, swapchain);
360 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
361 TRACE("Returning GL_BACK\n");
362 return GL_BACK;
363 } else if (swapchain_impl->frontBuffer == iface) {
364 TRACE("Returning GL_FRONT\n");
365 return GL_FRONT;
368 FIXME("Higher back buffer, returning GL_BACK\n");
369 return GL_BACK;
372 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
373 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
374 ULONG ref = InterlockedDecrement(&This->resource.ref);
375 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
376 if (ref == 0) {
377 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) This->resource.wineD3DDevice;
378 renderbuffer_entry_t *entry, *entry2;
379 TRACE("(%p) : cleaning up\n", This);
381 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
383 /* Need a context to destroy the texture. Use the currently active render target, but only if
384 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
385 * When destroying the primary rt, Uninit3D will activate a context before doing anything
387 if(device->render_targets && device->render_targets[0]) {
388 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
391 TRACE("Deleting texture %d\n", This->glDescription.textureName);
392 ENTER_GL();
393 glDeleteTextures(1, &This->glDescription.textureName);
394 LEAVE_GL();
397 if(This->Flags & SFLAG_PBO) {
398 /* Delete the PBO */
399 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
402 if(This->Flags & SFLAG_DIBSECTION) {
403 /* Release the DC */
404 SelectObject(This->hDC, This->dib.holdbitmap);
405 DeleteDC(This->hDC);
406 /* Release the DIB section */
407 DeleteObject(This->dib.DIBsection);
408 This->dib.bitmap_data = NULL;
409 This->resource.allocatedMemory = NULL;
411 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
413 HeapFree(GetProcessHeap(), 0, This->palette9);
415 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
416 if(iface == device->ddraw_primary)
417 device->ddraw_primary = NULL;
419 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
420 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
421 HeapFree(GetProcessHeap(), 0, entry);
424 TRACE("(%p) Released\n", This);
425 HeapFree(GetProcessHeap(), 0, This);
428 return ref;
431 /* ****************************************************
432 IWineD3DSurface IWineD3DResource parts follow
433 **************************************************** */
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 /* ******************************************************
481 IWineD3DSurface IWineD3DSurface parts follow
482 ****************************************************** */
484 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
485 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
486 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
487 if (This->glDescription.textureName == 0 && textureName != 0) {
488 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
489 IWineD3DSurface_AddDirtyRect(iface, NULL);
491 This->glDescription.textureName = textureName;
492 This->glDescription.target = target;
493 This->Flags &= ~SFLAG_ALLOCATED;
496 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
497 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
498 TRACE("(%p) : returning %p\n", This, &This->glDescription);
499 *glDescription = &This->glDescription;
502 /* TODO: think about moving this down to resource? */
503 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
504 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
505 /* 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 */
506 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
507 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
509 return (CONST void*)(This->resource.allocatedMemory);
512 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
513 IWineD3DSwapChainImpl *swapchain;
514 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
515 BYTE *mem;
516 GLint fmt;
517 GLint type;
518 BYTE *row, *top, *bottom;
519 int i;
520 BOOL bpp;
521 RECT local_rect;
522 BOOL srcIsUpsideDown;
524 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
525 static BOOL warned = FALSE;
526 if(!warned) {
527 ERR("The application tries to lock the render target, but render target locking is disabled\n");
528 warned = TRUE;
530 return;
533 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
534 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
535 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
536 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
537 * context->last_was_blit set on the unlock.
539 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
540 ENTER_GL();
542 /* Select the correct read buffer, and give some debug output.
543 * There is no need to keep track of the current read buffer or reset it, every part of the code
544 * that reads sets the read buffer as desired.
546 if(!swapchain) {
547 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
548 * Read from the back buffer
550 TRACE("Locking offscreen render target\n");
551 glReadBuffer(myDevice->offscreenBuffer);
552 srcIsUpsideDown = TRUE;
553 } else {
554 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
555 TRACE("Locking %#x buffer\n", buffer);
556 glReadBuffer(buffer);
557 checkGLcall("glReadBuffer");
559 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
560 srcIsUpsideDown = FALSE;
563 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
564 if(!rect) {
565 local_rect.left = 0;
566 local_rect.top = 0;
567 local_rect.right = This->currentDesc.Width;
568 local_rect.bottom = This->currentDesc.Height;
569 } else {
570 local_rect = *rect;
572 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
574 switch(This->resource.format)
576 case WINED3DFMT_P8:
578 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
579 /* In case of P8 render targets the index is stored in the alpha component */
580 fmt = GL_ALPHA;
581 type = GL_UNSIGNED_BYTE;
582 mem = dest;
583 bpp = This->bytesPerPixel;
584 } else {
585 /* GL can't return palettized data, so read ARGB pixels into a
586 * separate block of memory and convert them into palettized format
587 * in software. Slow, but if the app means to use palettized render
588 * targets and locks it...
590 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
591 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
592 * for the color channels when palettizing the colors.
594 fmt = GL_RGB;
595 type = GL_UNSIGNED_BYTE;
596 pitch *= 3;
597 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
598 if(!mem) {
599 ERR("Out of memory\n");
600 LEAVE_GL();
601 return;
603 bpp = This->bytesPerPixel * 3;
606 break;
608 default:
609 mem = dest;
610 fmt = This->glDescription.glFormat;
611 type = This->glDescription.glType;
612 bpp = This->bytesPerPixel;
615 if(This->Flags & SFLAG_PBO) {
616 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
617 checkGLcall("glBindBufferARB");
620 glReadPixels(local_rect.left, local_rect.top,
621 local_rect.right - local_rect.left,
622 local_rect.bottom - local_rect.top,
623 fmt, type, mem);
624 vcheckGLcall("glReadPixels");
626 if(This->Flags & SFLAG_PBO) {
627 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
628 checkGLcall("glBindBufferARB");
630 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
631 * to get a pointer to it and perform the flipping in software. This is a lot
632 * faster than calling glReadPixels for each line. In case we want more speed
633 * we should rerender it flipped in a FBO and read the data back from the FBO. */
634 if(!srcIsUpsideDown) {
635 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
636 checkGLcall("glBindBufferARB");
638 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
639 checkGLcall("glMapBufferARB");
643 /* TODO: Merge this with the palettization loop below for P8 targets */
644 if(!srcIsUpsideDown) {
645 UINT len, off;
646 /* glReadPixels returns the image upside down, and there is no way to prevent this.
647 Flip the lines in software */
648 len = (local_rect.right - local_rect.left) * bpp;
649 off = local_rect.left * bpp;
651 row = HeapAlloc(GetProcessHeap(), 0, len);
652 if(!row) {
653 ERR("Out of memory\n");
654 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
655 LEAVE_GL();
656 return;
659 top = mem + pitch * local_rect.top;
660 bottom = ((BYTE *) mem) + pitch * ( local_rect.bottom - local_rect.top - 1);
661 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
662 memcpy(row, top + off, len);
663 memcpy(top + off, bottom + off, len);
664 memcpy(bottom + off, row, len);
665 top += pitch;
666 bottom -= pitch;
668 HeapFree(GetProcessHeap(), 0, row);
670 /* Unmap the temp PBO buffer */
671 if(This->Flags & SFLAG_PBO) {
672 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
673 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
677 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
678 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
679 * the same color but we have no choice.
680 * In case of render targets, the index is stored in the alpha component so no conversion is needed.
682 if((This->resource.format == WINED3DFMT_P8) && !(This->resource.usage & WINED3DUSAGE_RENDERTARGET)) {
683 PALETTEENTRY *pal;
684 DWORD width = pitch / 3;
685 int x, y, c;
686 if(This->palette) {
687 pal = This->palette->palents;
688 } else {
689 pal = This->resource.wineD3DDevice->palettes[This->resource.wineD3DDevice->currentPalette];
692 for(y = local_rect.top; y < local_rect.bottom; y++) {
693 for(x = local_rect.left; x < local_rect.right; x++) {
694 /* start lines pixels */
695 BYTE *blue = (BYTE *) ((BYTE *) mem) + y * pitch + x * (sizeof(BYTE) * 3);
696 BYTE *green = blue + 1;
697 BYTE *red = green + 1;
699 for(c = 0; c < 256; c++) {
700 if(*red == pal[c].peRed &&
701 *green == pal[c].peGreen &&
702 *blue == pal[c].peBlue)
704 *((BYTE *) dest + y * width + x) = c;
705 break;
710 HeapFree(GetProcessHeap(), 0, mem);
712 LEAVE_GL();
715 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
716 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
717 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
718 * changed
720 if(!(This->Flags & SFLAG_DYNLOCK)) {
721 This->lockCount++;
722 /* MAXLOCKCOUNT is defined in wined3d_private.h */
723 if(This->lockCount > MAXLOCKCOUNT) {
724 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
725 This->Flags |= SFLAG_DYNLOCK;
729 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
730 * Also don't create a PBO for systemmem surfaces.
732 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
733 GLenum error;
734 ENTER_GL();
736 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
737 error = glGetError();
738 if(This->pbo == 0 || error != GL_NO_ERROR) {
739 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
742 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
744 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
745 checkGLcall("glBindBufferARB");
747 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
748 checkGLcall("glBufferDataARB");
750 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
751 checkGLcall("glBindBufferARB");
753 /* We don't need the system memory anymore and we can't even use it for PBOs */
754 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
755 This->resource.allocatedMemory = NULL;
756 This->resource.heapMemory = NULL;
757 This->Flags |= SFLAG_PBO;
758 LEAVE_GL();
759 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
760 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
761 * or a pbo to map
763 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
764 This->resource.allocatedMemory =
765 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
766 if(This->Flags & SFLAG_INSYSMEM) {
767 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
772 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
773 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
774 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
775 IWineD3DSwapChain *swapchain = NULL;
777 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
779 /* This is also done in the base class, but we have to verify this before loading any data from
780 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
781 * may interfere, and all other bad things may happen
783 if (This->Flags & SFLAG_LOCKED) {
784 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
785 return WINED3DERR_INVALIDCALL;
788 if (Flags & WINED3DLOCK_DISCARD) {
789 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
790 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
791 This->Flags |= SFLAG_INSYSMEM;
794 if (This->Flags & SFLAG_INSYSMEM) {
795 TRACE("Local copy is up to date, not downloading data\n");
796 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
797 goto lock_end;
800 /* Now download the surface content from opengl
801 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
802 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
804 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
805 if(swapchain || iface == myDevice->render_targets[0]) {
806 const RECT *pass_rect = pRect;
808 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
809 * because most caller functions do not need that. So do that here
811 if(pRect &&
812 pRect->top == 0 &&
813 pRect->left == 0 &&
814 pRect->right == This->currentDesc.Width &&
815 pRect->bottom == This->currentDesc.Height) {
816 pass_rect = NULL;
819 switch(wined3d_settings.rendertargetlock_mode) {
820 case RTL_TEXDRAW:
821 case RTL_TEXTEX:
822 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
823 #if 0
824 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
825 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
826 * This may be faster on some cards
828 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
829 #endif
830 /* drop through */
832 case RTL_AUTO:
833 case RTL_READDRAW:
834 case RTL_READTEX:
835 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pRect);
836 break;
838 case RTL_DISABLE:
839 break;
841 if(swapchain) IWineD3DSwapChain_Release(swapchain);
843 } else if(iface == myDevice->stencilBufferTarget) {
844 /** the depth stencil in openGL has a format of GL_FLOAT
845 * which should be good for WINED3DFMT_D16_LOCKABLE
846 * and WINED3DFMT_D16
847 * it is unclear what format the stencil buffer is in except.
848 * 'Each index is converted to fixed point...
849 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
850 * mappings in the table GL_PIXEL_MAP_S_TO_S.
851 * glReadPixels(This->lockedRect.left,
852 * This->lockedRect.bottom - j - 1,
853 * This->lockedRect.right - This->lockedRect.left,
854 * 1,
855 * GL_DEPTH_COMPONENT,
856 * type,
857 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
859 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
860 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
861 * none of that is the case the problem is not in this function :-)
862 ********************************************/
863 FIXME("Depth stencil locking not supported yet\n");
864 } else {
865 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
866 TRACE("locking an ordinary surface\n");
867 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
870 lock_end:
871 if(This->Flags & SFLAG_PBO) {
872 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
873 ENTER_GL();
874 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
875 checkGLcall("glBindBufferARB");
877 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
878 if(This->resource.allocatedMemory) {
879 ERR("The surface already has PBO memory allocated!\n");
882 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
883 checkGLcall("glMapBufferARB");
885 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
886 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
887 checkGLcall("glBindBufferARB");
889 LEAVE_GL();
892 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
893 /* Don't dirtify */
894 } else {
895 IWineD3DBaseTexture *pBaseTexture;
897 * Dirtify on lock
898 * as seen in msdn docs
900 IWineD3DSurface_AddDirtyRect(iface, &This->lockedRect);
902 /** Dirtify Container if needed */
903 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
904 TRACE("Making container dirty\n");
905 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
906 IWineD3DBaseTexture_Release(pBaseTexture);
907 } else {
908 TRACE("Surface is standalone, no need to dirty the container\n");
912 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
915 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This) {
916 GLint prev_store;
917 GLint prev_rasterpos[4];
918 GLint skipBytes = 0;
919 BOOL storechanged = FALSE, memory_allocated = FALSE;
920 GLint fmt, type;
921 BYTE *mem;
922 UINT bpp;
923 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
924 IWineD3DDeviceImpl *myDevice = (IWineD3DDeviceImpl *) This->resource.wineD3DDevice;
925 IWineD3DSwapChainImpl *swapchain;
927 /* Activate the correct context for the render target */
928 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
929 ENTER_GL();
931 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
932 if(!swapchain) {
933 /* Primary offscreen render target */
934 TRACE("Offscreen render target\n");
935 glDrawBuffer(myDevice->offscreenBuffer);
936 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
937 } else {
938 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
939 TRACE("Unlocking %#x buffer\n", buffer);
940 glDrawBuffer(buffer);
941 checkGLcall("glDrawBuffer");
943 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
946 glDisable(GL_TEXTURE_2D);
947 vcheckGLcall("glDisable(GL_TEXTURE_2D)");
949 glFlush();
950 vcheckGLcall("glFlush");
951 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
952 vcheckGLcall("glIntegerv");
953 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
954 vcheckGLcall("glIntegerv");
955 glPixelZoom(1.0, -1.0);
956 vcheckGLcall("glPixelZoom");
958 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
959 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
960 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
962 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
963 vcheckGLcall("glRasterPos2f");
965 /* Some drivers(radeon dri, others?) don't like exceptions during
966 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
967 * after ReleaseDC. Reading it will cause an exception, which x11drv will
968 * catch to put the dib section in InSync mode, which leads to a crash
969 * and a blocked x server on my radeon card.
971 * The following lines read the dib section so it is put in inSync mode
972 * before glDrawPixels is called and the crash is prevented. There won't
973 * be any interfering gdi accesses, because UnlockRect is called from
974 * ReleaseDC, and the app won't use the dc any more afterwards.
976 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
977 volatile BYTE read;
978 read = This->resource.allocatedMemory[0];
981 switch (This->resource.format) {
982 /* No special care needed */
983 case WINED3DFMT_A4R4G4B4:
984 case WINED3DFMT_R5G6B5:
985 case WINED3DFMT_A1R5G5B5:
986 case WINED3DFMT_R8G8B8:
987 type = This->glDescription.glType;
988 fmt = This->glDescription.glFormat;
989 mem = This->resource.allocatedMemory;
990 bpp = This->bytesPerPixel;
991 break;
993 case WINED3DFMT_X4R4G4B4:
995 int size;
996 unsigned short *data;
997 data = (unsigned short *)This->resource.allocatedMemory;
998 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
999 while(size > 0) {
1000 *data |= 0xF000;
1001 data++;
1002 size--;
1004 type = This->glDescription.glType;
1005 fmt = This->glDescription.glFormat;
1006 mem = This->resource.allocatedMemory;
1007 bpp = This->bytesPerPixel;
1009 break;
1011 case WINED3DFMT_X1R5G5B5:
1013 int size;
1014 unsigned short *data;
1015 data = (unsigned short *)This->resource.allocatedMemory;
1016 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
1017 while(size > 0) {
1018 *data |= 0x8000;
1019 data++;
1020 size--;
1022 type = This->glDescription.glType;
1023 fmt = This->glDescription.glFormat;
1024 mem = This->resource.allocatedMemory;
1025 bpp = This->bytesPerPixel;
1027 break;
1029 case WINED3DFMT_X8R8G8B8:
1031 /* make sure the X byte is set to alpha on, since it
1032 could be any random value. This fixes the intro movie in Pirates! */
1033 int size;
1034 unsigned int *data;
1035 data = (unsigned int *)This->resource.allocatedMemory;
1036 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
1037 while(size > 0) {
1038 *data |= 0xFF000000;
1039 data++;
1040 size--;
1043 /* Fall through */
1045 case WINED3DFMT_A8R8G8B8:
1047 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
1048 vcheckGLcall("glPixelStorei");
1049 storechanged = TRUE;
1050 type = This->glDescription.glType;
1051 fmt = This->glDescription.glFormat;
1052 mem = This->resource.allocatedMemory;
1053 bpp = This->bytesPerPixel;
1055 break;
1057 case WINED3DFMT_A2R10G10B10:
1059 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
1060 vcheckGLcall("glPixelStorei");
1061 storechanged = TRUE;
1062 type = This->glDescription.glType;
1063 fmt = This->glDescription.glFormat;
1064 mem = This->resource.allocatedMemory;
1065 bpp = This->bytesPerPixel;
1067 break;
1069 case WINED3DFMT_P8:
1071 int height = This->glRect.bottom - This->glRect.top;
1072 type = GL_UNSIGNED_BYTE;
1073 fmt = GL_RGBA;
1075 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * sizeof(DWORD));
1076 if(!mem) {
1077 ERR("Out of memory\n");
1078 goto cleanup;
1080 memory_allocated = TRUE;
1081 d3dfmt_convert_surface(This->resource.allocatedMemory,
1082 mem,
1083 pitch,
1084 pitch,
1085 height,
1086 pitch * 4,
1087 CONVERT_PALETTED,
1088 This);
1089 bpp = This->bytesPerPixel * 4;
1090 pitch *= 4;
1092 break;
1094 default:
1095 FIXME("Unsupported Format %u in locking func\n", This->resource.format);
1097 /* Give it a try */
1098 type = This->glDescription.glType;
1099 fmt = This->glDescription.glFormat;
1100 mem = This->resource.allocatedMemory;
1101 bpp = This->bytesPerPixel;
1104 if(This->Flags & SFLAG_PBO) {
1105 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1106 checkGLcall("glBindBufferARB");
1109 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1110 (This->lockedRect.bottom - This->lockedRect.top)-1,
1111 fmt, type,
1112 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1113 checkGLcall("glDrawPixels");
1115 cleanup:
1116 if(This->Flags & SFLAG_PBO) {
1117 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1118 checkGLcall("glBindBufferARB");
1121 glPixelZoom(1.0,1.0);
1122 vcheckGLcall("glPixelZoom");
1124 glRasterPos3iv(&prev_rasterpos[0]);
1125 vcheckGLcall("glRasterPos3iv");
1127 /* Reset to previous pack row length */
1128 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1129 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1130 if(storechanged) {
1131 glPixelStorei(GL_PACK_SWAP_BYTES, prev_store);
1132 vcheckGLcall("glPixelStorei GL_PACK_SWAP_BYTES");
1135 /* Blitting environment requires that 2D texturing is enabled. It was turned off before,
1136 * turn it on again
1138 glEnable(GL_TEXTURE_2D);
1139 checkGLcall("glEnable(GL_TEXTURE_2D)");
1141 if(memory_allocated) HeapFree(GetProcessHeap(), 0, mem);
1143 if(!swapchain) {
1144 glDrawBuffer(myDevice->offscreenBuffer);
1145 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1146 } else if(swapchain->backBuffer) {
1147 glDrawBuffer(GL_BACK);
1148 checkGLcall("glDrawBuffer(GL_BACK)");
1149 } else {
1150 glDrawBuffer(GL_FRONT);
1151 checkGLcall("glDrawBuffer(GL_FRONT)");
1153 LEAVE_GL();
1155 return;
1158 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1159 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1160 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1161 IWineD3DSwapChainImpl *swapchain = NULL;
1162 BOOL fullsurface;
1164 if (!(This->Flags & SFLAG_LOCKED)) {
1165 WARN("trying to Unlock an unlocked surf@%p\n", This);
1166 return WINED3DERR_INVALIDCALL;
1169 if (This->Flags & SFLAG_PBO) {
1170 TRACE("Freeing PBO memory\n");
1171 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1172 ENTER_GL();
1173 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1174 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1175 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1176 checkGLcall("glUnmapBufferARB");
1177 LEAVE_GL();
1178 This->resource.allocatedMemory = NULL;
1181 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1183 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1184 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1185 goto unlock_end;
1188 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1189 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1190 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1191 static BOOL warned = FALSE;
1192 if(!warned) {
1193 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1194 warned = TRUE;
1196 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1197 goto unlock_end;
1200 if(This->dirtyRect.left == 0 &&
1201 This->dirtyRect.top == 0 &&
1202 This->dirtyRect.right == This->currentDesc.Width &&
1203 This->dirtyRect.bottom == This->currentDesc.Height) {
1204 fullsurface = TRUE;
1205 } else {
1206 /* TODO: Proper partial rectangle tracking */
1207 fullsurface = FALSE;
1208 This->Flags |= SFLAG_INSYSMEM;
1211 switch(wined3d_settings.rendertargetlock_mode) {
1212 case RTL_READTEX:
1213 case RTL_TEXTEX:
1214 ENTER_GL();
1215 if (This->glDescription.textureName == 0) {
1216 glGenTextures(1, &This->glDescription.textureName);
1217 checkGLcall("glGenTextures");
1219 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
1220 checkGLcall("glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);");
1221 LEAVE_GL();
1222 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1223 /* drop through */
1225 case RTL_AUTO:
1226 case RTL_READDRAW:
1227 case RTL_TEXDRAW:
1228 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1229 break;
1232 if(!fullsurface) {
1233 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1234 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1235 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1236 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1237 * not fully up to date because only a subrectangle was read in LockRect.
1239 This->Flags &= ~SFLAG_INSYSMEM;
1240 This->Flags |= SFLAG_INDRAWABLE;
1243 This->dirtyRect.left = This->currentDesc.Width;
1244 This->dirtyRect.top = This->currentDesc.Height;
1245 This->dirtyRect.right = 0;
1246 This->dirtyRect.bottom = 0;
1247 } else if(iface == myDevice->stencilBufferTarget) {
1248 FIXME("Depth Stencil buffer locking is not implemented\n");
1249 } else {
1250 /* The rest should be a normal texture */
1251 IWineD3DBaseTextureImpl *impl;
1252 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1253 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1254 * states need resetting
1256 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1257 if(impl->baseTexture.bindCount) {
1258 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1260 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1264 unlock_end:
1265 This->Flags &= ~SFLAG_LOCKED;
1266 memset(&This->lockedRect, 0, sizeof(RECT));
1267 return WINED3D_OK;
1270 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1271 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1272 WINED3DLOCKED_RECT lock;
1273 HRESULT hr;
1274 RGBQUAD col[256];
1276 TRACE("(%p)->(%p)\n",This,pHDC);
1278 if(This->Flags & SFLAG_USERPTR) {
1279 ERR("Not supported on surfaces with an application-provided surfaces\n");
1280 return WINEDDERR_NODC;
1283 /* Give more detailed info for ddraw */
1284 if (This->Flags & SFLAG_DCINUSE)
1285 return WINEDDERR_DCALREADYCREATED;
1287 /* Can't GetDC if the surface is locked */
1288 if (This->Flags & SFLAG_LOCKED)
1289 return WINED3DERR_INVALIDCALL;
1291 memset(&lock, 0, sizeof(lock)); /* To be sure */
1293 /* Create a DIB section if there isn't a hdc yet */
1294 if(!This->hDC) {
1295 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1296 if(This->Flags & SFLAG_CLIENT) {
1297 IWineD3DSurface_PreLoad(iface);
1300 /* Use the dib section from now on if we are not using a PBO */
1301 if(!(This->Flags & SFLAG_PBO))
1302 This->resource.allocatedMemory = This->dib.bitmap_data;
1305 /* Lock the surface */
1306 hr = IWineD3DSurface_LockRect(iface,
1307 &lock,
1308 NULL,
1311 if(This->Flags & SFLAG_PBO) {
1312 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1313 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1316 if(FAILED(hr)) {
1317 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1318 /* keep the dib section */
1319 return hr;
1322 if(This->resource.format == WINED3DFMT_P8 ||
1323 This->resource.format == WINED3DFMT_A8P8) {
1324 unsigned int n;
1325 if(This->palette) {
1326 PALETTEENTRY ent[256];
1328 GetPaletteEntries(This->palette->hpal, 0, 256, ent);
1329 for (n=0; n<256; n++) {
1330 col[n].rgbRed = ent[n].peRed;
1331 col[n].rgbGreen = ent[n].peGreen;
1332 col[n].rgbBlue = ent[n].peBlue;
1333 col[n].rgbReserved = 0;
1335 } else {
1336 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1338 for (n=0; n<256; n++) {
1339 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
1340 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
1341 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
1342 col[n].rgbReserved = 0;
1346 SetDIBColorTable(This->hDC, 0, 256, col);
1349 *pHDC = This->hDC;
1350 TRACE("returning %p\n",*pHDC);
1351 This->Flags |= SFLAG_DCINUSE;
1353 return WINED3D_OK;
1356 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1357 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1359 TRACE("(%p)->(%p)\n",This,hDC);
1361 if (!(This->Flags & SFLAG_DCINUSE))
1362 return WINED3DERR_INVALIDCALL;
1364 if (This->hDC !=hDC) {
1365 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1366 return WINED3DERR_INVALIDCALL;
1369 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1370 /* Copy the contents of the DIB over to the PBO */
1371 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1374 /* we locked first, so unlock now */
1375 IWineD3DSurface_UnlockRect(iface);
1377 This->Flags &= ~SFLAG_DCINUSE;
1379 return WINED3D_OK;
1382 /* ******************************************************
1383 IWineD3DSurface Internal (No mapping to directx api) parts follow
1384 ****************************************************** */
1386 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) {
1387 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1388 const GlPixelFormatDesc *glDesc;
1389 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1391 /* Default values: From the surface */
1392 *format = glDesc->glFormat;
1393 *internal = srgb_mode?glDesc->glGammaInternal:glDesc->glInternal;
1394 *type = glDesc->glType;
1395 *convert = NO_CONVERSION;
1396 *target_bpp = This->bytesPerPixel;
1398 /* Ok, now look if we have to do any conversion */
1399 switch(This->resource.format) {
1400 case WINED3DFMT_P8:
1401 /* ****************
1402 Paletted Texture
1403 **************** */
1404 /* Use conversion when the paletted texture extension is not available, or when it is available make sure it is used
1405 * for texturing as it won't work for calls like glDraw-/glReadPixels and further also use conversion in case of color keying.
1407 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) || colorkey_active || (!use_texturing && GL_SUPPORT(EXT_PALETTED_TEXTURE)) ) {
1408 *format = GL_RGBA;
1409 *internal = GL_RGBA;
1410 *type = GL_UNSIGNED_BYTE;
1411 *target_bpp = 4;
1412 if(colorkey_active) {
1413 *convert = CONVERT_PALETTED_CK;
1414 } else {
1415 *convert = CONVERT_PALETTED;
1418 else if(GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1419 *format = GL_RED;
1420 *internal = GL_RGBA;
1421 *type = GL_UNSIGNED_BYTE;
1422 *target_bpp = 1;
1425 break;
1427 case WINED3DFMT_R3G3B2:
1428 /* **********************
1429 GL_UNSIGNED_BYTE_3_3_2
1430 ********************** */
1431 if (colorkey_active) {
1432 /* This texture format will never be used.. So do not care about color keying
1433 up until the point in time it will be needed :-) */
1434 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1436 break;
1438 case WINED3DFMT_R5G6B5:
1439 if (colorkey_active) {
1440 *convert = CONVERT_CK_565;
1441 *format = GL_RGBA;
1442 *internal = GL_RGBA;
1443 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1445 break;
1447 case WINED3DFMT_X1R5G5B5:
1448 if (colorkey_active) {
1449 *convert = CONVERT_CK_5551;
1450 *format = GL_BGRA;
1451 *internal = GL_RGBA;
1452 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1454 break;
1456 case WINED3DFMT_R8G8B8:
1457 if (colorkey_active) {
1458 *convert = CONVERT_CK_RGB24;
1459 *format = GL_RGBA;
1460 *internal = GL_RGBA;
1461 *type = GL_UNSIGNED_INT_8_8_8_8;
1462 *target_bpp = 4;
1464 break;
1466 case WINED3DFMT_X8R8G8B8:
1467 if (colorkey_active) {
1468 *convert = CONVERT_RGB32_888;
1469 *format = GL_RGBA;
1470 *internal = GL_RGBA;
1471 *type = GL_UNSIGNED_INT_8_8_8_8;
1473 break;
1475 case WINED3DFMT_V8U8:
1476 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1477 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1478 *format = GL_DUDV_ATI;
1479 *internal = GL_DU8DV8_ATI;
1480 *type = GL_BYTE;
1481 /* No conversion - Just change the gl type */
1482 break;
1484 *convert = CONVERT_V8U8;
1485 *format = GL_BGR;
1486 *internal = GL_RGB8;
1487 *type = GL_UNSIGNED_BYTE;
1488 *target_bpp = 3;
1489 break;
1491 case WINED3DFMT_L6V5U5:
1492 *convert = CONVERT_L6V5U5;
1493 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1494 *target_bpp = 3;
1495 /* Use format and types from table */
1496 } else {
1497 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1498 *target_bpp = 2;
1499 *format = GL_RGB;
1500 *internal = GL_RGB5;
1501 *type = GL_UNSIGNED_SHORT_5_6_5;
1503 break;
1505 case WINED3DFMT_X8L8V8U8:
1506 *convert = CONVERT_X8L8V8U8;
1507 *target_bpp = 4;
1508 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1509 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1510 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1511 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1512 * the needed type and format parameter, so the internal format contains a
1513 * 4th component, which is returned as alpha
1515 } else {
1516 /* Not supported by GL_ATI_envmap_bumpmap */
1517 *format = GL_BGRA;
1518 *internal = GL_RGB8;
1519 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1521 break;
1523 case WINED3DFMT_Q8W8V8U8:
1524 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1525 *convert = CONVERT_Q8W8V8U8;
1526 *format = GL_BGRA;
1527 *internal = GL_RGBA8;
1528 *type = GL_UNSIGNED_BYTE;
1529 *target_bpp = 4;
1530 /* Not supported by GL_ATI_envmap_bumpmap */
1531 break;
1533 case WINED3DFMT_V16U16:
1534 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1535 *convert = CONVERT_V16U16;
1536 *format = GL_BGR;
1537 *internal = GL_RGB16_EXT;
1538 *type = GL_UNSIGNED_SHORT;
1539 *target_bpp = 6;
1540 /* What should I do here about GL_ATI_envmap_bumpmap?
1541 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1543 break;
1545 case WINED3DFMT_A4L4:
1546 /* A4L4 exists as an internal gl format, but for some reason there is not
1547 * format+type combination to load it. Thus convert it to A8L8, then load it
1548 * with A4L4 internal, but A8L8 format+type
1550 *convert = CONVERT_A4L4;
1551 *format = GL_LUMINANCE_ALPHA;
1552 *internal = GL_LUMINANCE4_ALPHA4;
1553 *type = GL_UNSIGNED_BYTE;
1554 *target_bpp = 2;
1555 break;
1557 case WINED3DFMT_R32F:
1558 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1559 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1560 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1561 * 1.0 instead.
1563 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1565 *convert = CONVERT_R32F;
1566 *format = GL_RGB;
1567 *internal = GL_RGB32F_ARB;
1568 *type = GL_FLOAT;
1569 *target_bpp = 12;
1570 break;
1572 case WINED3DFMT_R16F:
1573 /* Similar to R32F */
1574 *convert = CONVERT_R16F;
1575 *format = GL_RGB;
1576 *internal = GL_RGB16F_ARB;
1577 *type = GL_HALF_FLOAT_ARB;
1578 *target_bpp = 6;
1579 break;
1581 default:
1582 break;
1585 return WINED3D_OK;
1588 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This) {
1589 BYTE *source, *dest;
1590 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1592 switch (convert) {
1593 case NO_CONVERSION:
1595 memcpy(dst, src, pitch * height);
1596 break;
1598 case CONVERT_PALETTED:
1599 case CONVERT_PALETTED_CK:
1601 IWineD3DPaletteImpl* pal = This->palette;
1602 BYTE table[256][4];
1603 unsigned int x, y;
1605 if( pal == NULL) {
1606 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1609 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1611 for (y = 0; y < height; y++)
1613 source = src + pitch * y;
1614 dest = dst + outpitch * y;
1615 /* This is an 1 bpp format, using the width here is fine */
1616 for (x = 0; x < width; x++) {
1617 BYTE color = *source++;
1618 *dest++ = table[color][0];
1619 *dest++ = table[color][1];
1620 *dest++ = table[color][2];
1621 *dest++ = table[color][3];
1625 break;
1627 case CONVERT_CK_565:
1629 /* Converting the 565 format in 5551 packed to emulate color-keying.
1631 Note : in all these conversion, it would be best to average the averaging
1632 pixels to get the color of the pixel that will be color-keyed to
1633 prevent 'color bleeding'. This will be done later on if ever it is
1634 too visible.
1636 Note2: Nvidia documents say that their driver does not support alpha + color keying
1637 on the same surface and disables color keying in such a case
1639 unsigned int x, y;
1640 WORD *Source;
1641 WORD *Dest;
1643 TRACE("Color keyed 565\n");
1645 for (y = 0; y < height; y++) {
1646 Source = (WORD *) (src + y * pitch);
1647 Dest = (WORD *) (dst + y * outpitch);
1648 for (x = 0; x < width; x++ ) {
1649 WORD color = *Source++;
1650 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1651 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1652 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1653 *Dest |= 0x0001;
1655 Dest++;
1659 break;
1661 case CONVERT_CK_5551:
1663 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1664 unsigned int x, y;
1665 WORD *Source;
1666 WORD *Dest;
1667 TRACE("Color keyed 5551\n");
1668 for (y = 0; y < height; y++) {
1669 Source = (WORD *) (src + y * pitch);
1670 Dest = (WORD *) (dst + y * outpitch);
1671 for (x = 0; x < width; x++ ) {
1672 WORD color = *Source++;
1673 *Dest = color;
1674 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1675 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1676 *Dest |= (1 << 15);
1678 else {
1679 *Dest &= ~(1 << 15);
1681 Dest++;
1685 break;
1687 case CONVERT_V8U8:
1689 unsigned int x, y;
1690 short *Source;
1691 unsigned char *Dest;
1692 for(y = 0; y < height; y++) {
1693 Source = (short *) (src + y * pitch);
1694 Dest = (unsigned char *) (dst + y * outpitch);
1695 for (x = 0; x < width; x++ ) {
1696 long color = (*Source++);
1697 /* B */ Dest[0] = 0xff;
1698 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1699 /* R */ Dest[2] = (color) + 128; /* U */
1700 Dest += 3;
1703 break;
1706 case CONVERT_V16U16:
1708 unsigned int x, y;
1709 DWORD *Source;
1710 unsigned short *Dest;
1711 for(y = 0; y < height; y++) {
1712 Source = (DWORD *) (src + y * pitch);
1713 Dest = (unsigned short *) (dst + y * outpitch);
1714 for (x = 0; x < width; x++ ) {
1715 DWORD color = (*Source++);
1716 /* B */ Dest[0] = 0xffff;
1717 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1718 /* R */ Dest[2] = (color ) + 32768; /* U */
1719 Dest += 3;
1722 break;
1725 case CONVERT_Q8W8V8U8:
1727 unsigned int x, y;
1728 DWORD *Source;
1729 unsigned char *Dest;
1730 for(y = 0; y < height; y++) {
1731 Source = (DWORD *) (src + y * pitch);
1732 Dest = (unsigned char *) (dst + y * outpitch);
1733 for (x = 0; x < width; x++ ) {
1734 long color = (*Source++);
1735 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1736 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1737 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1738 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1739 Dest += 4;
1742 break;
1745 case CONVERT_L6V5U5:
1747 unsigned int x, y;
1748 WORD *Source;
1749 unsigned char *Dest;
1751 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1752 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1753 * fixed function and shaders without further conversion once the surface is
1754 * loaded
1756 for(y = 0; y < height; y++) {
1757 Source = (WORD *) (src + y * pitch);
1758 Dest = (unsigned char *) (dst + y * outpitch);
1759 for (x = 0; x < width; x++ ) {
1760 short color = (*Source++);
1761 unsigned char l = ((color >> 10) & 0xfc);
1762 char v = ((color >> 5) & 0x3e);
1763 char u = ((color ) & 0x1f);
1765 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1766 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1767 * shift. GL reads a signed value and converts it into an unsigned value.
1769 /* M */ Dest[2] = l << 1;
1771 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1772 * from 5 bit values to 8 bit values.
1774 /* V */ Dest[1] = v << 3;
1775 /* U */ Dest[0] = u << 3;
1776 Dest += 3;
1779 } else {
1780 for(y = 0; y < height; y++) {
1781 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
1782 Source = (WORD *) (src + y * pitch);
1783 for (x = 0; x < width; x++ ) {
1784 short color = (*Source++);
1785 unsigned char l = ((color >> 10) & 0xfc);
1786 short v = ((color >> 5) & 0x3e);
1787 short u = ((color ) & 0x1f);
1788 short v_conv = v + 16;
1789 short u_conv = u + 16;
1791 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
1792 Dest_s += 1;
1796 break;
1799 case CONVERT_X8L8V8U8:
1801 unsigned int x, y;
1802 DWORD *Source;
1803 unsigned char *Dest;
1805 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1806 /* This implementation works with the fixed function pipeline and shaders
1807 * without further modification after converting the surface.
1809 for(y = 0; y < height; y++) {
1810 Source = (DWORD *) (src + y * pitch);
1811 Dest = (unsigned char *) (dst + y * outpitch);
1812 for (x = 0; x < width; x++ ) {
1813 long color = (*Source++);
1814 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
1815 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
1816 /* U */ Dest[0] = (color & 0xff); /* U */
1817 /* I */ Dest[3] = 255; /* X */
1818 Dest += 4;
1821 } else {
1822 /* Doesn't work correctly with the fixed function pipeline, but can work in
1823 * shaders if the shader is adjusted. (There's no use for this format in gl's
1824 * standard fixed function pipeline anyway).
1826 for(y = 0; y < height; y++) {
1827 Source = (DWORD *) (src + y * pitch);
1828 Dest = (unsigned char *) (dst + y * outpitch);
1829 for (x = 0; x < width; x++ ) {
1830 long color = (*Source++);
1831 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
1832 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1833 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1834 Dest += 4;
1838 break;
1841 case CONVERT_A4L4:
1843 unsigned int x, y;
1844 unsigned char *Source;
1845 unsigned char *Dest;
1846 for(y = 0; y < height; y++) {
1847 Source = (unsigned char *) (src + y * pitch);
1848 Dest = (unsigned char *) (dst + y * outpitch);
1849 for (x = 0; x < width; x++ ) {
1850 unsigned char color = (*Source++);
1851 /* A */ Dest[1] = (color & 0xf0) << 0;
1852 /* L */ Dest[0] = (color & 0x0f) << 4;
1853 Dest += 2;
1856 break;
1859 case CONVERT_R32F:
1861 unsigned int x, y;
1862 float *Source;
1863 float *Dest;
1864 for(y = 0; y < height; y++) {
1865 Source = (float *) (src + y * pitch);
1866 Dest = (float *) (dst + y * outpitch);
1867 for (x = 0; x < width; x++ ) {
1868 float color = (*Source++);
1869 Dest[0] = color;
1870 Dest[1] = 1.0;
1871 Dest[2] = 1.0;
1872 Dest += 3;
1875 break;
1878 case CONVERT_R16F:
1880 unsigned int x, y;
1881 WORD *Source;
1882 WORD *Dest;
1883 WORD one = 0x3c00;
1884 for(y = 0; y < height; y++) {
1885 Source = (WORD *) (src + y * pitch);
1886 Dest = (WORD *) (dst + y * outpitch);
1887 for (x = 0; x < width; x++ ) {
1888 WORD color = (*Source++);
1889 Dest[0] = color;
1890 Dest[1] = one;
1891 Dest[2] = one;
1892 Dest += 3;
1895 break;
1897 default:
1898 ERR("Unsupported conversation type %d\n", convert);
1900 return WINED3D_OK;
1903 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
1904 IWineD3DPaletteImpl* pal = This->palette;
1905 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1906 BOOL index_in_alpha = FALSE;
1907 int i;
1909 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
1910 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
1911 * is slow. Further RGB->P8 conversion is not possible because palettes can have
1912 * duplicate entries. Store the color key in the unused alpha component to speed the
1913 * download up and to make conversion unneeded. */
1914 if (device->render_targets && device->render_targets[0]) {
1915 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
1917 if(render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1918 index_in_alpha = TRUE;
1921 if (pal == NULL) {
1922 /* Still no palette? Use the device's palette */
1923 /* Get the surface's palette */
1924 for (i = 0; i < 256; i++) {
1925 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1926 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1927 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1929 if(index_in_alpha) {
1930 table[i][3] = i;
1931 } else if (colorkey &&
1932 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1933 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1934 /* We should maybe here put a more 'neutral' color than the standard bright purple
1935 one often used by application to prevent the nice purple borders when bi-linear
1936 filtering is on */
1937 table[i][3] = 0x00;
1938 } else {
1939 table[i][3] = 0xFF;
1942 } else {
1943 TRACE("Using surface palette %p\n", pal);
1944 /* Get the surface's palette */
1945 for (i = 0; i < 256; i++) {
1946 table[i][0] = pal->palents[i].peRed;
1947 table[i][1] = pal->palents[i].peGreen;
1948 table[i][2] = pal->palents[i].peBlue;
1950 if(index_in_alpha) {
1951 table[i][3] = i;
1953 else if (colorkey &&
1954 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1955 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1956 /* We should maybe here put a more 'neutral' color than the standard bright purple
1957 one often used by application to prevent the nice purple borders when bi-linear
1958 filtering is on */
1959 table[i][3] = 0x00;
1960 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
1961 table[i][3] = pal->palents[i].peFlags;
1962 } else {
1963 table[i][3] = 0xFF;
1969 const char *fragment_palette_conversion =
1970 "!!ARBfp1.0\n"
1971 "TEMP index;\n"
1972 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n" /* { 255/256, 0.5/255*255/256, 0, 0 } */
1973 "TEX index.x, fragment.texcoord[0], texture[0], 2D;\n" /* store the red-component of the current pixel */
1974 "MAD index.x, index.x, constants.x, constants.y;\n" /* Scale the index by 255/256 and add a bias of '0.5' in order to sample in the middle */
1975 "TEX result.color, index, texture[1], 1D;\n" /* use the red-component as a index in the palette to get the final color */
1976 "END";
1978 /* This function is used in case of 8bit paletted textures to upload the palette.
1979 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
1980 extensions like ATI_fragment_shaders is possible.
1982 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
1983 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1984 BYTE table[256][4];
1985 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1987 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1989 /* Try to use the paletted texture extension */
1990 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
1992 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
1993 GL_EXTCALL(glColorTableEXT(GL_TEXTURE_2D,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
1995 else
1997 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
1998 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
1999 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2001 /* Create the fragment program if we don't have it */
2002 if(!device->paletteConversionShader)
2004 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2005 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2006 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2007 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), (const GLbyte *)fragment_palette_conversion));
2008 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2011 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2012 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2014 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2015 glEnable(GL_TEXTURE_1D);
2016 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2018 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2019 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2020 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2021 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2023 /* Switch back to unit 0 in which the 2D texture will be stored. */
2024 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2026 /* Rebind the texture because it isn't bound anymore */
2027 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2031 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2032 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2034 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2035 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2036 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2038 return FALSE;
2041 if(This->palette9) {
2042 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2043 return FALSE;
2045 } else {
2046 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2048 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2049 return TRUE;
2052 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
2053 GLboolean oldwrite[4];
2055 /* Some formats have only some color channels, and the others are 1.0.
2056 * since our rendering renders to all channels, and those pixel formats
2057 * are emulated by using a full texture with the other channels set to 1.0
2058 * manually, clear the unused channels.
2060 * This could be done with hacking colorwriteenable to mask the colors,
2061 * but before drawing the buffer would have to be cleared too, so there's
2062 * no gain in that
2064 switch(This->resource.format) {
2065 case WINED3DFMT_R16F:
2066 case WINED3DFMT_R32F:
2067 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2068 /* Do not activate a context, the correct drawable is active already
2069 * though just the read buffer is set, make sure to have the correct draw
2070 * buffer too
2072 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2073 glDisable(GL_SCISSOR_TEST);
2074 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2075 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2076 glClearColor(0.0, 1.0, 1.0, 1.0);
2077 glClear(GL_COLOR_BUFFER_BIT);
2078 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2079 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2080 checkGLcall("Unused channel clear\n");
2081 break;
2083 default: break;
2087 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2088 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2090 if (!(This->Flags & SFLAG_INTEXTURE)) {
2091 TRACE("Reloading because surface is dirty\n");
2092 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2093 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2094 /* Reload: vice versa OR */
2095 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2096 /* Also reload: Color key is active AND the color key has changed */
2097 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2098 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2099 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2100 TRACE("Reloading because of color keying\n");
2101 /* To perform the color key conversion we need a sysmem copy of
2102 * the surface. Make sure we have it
2104 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2105 } else if(palette9_changed(This)) {
2106 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
2107 /* TODO: This is not necessarily needed with hw palettized texture support */
2108 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2109 } else {
2110 TRACE("surface is already in texture\n");
2111 return WINED3D_OK;
2114 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2115 * These resources are not bound by device size or format restrictions. Because of this,
2116 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2117 * However, these resources can always be created, locked, and copied.
2119 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2121 FIXME("(%p) Operation not supported for scratch textures\n",This);
2122 return WINED3DERR_INVALIDCALL;
2125 This->srgb = srgb_mode;
2126 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* no partial locking for textures yet */);
2128 #if 0
2130 static unsigned int gen = 0;
2131 char buffer[4096];
2132 ++gen;
2133 if ((gen % 10) == 0) {
2134 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2135 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2138 * debugging crash code
2139 if (gen == 250) {
2140 void** test = NULL;
2141 *test = 0;
2145 #endif
2147 if (!(This->Flags & SFLAG_DONOTFREE)) {
2148 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2149 This->resource.allocatedMemory = NULL;
2150 This->resource.heapMemory = NULL;
2151 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2154 return WINED3D_OK;
2157 #include <errno.h>
2158 #include <stdio.h>
2159 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2160 FILE* f = NULL;
2161 UINT i, y;
2162 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2163 char *allocatedMemory;
2164 char *textureRow;
2165 IWineD3DSwapChain *swapChain = NULL;
2166 int width, height;
2167 GLuint tmpTexture = 0;
2168 DWORD color;
2169 /*FIXME:
2170 Textures may not be stored in ->allocatedgMemory and a GlTexture
2171 so we should lock the surface before saving a snapshot, or at least check that
2173 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2174 by calling GetTexImage and in compressed form by calling
2175 GetCompressedTexImageARB. Queried compressed images can be saved and
2176 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2177 texture images do not need to be processed by the GL and should
2178 significantly improve texture loading performance relative to uncompressed
2179 images. */
2181 /* Setup the width and height to be the internal texture width and height. */
2182 width = This->pow2Width;
2183 height = This->pow2Height;
2184 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2185 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2187 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2188 /* if were not a real texture then read the back buffer into a real texture */
2189 /* we don't want to interfere with the back buffer so read the data into a temporary
2190 * texture and then save the data out of the temporary texture
2192 GLint prevRead;
2193 ENTER_GL();
2194 TRACE("(%p) Reading render target into texture\n", This);
2195 glEnable(GL_TEXTURE_2D);
2197 glGenTextures(1, &tmpTexture);
2198 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2200 glTexImage2D(GL_TEXTURE_2D,
2202 GL_RGBA,
2203 width,
2204 height,
2205 0/*border*/,
2206 GL_RGBA,
2207 GL_UNSIGNED_INT_8_8_8_8_REV,
2208 NULL);
2210 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2211 vcheckGLcall("glGetIntegerv");
2212 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2213 vcheckGLcall("glReadBuffer");
2214 glCopyTexImage2D(GL_TEXTURE_2D,
2216 GL_RGBA,
2219 width,
2220 height,
2223 checkGLcall("glCopyTexImage2D");
2224 glReadBuffer(prevRead);
2225 LEAVE_GL();
2227 } else { /* bind the real texture, and make sure it up to date */
2228 IWineD3DSurface_PreLoad(iface);
2230 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2231 ENTER_GL();
2232 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2233 glGetTexImage(GL_TEXTURE_2D,
2234 This->glDescription.level,
2235 GL_RGBA,
2236 GL_UNSIGNED_INT_8_8_8_8_REV,
2237 allocatedMemory);
2238 checkGLcall("glTexImage2D");
2239 if (tmpTexture) {
2240 glBindTexture(GL_TEXTURE_2D, 0);
2241 glDeleteTextures(1, &tmpTexture);
2243 LEAVE_GL();
2245 f = fopen(filename, "w+");
2246 if (NULL == f) {
2247 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2248 return WINED3DERR_INVALIDCALL;
2250 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2251 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2252 /* TGA header */
2253 fputc(0,f);
2254 fputc(0,f);
2255 fputc(2,f);
2256 fputc(0,f);
2257 fputc(0,f);
2258 fputc(0,f);
2259 fputc(0,f);
2260 fputc(0,f);
2261 fputc(0,f);
2262 fputc(0,f);
2263 fputc(0,f);
2264 fputc(0,f);
2265 /* short width*/
2266 fwrite(&width,2,1,f);
2267 /* short height */
2268 fwrite(&height,2,1,f);
2269 /* format rgba */
2270 fputc(0x20,f);
2271 fputc(0x28,f);
2272 /* raw data */
2273 /* 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 */
2274 if(swapChain)
2275 textureRow = allocatedMemory + (width * (height - 1) *4);
2276 else
2277 textureRow = allocatedMemory;
2278 for (y = 0 ; y < height; y++) {
2279 for (i = 0; i < width; i++) {
2280 color = *((DWORD*)textureRow);
2281 fputc((color >> 16) & 0xFF, f); /* B */
2282 fputc((color >> 8) & 0xFF, f); /* G */
2283 fputc((color >> 0) & 0xFF, f); /* R */
2284 fputc((color >> 24) & 0xFF, f); /* A */
2285 textureRow += 4;
2287 /* take two rows of the pointer to the texture memory */
2288 if(swapChain)
2289 (textureRow-= width << 3);
2292 TRACE("Closing file\n");
2293 fclose(f);
2295 if(swapChain) {
2296 IWineD3DSwapChain_Release(swapChain);
2298 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2299 return WINED3D_OK;
2303 * Slightly inefficient way to handle multiple dirty rects but it works :)
2305 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2306 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2307 IWineD3DBaseTexture *baseTexture = NULL;
2309 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2310 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
2312 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2313 if (NULL != pDirtyRect) {
2314 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2315 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2316 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2317 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2318 } else {
2319 This->dirtyRect.left = 0;
2320 This->dirtyRect.top = 0;
2321 This->dirtyRect.right = This->currentDesc.Width;
2322 This->dirtyRect.bottom = This->currentDesc.Height;
2324 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2325 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2326 /* if the container is a basetexture then mark it dirty. */
2327 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2328 TRACE("Passing to container\n");
2329 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2330 IWineD3DBaseTexture_Release(baseTexture);
2332 return WINED3D_OK;
2335 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2336 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2337 HRESULT hr;
2338 const GlPixelFormatDesc *glDesc;
2339 getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2341 TRACE("(%p) : Calling base function first\n", This);
2342 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2343 if(SUCCEEDED(hr)) {
2344 /* Setup some glformat defaults */
2345 This->glDescription.glFormat = glDesc->glFormat;
2346 This->glDescription.glFormatInternal = glDesc->glInternal;
2347 This->glDescription.glType = glDesc->glType;
2349 This->Flags &= ~SFLAG_ALLOCATED;
2350 TRACE("(%p) : glFormat %d, glFotmatInternal %d, glType %d\n", This,
2351 This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2353 return hr;
2356 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2357 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2359 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2360 WARN("Surface is locked or the HDC is in use\n");
2361 return WINED3DERR_INVALIDCALL;
2364 if(Mem && Mem != This->resource.allocatedMemory) {
2365 void *release = NULL;
2367 /* Do I have to copy the old surface content? */
2368 if(This->Flags & SFLAG_DIBSECTION) {
2369 /* Release the DC. No need to hold the critical section for the update
2370 * Thread because this thread runs only on front buffers, but this method
2371 * fails for render targets in the check above.
2373 SelectObject(This->hDC, This->dib.holdbitmap);
2374 DeleteDC(This->hDC);
2375 /* Release the DIB section */
2376 DeleteObject(This->dib.DIBsection);
2377 This->dib.bitmap_data = NULL;
2378 This->resource.allocatedMemory = NULL;
2379 This->hDC = NULL;
2380 This->Flags &= ~SFLAG_DIBSECTION;
2381 } else if(!(This->Flags & SFLAG_USERPTR)) {
2382 release = This->resource.heapMemory;
2383 This->resource.heapMemory = NULL;
2385 This->resource.allocatedMemory = Mem;
2386 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2388 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2389 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2391 /* For client textures opengl has to be notified */
2392 if(This->Flags & SFLAG_CLIENT) {
2393 This->Flags &= ~SFLAG_ALLOCATED;
2394 IWineD3DSurface_PreLoad(iface);
2395 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2398 /* Now free the old memory if any */
2399 HeapFree(GetProcessHeap(), 0, release);
2400 } else if(This->Flags & SFLAG_USERPTR) {
2401 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2402 This->resource.allocatedMemory = NULL;
2403 /* HeapMemory should be NULL already */
2404 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2405 This->Flags &= ~SFLAG_USERPTR;
2407 if(This->Flags & SFLAG_CLIENT) {
2408 This->Flags &= ~SFLAG_ALLOCATED;
2409 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2410 IWineD3DSurface_PreLoad(iface);
2413 return WINED3D_OK;
2416 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2417 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2418 IWineD3DSwapChainImpl *swapchain = NULL;
2419 HRESULT hr;
2420 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2422 /* Flipping is only supported on RenderTargets */
2423 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2425 if(override) {
2426 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2427 * FIXME("(%p) Target override is not supported by now\n", This);
2428 * Additionally, it isn't really possible to support triple-buffering
2429 * properly on opengl at all
2433 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2434 if(!swapchain) {
2435 ERR("Flipped surface is not on a swapchain\n");
2436 return WINEDDERR_NOTFLIPPABLE;
2439 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2440 * and only d3d8 and d3d9 apps specify the presentation interval
2442 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2443 /* Most common case first to avoid wasting time on all the other cases */
2444 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2445 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2446 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2447 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2448 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2449 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2450 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2451 } else {
2452 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2455 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2456 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2457 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2458 return hr;
2461 /* Does a direct frame buffer -> texture copy. Stretching is done
2462 * with single pixel copy calls
2464 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2465 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2466 float xrel, yrel;
2467 UINT row;
2468 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2471 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2472 ENTER_GL();
2473 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2475 /* Bind the target texture */
2476 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
2477 checkGLcall("glBindTexture");
2478 if(!swapchain) {
2479 glReadBuffer(myDevice->offscreenBuffer);
2480 } else {
2481 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2482 glReadBuffer(buffer);
2484 checkGLcall("glReadBuffer");
2486 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2487 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2489 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2490 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2492 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2493 ERR("Texture filtering not supported in direct blit\n");
2495 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2496 ERR("Texture filtering not supported in direct blit\n");
2499 if(upsidedown &&
2500 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2501 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2502 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2504 glCopyTexSubImage2D(This->glDescription.target,
2505 This->glDescription.level,
2506 drect->x1, drect->y1, /* xoffset, yoffset */
2507 srect->x1, Src->currentDesc.Height - srect->y2,
2508 drect->x2 - drect->x1, drect->y2 - drect->y1);
2509 } else {
2510 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2511 /* I have to process this row by row to swap the image,
2512 * otherwise it would be upside down, so stretching in y direction
2513 * doesn't cost extra time
2515 * However, stretching in x direction can be avoided if not necessary
2517 for(row = drect->y1; row < drect->y2; row++) {
2518 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2519 /* Well, that stuff works, but it's very slow.
2520 * find a better way instead
2522 UINT col;
2524 for(col = drect->x1; col < drect->x2; col++) {
2525 glCopyTexSubImage2D(This->glDescription.target,
2526 This->glDescription.level,
2527 drect->x1 + col, row, /* xoffset, yoffset */
2528 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2529 1, 1);
2531 } else {
2532 glCopyTexSubImage2D(This->glDescription.target,
2533 This->glDescription.level,
2534 drect->x1, row, /* xoffset, yoffset */
2535 srect->x1, yoffset - (int) (row * yrel),
2536 drect->x2-drect->x1, 1);
2541 vcheckGLcall("glCopyTexSubImage2D");
2542 LEAVE_GL();
2545 /* Uses the hardware to stretch and flip the image */
2546 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2547 GLuint src, backup = 0;
2548 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2549 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2550 float left, right, top, bottom; /* Texture coordinates */
2551 UINT fbwidth = Src->currentDesc.Width;
2552 UINT fbheight = Src->currentDesc.Height;
2553 GLenum drawBuffer = GL_BACK;
2555 TRACE("Using hwstretch blit\n");
2556 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2557 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2558 ENTER_GL();
2559 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2561 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2562 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2564 if(GL_LIMITS(aux_buffers) >= 2) {
2565 /* Got more than one aux buffer? Use the 2nd aux buffer */
2566 drawBuffer = GL_AUX1;
2567 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2568 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2569 drawBuffer = GL_AUX0;
2572 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2573 glGenTextures(1, &backup);
2574 checkGLcall("glGenTextures\n");
2575 glBindTexture(GL_TEXTURE_2D, backup);
2576 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2577 } else {
2578 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2579 * we are reading from the back buffer, the backup can be used as source texture
2581 if(Src->glDescription.textureName == 0) {
2582 /* Get it a description */
2583 IWineD3DSurface_PreLoad(SrcSurface);
2585 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
2586 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2588 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2589 Src->Flags &= ~SFLAG_INTEXTURE;
2592 glReadBuffer(GL_BACK);
2593 checkGLcall("glReadBuffer(GL_BACK)");
2595 /* TODO: Only back up the part that will be overwritten */
2596 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2597 0, 0 /* read offsets */,
2598 0, 0,
2599 fbwidth,
2600 fbheight);
2602 checkGLcall("glCopyTexSubImage2D");
2604 /* No issue with overriding these - the sampler is dirty due to blit usage */
2605 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
2606 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2607 checkGLcall("glTexParameteri");
2608 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
2609 minMipLookup[Filter][WINED3DTEXF_NONE]);
2610 checkGLcall("glTexParameteri");
2612 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2613 src = backup ? backup : Src->glDescription.textureName;
2614 } else {
2615 glReadBuffer(GL_FRONT);
2616 checkGLcall("glReadBuffer(GL_FRONT)");
2618 glGenTextures(1, &src);
2619 checkGLcall("glGenTextures(1, &src)");
2620 glBindTexture(GL_TEXTURE_2D, src);
2621 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2623 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2624 * out for power of 2 sizes
2626 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2627 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2628 checkGLcall("glTexImage2D");
2629 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2630 0, 0 /* read offsets */,
2631 0, 0,
2632 fbwidth,
2633 fbheight);
2635 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2636 checkGLcall("glTexParameteri");
2637 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2638 checkGLcall("glTexParameteri");
2640 glReadBuffer(GL_BACK);
2641 checkGLcall("glReadBuffer(GL_BACK)");
2643 checkGLcall("glEnd and previous");
2645 left = (float) srect->x1 / (float) Src->pow2Width;
2646 right = (float) srect->x2 / (float) Src->pow2Width;
2648 if(upsidedown) {
2649 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2650 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2651 } else {
2652 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2653 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2656 /* draw the source texture stretched and upside down. The correct surface is bound already */
2657 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
2658 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
2660 glDrawBuffer(drawBuffer);
2661 glReadBuffer(drawBuffer);
2663 glBegin(GL_QUADS);
2664 /* bottom left */
2665 glTexCoord2f(left, bottom);
2666 glVertex2i(0, fbheight);
2668 /* top left */
2669 glTexCoord2f(left, top);
2670 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2672 /* top right */
2673 glTexCoord2f(right, top);
2674 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2676 /* bottom right */
2677 glTexCoord2f(right, bottom);
2678 glVertex2i(drect->x2 - drect->x1, fbheight);
2679 glEnd();
2680 checkGLcall("glEnd and previous");
2682 /* Now read the stretched and upside down image into the destination texture */
2683 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2684 checkGLcall("glBindTexture");
2685 glCopyTexSubImage2D(This->glDescription.target,
2687 drect->x1, drect->y1, /* xoffset, yoffset */
2688 0, 0, /* We blitted the image to the origin */
2689 drect->x2 - drect->x1, drect->y2 - drect->y1);
2690 checkGLcall("glCopyTexSubImage2D");
2692 /* Write the back buffer backup back */
2693 glBindTexture(GL_TEXTURE_2D, backup ? backup : Src->glDescription.textureName);
2694 checkGLcall("glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName)");
2696 if(drawBuffer == GL_BACK) {
2697 glBegin(GL_QUADS);
2698 /* top left */
2699 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2700 glVertex2i(0, 0);
2702 /* bottom left */
2703 glTexCoord2f(0.0, 0.0);
2704 glVertex2i(0, fbheight);
2706 /* bottom right */
2707 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2708 glVertex2i(fbwidth, Src->currentDesc.Height);
2710 /* top right */
2711 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2712 glVertex2i(fbwidth, 0);
2713 glEnd();
2714 } else {
2715 /* Restore the old draw buffer */
2716 glDrawBuffer(GL_BACK);
2719 /* Cleanup */
2720 if(src != Src->glDescription.textureName && src != backup) {
2721 glDeleteTextures(1, &src);
2722 checkGLcall("glDeleteTextures(1, &src)");
2724 if(backup) {
2725 glDeleteTextures(1, &backup);
2726 checkGLcall("glDeleteTextures(1, &backup)");
2728 LEAVE_GL();
2731 /* Not called from the VTable */
2732 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2733 WINED3DRECT rect;
2734 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2735 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2736 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2738 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2740 /* Get the swapchain. One of the surfaces has to be a primary surface */
2741 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2742 WARN("Destination is in sysmem, rejecting gl blt\n");
2743 return WINED3DERR_INVALIDCALL;
2745 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2746 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2747 if(Src) {
2748 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2749 WARN("Src is in sysmem, rejecting gl blt\n");
2750 return WINED3DERR_INVALIDCALL;
2752 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2753 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2756 /* Early sort out of cases where no render target is used */
2757 if(!dstSwapchain && !srcSwapchain &&
2758 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2759 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2760 return WINED3DERR_INVALIDCALL;
2763 /* No destination color keying supported */
2764 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
2765 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2766 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2767 return WINED3DERR_INVALIDCALL;
2770 if (DestRect) {
2771 rect.x1 = DestRect->left;
2772 rect.y1 = DestRect->top;
2773 rect.x2 = DestRect->right;
2774 rect.y2 = DestRect->bottom;
2775 } else {
2776 rect.x1 = 0;
2777 rect.y1 = 0;
2778 rect.x2 = This->currentDesc.Width;
2779 rect.y2 = This->currentDesc.Height;
2782 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2783 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
2784 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
2785 /* Half-life does a Blt from the back buffer to the front buffer,
2786 * Full surface size, no flags... Use present instead
2788 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
2791 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2792 while(1)
2794 RECT mySrcRect;
2795 TRACE("Looking if a Present can be done...\n");
2796 /* Source Rectangle must be full surface */
2797 if( SrcRect ) {
2798 if(SrcRect->left != 0 || SrcRect->top != 0 ||
2799 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
2800 TRACE("No, Source rectangle doesn't match\n");
2801 break;
2804 mySrcRect.left = 0;
2805 mySrcRect.top = 0;
2806 mySrcRect.right = Src->currentDesc.Width;
2807 mySrcRect.bottom = Src->currentDesc.Height;
2809 /* No stretching may occur */
2810 if(mySrcRect.right != rect.x2 - rect.x1 ||
2811 mySrcRect.bottom != rect.y2 - rect.y1) {
2812 TRACE("No, stretching is done\n");
2813 break;
2816 /* Destination must be full surface or match the clipping rectangle */
2817 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
2819 RECT cliprect;
2820 POINT pos[2];
2821 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
2822 pos[0].x = rect.x1;
2823 pos[0].y = rect.y1;
2824 pos[1].x = rect.x2;
2825 pos[1].y = rect.y2;
2826 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
2827 pos, 2);
2829 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
2830 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
2832 TRACE("No, dest rectangle doesn't match(clipper)\n");
2833 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
2834 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
2835 break;
2838 else
2840 if(rect.x1 != 0 || rect.y1 != 0 ||
2841 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
2842 TRACE("No, dest rectangle doesn't match(surface size)\n");
2843 break;
2847 TRACE("Yes\n");
2849 /* These flags are unimportant for the flag check, remove them */
2850 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
2851 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
2853 /* The idea behind this is that a glReadPixels and a glDrawPixels call
2854 * take very long, while a flip is fast.
2855 * This applies to Half-Life, which does such Blts every time it finished
2856 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
2857 * menu. This is also used by all apps when they do windowed rendering
2859 * The problem is that flipping is not really the same as copying. After a
2860 * Blt the front buffer is a copy of the back buffer, and the back buffer is
2861 * untouched. Therefore it's necessary to override the swap effect
2862 * and to set it back after the flip.
2864 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
2865 * testcases.
2868 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
2869 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2871 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
2872 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
2874 dstSwapchain->presentParms.SwapEffect = orig_swap;
2876 return WINED3D_OK;
2878 break;
2881 TRACE("Unsupported blit between buffers on the same swapchain\n");
2882 return WINED3DERR_INVALIDCALL;
2883 } else if((dstSwapchain || This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) &&
2884 (srcSwapchain || SrcSurface == myDevice->render_targets[0]) ) {
2885 ERR("Can't perform hardware blit between 2 different swapchains, falling back to software\n");
2886 return WINED3DERR_INVALIDCALL;
2889 if(srcSwapchain || SrcSurface == myDevice->render_targets[0]) {
2890 /* Blit from render target to texture */
2891 WINED3DRECT srect;
2892 BOOL upsideDown, stretchx;
2894 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
2895 TRACE("Color keying not supported by frame buffer to texture blit\n");
2896 return WINED3DERR_INVALIDCALL;
2897 /* Destination color key is checked above */
2900 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2901 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2903 if(SrcRect) {
2904 if(SrcRect->top < SrcRect->bottom) {
2905 srect.y1 = SrcRect->top;
2906 srect.y2 = SrcRect->bottom;
2907 upsideDown = FALSE;
2908 } else {
2909 srect.y1 = SrcRect->bottom;
2910 srect.y2 = SrcRect->top;
2911 upsideDown = TRUE;
2913 srect.x1 = SrcRect->left;
2914 srect.x2 = SrcRect->right;
2915 } else {
2916 srect.x1 = 0;
2917 srect.y1 = 0;
2918 srect.x2 = Src->currentDesc.Width;
2919 srect.y2 = Src->currentDesc.Height;
2920 upsideDown = FALSE;
2922 if(rect.x1 > rect.x2) {
2923 UINT tmp = rect.x2;
2924 rect.x2 = rect.x1;
2925 rect.x1 = tmp;
2926 upsideDown = !upsideDown;
2928 if(!srcSwapchain) {
2929 TRACE("Reading from an offscreen target\n");
2930 upsideDown = !upsideDown;
2933 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
2934 stretchx = TRUE;
2935 } else {
2936 stretchx = FALSE;
2939 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2940 * flip the image nor scale it.
2942 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2943 * -> If the app wants a image width an unscaled width, copy it line per line
2944 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
2945 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2946 * back buffer. This is slower than reading line per line, thus not used for flipping
2947 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2948 * pixel by pixel
2950 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
2951 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
2952 * backends.
2954 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
2955 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
2956 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
2957 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
2958 rect.y2 - rect.y1 > Src->currentDesc.Height) {
2959 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
2960 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
2961 } else {
2962 TRACE("Using hardware stretching to flip / stretch the texture\n");
2963 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
2966 if(!(This->Flags & SFLAG_DONOTFREE)) {
2967 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2968 This->resource.allocatedMemory = NULL;
2969 This->resource.heapMemory = NULL;
2970 } else {
2971 This->Flags &= ~SFLAG_INSYSMEM;
2973 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
2974 * path is never entered
2976 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
2978 return WINED3D_OK;
2979 } else if(Src) {
2980 /* Blit from offscreen surface to render target */
2981 float glTexCoord[4];
2982 DWORD oldCKeyFlags = Src->CKeyFlags;
2983 WINEDDCOLORKEY oldBltCKey = This->SrcBltCKey;
2984 RECT SourceRectangle;
2986 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
2988 if(SrcRect) {
2989 SourceRectangle.left = SrcRect->left;
2990 SourceRectangle.right = SrcRect->right;
2991 SourceRectangle.top = SrcRect->top;
2992 SourceRectangle.bottom = SrcRect->bottom;
2993 } else {
2994 SourceRectangle.left = 0;
2995 SourceRectangle.right = Src->currentDesc.Width;
2996 SourceRectangle.top = 0;
2997 SourceRectangle.bottom = Src->currentDesc.Height;
3000 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3001 /* Fall back to software */
3002 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3003 SourceRectangle.left, SourceRectangle.top,
3004 SourceRectangle.right, SourceRectangle.bottom);
3005 return WINED3DERR_INVALIDCALL;
3008 /* Color keying: Check if we have to do a color keyed blt,
3009 * and if not check if a color key is activated.
3011 * Just modify the color keying parameters in the surface and restore them afterwards
3012 * The surface keeps track of the color key last used to load the opengl surface.
3013 * PreLoad will catch the change to the flags and color key and reload if necessary.
3015 if(Flags & WINEDDBLT_KEYSRC) {
3016 /* Use color key from surface */
3017 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3018 /* Use color key from DDBltFx */
3019 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3020 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3021 } else {
3022 /* Do not use color key */
3023 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3026 /* Now load the surface */
3027 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3030 /* Activate the destination context, set it up for blitting */
3031 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3032 ENTER_GL();
3034 if(!dstSwapchain) {
3035 TRACE("Drawing to offscreen buffer\n");
3036 glDrawBuffer(myDevice->offscreenBuffer);
3037 checkGLcall("glDrawBuffer");
3038 } else {
3039 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
3040 TRACE("Drawing to %#x buffer\n", buffer);
3041 glDrawBuffer(buffer);
3042 checkGLcall("glDrawBuffer");
3045 /* Bind the texture */
3046 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
3047 checkGLcall("glBindTexture");
3049 /* Filtering for StretchRect */
3050 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
3051 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
3052 checkGLcall("glTexParameteri");
3053 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
3054 minMipLookup[Filter][WINED3DTEXF_NONE]);
3055 checkGLcall("glTexParameteri");
3056 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
3057 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
3058 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3059 checkGLcall("glTexEnvi");
3061 /* This is for color keying */
3062 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3063 glEnable(GL_ALPHA_TEST);
3064 checkGLcall("glEnable GL_ALPHA_TEST");
3065 glAlphaFunc(GL_NOTEQUAL, 0.0);
3066 checkGLcall("glAlphaFunc\n");
3067 } else {
3068 glDisable(GL_ALPHA_TEST);
3069 checkGLcall("glDisable GL_ALPHA_TEST");
3072 /* Draw a textured quad
3074 glBegin(GL_QUADS);
3076 glColor3d(1.0f, 1.0f, 1.0f);
3077 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3078 glVertex3f(rect.x1,
3079 rect.y1,
3080 0.0);
3082 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3083 glVertex3f(rect.x1, rect.y2, 0.0);
3085 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3086 glVertex3f(rect.x2,
3087 rect.y2,
3088 0.0);
3090 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3091 glVertex3f(rect.x2,
3092 rect.y1,
3093 0.0);
3094 glEnd();
3095 checkGLcall("glEnd");
3097 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3098 glDisable(GL_ALPHA_TEST);
3099 checkGLcall("glDisable(GL_ALPHA_TEST)");
3102 /* Unbind the texture */
3103 glBindTexture(GL_TEXTURE_2D, 0);
3104 checkGLcall("glEnable glBindTexture");
3106 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3107 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3109 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3110 glDrawBuffer(GL_BACK);
3111 checkGLcall("glDrawBuffer");
3113 /* Restore the color key parameters */
3114 Src->CKeyFlags = oldCKeyFlags;
3115 This->SrcBltCKey = oldBltCKey;
3117 LEAVE_GL();
3119 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3120 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3121 * is outdated now
3123 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3124 /* TODO: This should be moved to ModifyLocation() */
3125 if(!(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO)) {
3126 This->Flags |= SFLAG_INTEXTURE;
3129 return WINED3D_OK;
3130 } else {
3131 /* Source-Less Blit to render target */
3132 if (Flags & WINEDDBLT_COLORFILL) {
3133 /* This is easy to handle for the D3D Device... */
3134 DWORD color;
3136 TRACE("Colorfill\n");
3138 /* The color as given in the Blt function is in the format of the frame-buffer...
3139 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3141 if (This->resource.format == WINED3DFMT_P8) {
3142 if (This->palette) {
3143 color = ((0xFF000000) |
3144 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3145 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3146 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3147 } else {
3148 color = 0xFF000000;
3151 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3152 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3153 color = 0xFFFFFFFF;
3154 } else {
3155 color = ((0xFF000000) |
3156 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3157 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3158 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3161 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3162 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3163 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3165 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3166 color = DDBltFx->u5.dwFillColor;
3168 else {
3169 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3170 return WINED3DERR_INVALIDCALL;
3173 TRACE("Calling GetSwapChain with mydevice = %p\n", myDevice);
3174 if(dstSwapchain && dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]) {
3175 glDrawBuffer(GL_BACK);
3176 checkGLcall("glDrawBuffer(GL_BACK)");
3177 } else if (dstSwapchain && This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer) {
3178 glDrawBuffer(GL_FRONT);
3179 checkGLcall("glDrawBuffer(GL_FRONT)");
3180 } else if(This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3181 glDrawBuffer(myDevice->offscreenBuffer);
3182 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer3)");
3183 } else {
3184 TRACE("Surface is higher back buffer, falling back to software\n");
3185 return WINED3DERR_INVALIDCALL;
3188 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3190 IWineD3DDevice_Clear( (IWineD3DDevice *) myDevice,
3191 1 /* Number of rectangles */,
3192 &rect,
3193 WINED3DCLEAR_TARGET,
3194 color,
3195 0.0 /* Z */,
3196 0 /* Stencil */);
3198 /* Restore the original draw buffer */
3199 if(!dstSwapchain) {
3200 glDrawBuffer(myDevice->offscreenBuffer);
3201 } else if(dstSwapchain->backBuffer && dstSwapchain->backBuffer[0]) {
3202 glDrawBuffer(GL_BACK);
3204 vcheckGLcall("glDrawBuffer");
3206 return WINED3D_OK;
3210 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3211 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3212 return WINED3DERR_INVALIDCALL;
3215 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3217 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3218 float depth;
3220 if (Flags & WINEDDBLT_DEPTHFILL) {
3221 switch(This->resource.format) {
3222 case WINED3DFMT_D16:
3223 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3224 break;
3225 case WINED3DFMT_D15S1:
3226 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3227 break;
3228 case WINED3DFMT_D24S8:
3229 case WINED3DFMT_D24X8:
3230 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3231 break;
3232 case WINED3DFMT_D32:
3233 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3234 break;
3235 default:
3236 depth = 0.0;
3237 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3240 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3241 DestRect == NULL ? 0 : 1,
3242 (WINED3DRECT *) DestRect,
3243 WINED3DCLEAR_ZBUFFER,
3244 0x00000000,
3245 depth,
3246 0x00000000);
3249 FIXME("(%p): Unsupp depthstencil blit\n", This);
3250 return WINED3DERR_INVALIDCALL;
3253 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3254 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3255 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3256 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3257 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3258 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3260 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3261 * except depth blits, which seem to work
3263 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3264 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3265 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3266 return WINED3DERR_INVALIDCALL;
3267 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3268 TRACE("Z Blit override handled the blit\n");
3269 return WINED3D_OK;
3273 /* Special cases for RenderTargets */
3274 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3275 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3276 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3279 /* For the rest call the X11 surface implementation.
3280 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3281 * other Blts are rather rare
3283 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3286 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3287 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3288 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3289 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3290 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3292 if(myDevice->inScene &&
3293 (iface == myDevice->stencilBufferTarget ||
3294 (Source && Source == myDevice->stencilBufferTarget))) {
3295 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3296 return WINED3DERR_INVALIDCALL;
3299 /* Special cases for RenderTargets */
3300 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3301 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3303 RECT SrcRect, DstRect;
3304 DWORD Flags=0;
3306 if(rsrc) {
3307 SrcRect.left = rsrc->left;
3308 SrcRect.top= rsrc->top;
3309 SrcRect.bottom = rsrc->bottom;
3310 SrcRect.right = rsrc->right;
3311 } else {
3312 SrcRect.left = 0;
3313 SrcRect.top = 0;
3314 SrcRect.right = srcImpl->currentDesc.Width;
3315 SrcRect.bottom = srcImpl->currentDesc.Height;
3318 DstRect.left = dstx;
3319 DstRect.top=dsty;
3320 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3321 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3323 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3324 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3325 Flags |= WINEDDBLT_KEYSRC;
3326 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3327 Flags |= WINEDDBLT_KEYDEST;
3328 if(trans & WINEDDBLTFAST_WAIT)
3329 Flags |= WINEDDBLT_WAIT;
3330 if(trans & WINEDDBLTFAST_DONOTWAIT)
3331 Flags |= WINEDDBLT_DONOTWAIT;
3333 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3337 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3340 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3341 /** Check against the maximum texture sizes supported by the video card **/
3342 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3343 unsigned int pow2Width, pow2Height;
3344 const GlPixelFormatDesc *glDesc;
3346 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3347 /* Setup some glformat defaults */
3348 This->glDescription.glFormat = glDesc->glFormat;
3349 This->glDescription.glFormatInternal = glDesc->glInternal;
3350 This->glDescription.glType = glDesc->glType;
3352 This->glDescription.textureName = 0;
3353 This->glDescription.target = GL_TEXTURE_2D;
3355 /* Non-power2 support */
3356 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO)) {
3357 pow2Width = This->currentDesc.Width;
3358 pow2Height = This->currentDesc.Height;
3359 } else {
3360 /* Find the nearest pow2 match */
3361 pow2Width = pow2Height = 1;
3362 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3363 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3365 This->pow2Width = pow2Width;
3366 This->pow2Height = pow2Height;
3368 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3369 WINED3DFORMAT Format = This->resource.format;
3370 /** TODO: add support for non power two compressed textures **/
3371 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3372 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5) {
3373 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3374 This, This->currentDesc.Width, This->currentDesc.Height);
3375 return WINED3DERR_NOTAVAILABLE;
3379 if(pow2Width != This->currentDesc.Width ||
3380 pow2Height != This->currentDesc.Height) {
3381 This->Flags |= SFLAG_NONPOW2;
3384 TRACE("%p\n", This);
3385 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3386 /* one of three options
3387 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)
3388 2: Set the texture to the maximum size (bad idea)
3389 3: WARN and return WINED3DERR_NOTAVAILABLE;
3390 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.
3392 WARN("(%p) Creating an oversized surface\n", This);
3393 This->Flags |= SFLAG_OVERSIZE;
3395 /* This will be initialized on the first blt */
3396 This->glRect.left = 0;
3397 This->glRect.top = 0;
3398 This->glRect.right = 0;
3399 This->glRect.bottom = 0;
3400 } else {
3401 /* No oversize, gl rect is the full texture size */
3402 This->Flags &= ~SFLAG_OVERSIZE;
3403 This->glRect.left = 0;
3404 This->glRect.top = 0;
3405 This->glRect.right = This->pow2Width;
3406 This->glRect.bottom = This->pow2Height;
3409 This->Flags |= SFLAG_INSYSMEM;
3411 return WINED3D_OK;
3414 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
3415 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3417 TRACE("(%p)->(%s, %s)\n", iface,
3418 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3419 persistent ? "TRUE" : "FALSE");
3421 /* TODO: For offscreen textures with fbo offscreen rendering the drawable is the same as the texture.*/
3422 if(persistent) {
3423 This->Flags &= ~SFLAG_LOCATIONS;
3424 This->Flags |= flag;
3425 } else {
3426 This->Flags &= ~flag;
3430 struct coords {
3431 int x, y, z;
3434 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
3435 struct coords coords[4];
3436 RECT rect;
3437 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3439 if(rect_in) {
3440 rect = *rect_in;
3441 } else {
3442 rect.left = 0;
3443 rect.top = 0;
3444 rect.right = This->currentDesc.Width;
3445 rect.bottom = This->currentDesc.Height;
3448 ActivateContext(device, device->render_targets[0], CTXUSAGE_BLIT);
3449 ENTER_GL();
3451 if(This->glDescription.target == GL_TEXTURE_2D) {
3452 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
3453 checkGLcall("GL_TEXTURE_2D, This->glDescription.textureName)");
3454 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3455 checkGLcall("glTexParameteri");
3456 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3457 checkGLcall("glTexParameteri");
3459 coords[0].x = rect.left / This->pow2Width;
3460 coords[0].z = 0;
3462 coords[1].x = rect.left / This->pow2Width;
3463 coords[1].z = 0;
3465 coords[2].x = rect.right / This->pow2Width;
3466 coords[2].z = 0;
3468 coords[3].x = rect.right / This->pow2Width;
3469 coords[3].z = 0;
3471 coords[0].y = rect.top / This->pow2Height;
3472 coords[1].y = rect.bottom / This->pow2Height;
3473 coords[2].y = rect.bottom / This->pow2Height;
3474 coords[3].y = rect.top / This->pow2Height;
3475 } else {
3476 /* Must be a cube map */
3477 glDisable(GL_TEXTURE_2D);
3478 checkGLcall("glDisable(GL_TEXTURE_2D)");
3479 glEnable(GL_TEXTURE_CUBE_MAP_ARB);
3480 checkGLcall("glEnable(GL_TEXTURE_CUBE_MAP_ARB)");
3481 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName);
3482 checkGLcall("GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName)");
3483 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3484 checkGLcall("glTexParameteri");
3485 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3486 checkGLcall("glTexParameteri");
3488 switch(This->glDescription.target) {
3489 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
3490 coords[0].x = 1; coords[0].y = -1; coords[0].z = 1;
3491 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3492 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3493 coords[3].x = 1; coords[3].y = -1; coords[3].z = -1;
3494 break;
3496 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
3497 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3498 coords[1].x = -1; coords[1].y = 1; coords[1].z = 1;
3499 coords[2].x = -1; coords[2].y = 1; coords[2].z = -1;
3500 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3501 break;
3503 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
3504 coords[0].x = -1; coords[0].y = 1; coords[0].z = 1;
3505 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3506 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3507 coords[3].x = -1; coords[3].y = 1; coords[3].z = -1;
3508 break;
3510 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
3511 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3512 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3513 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3514 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3515 break;
3517 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
3518 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3519 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3520 coords[2].x = 1; coords[2].y = -1; coords[2].z = 1;
3521 coords[3].x = -1; coords[3].y = -1; coords[3].z = 1;
3522 break;
3524 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
3525 coords[0].x = -1; coords[0].y = -1; coords[0].z = -1;
3526 coords[1].x = 1; coords[1].y = -1; coords[1].z = -1;
3527 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3528 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3530 default:
3531 ERR("Unexpected texture target\n");
3532 LEAVE_GL();
3533 return;
3537 glBegin(GL_QUADS);
3538 glTexCoord3iv((GLint *) &coords[0]);
3539 glVertex2i(rect.left, device->render_offscreen ? rect.bottom : rect.top);
3541 glTexCoord3iv((GLint *) &coords[1]);
3542 glVertex2i(0, device->render_offscreen ? rect.top : rect.bottom);
3544 glTexCoord3iv((GLint *) &coords[2]);
3545 glVertex2i(rect.right, device->render_offscreen ? rect.top : rect.bottom);
3547 glTexCoord3iv((GLint *) &coords[3]);
3548 glVertex2i(rect.right, device->render_offscreen ? rect.bottom : rect.top);
3549 glEnd();
3550 checkGLcall("glEnd");
3552 if(This->glDescription.target != GL_TEXTURE_2D) {
3553 glEnable(GL_TEXTURE_2D);
3554 checkGLcall("glEnable(GL_TEXTURE_2D)");
3555 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3556 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3558 LEAVE_GL();
3561 /*****************************************************************************
3562 * IWineD3DSurface::LoadLocation
3564 * Copies the current surface data from wherever it is to the requested
3565 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
3566 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
3567 * multiple locations, the gl texture is prefered over the drawable, which is
3568 * prefered over system memory. The PBO counts as system memory. If rect is
3569 * not NULL, only the specified rectangle is copied(only supported for
3570 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
3571 * location is marked up to date after the copy.
3573 * Parameters:
3574 * flag: Surface location flag to be updated
3575 * rect: rectangle to be copied
3577 * Returns:
3578 * WINED3D_OK on success
3579 * WINED3DERR_DEVICELOST on an internal error
3581 *****************************************************************************/
3582 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
3583 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3584 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3585 GLenum format, internal, type;
3586 CONVERT_TYPES convert;
3587 int bpp;
3588 int width, pitch, outpitch;
3589 BYTE *mem;
3591 TRACE("(%p)->(%s, %p)\n", iface,
3592 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3593 rect);
3594 if(rect) {
3595 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
3598 /* TODO: For fbo targets, texture == drawable */
3599 if(This->Flags & flag) {
3600 TRACE("Location already up to date\n");
3601 return WINED3D_OK;
3604 if(!(This->Flags & SFLAG_LOCATIONS)) {
3605 ERR("Surface does not have any up to date location\n");
3606 This->Flags |= SFLAG_LOST;
3607 return WINED3DERR_DEVICELOST;
3610 if(flag == SFLAG_INSYSMEM) {
3611 surface_prepare_system_memory(This);
3613 /* Download the surface to system memory */
3614 if(This->Flags & SFLAG_INTEXTURE) {
3615 surface_download_data(This);
3616 } else {
3617 read_from_framebuffer(This, rect,
3618 This->resource.allocatedMemory,
3619 IWineD3DSurface_GetPitch(iface));
3621 } else if(flag == SFLAG_INDRAWABLE) {
3622 if(This->Flags & SFLAG_INTEXTURE) {
3623 surface_blt_to_drawable(This, rect);
3624 } else {
3625 flush_to_framebuffer_drawpixels(This);
3627 } else /* if(flag == SFLAG_INTEXTURE) */ {
3628 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
3630 if (This->Flags & SFLAG_INDRAWABLE) {
3631 GLint prevRead;
3633 ENTER_GL();
3634 glGetIntegerv(GL_READ_BUFFER, &prevRead);
3635 vcheckGLcall("glGetIntegerv");
3636 glReadBuffer(This->resource.wineD3DDevice->offscreenBuffer);
3637 vcheckGLcall("glReadBuffer");
3639 if(!(This->Flags & SFLAG_ALLOCATED)) {
3640 surface_allocate_surface(This, internal, This->pow2Width,
3641 This->pow2Height, format, type);
3644 clear_unused_channels(This);
3646 glCopyTexSubImage2D(This->glDescription.target,
3647 This->glDescription.level,
3648 0, 0, 0, 0,
3649 This->currentDesc.Width,
3650 This->currentDesc.Height);
3651 checkGLcall("glCopyTexSubImage2D");
3653 glReadBuffer(prevRead);
3654 vcheckGLcall("glReadBuffer");
3656 LEAVE_GL();
3658 TRACE("Updated target %d\n", This->glDescription.target);
3659 } else {
3660 /* The only place where LoadTexture() might get called when isInDraw=1
3661 * is ActivateContext where lastActiveRenderTarget is preloaded.
3663 if(iface == device->lastActiveRenderTarget && device->isInDraw)
3664 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
3666 /* Otherwise: System memory copy must be most up to date */
3668 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
3669 This->Flags |= SFLAG_GLCKEY;
3670 This->glCKey = This->SrcBltCKey;
3672 else This->Flags &= ~SFLAG_GLCKEY;
3674 /* The width is in 'length' not in bytes */
3675 width = This->currentDesc.Width;
3676 pitch = IWineD3DSurface_GetPitch(iface);
3678 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
3679 int height = This->currentDesc.Height;
3681 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
3682 outpitch = width * bpp;
3683 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
3685 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
3686 if(!mem) {
3687 ERR("Out of memory %d, %d!\n", outpitch, height);
3688 return WINED3DERR_OUTOFVIDEOMEMORY;
3690 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
3692 This->Flags |= SFLAG_CONVERTED;
3693 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
3694 d3dfmt_p8_upload_palette(iface, convert);
3695 This->Flags &= ~SFLAG_CONVERTED;
3696 mem = This->resource.allocatedMemory;
3697 } else {
3698 This->Flags &= ~SFLAG_CONVERTED;
3699 mem = This->resource.allocatedMemory;
3702 /* Make sure the correct pitch is used */
3703 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
3705 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
3706 TRACE("non power of two support\n");
3707 if(!(This->Flags & SFLAG_ALLOCATED)) {
3708 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
3710 if (mem || (This->Flags & SFLAG_PBO)) {
3711 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
3713 } else {
3714 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
3715 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
3717 if(!(This->Flags & SFLAG_ALLOCATED)) {
3718 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
3720 if (mem || (This->Flags & SFLAG_PBO)) {
3721 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
3725 /* Restore the default pitch */
3726 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
3728 /* Don't delete PBO memory */
3729 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
3730 HeapFree(GetProcessHeap(), 0, mem);
3734 if(rect == NULL) {
3735 This->Flags |= flag;
3738 return WINED3D_OK;
3741 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
3743 /* IUnknown */
3744 IWineD3DBaseSurfaceImpl_QueryInterface,
3745 IWineD3DBaseSurfaceImpl_AddRef,
3746 IWineD3DSurfaceImpl_Release,
3747 /* IWineD3DResource */
3748 IWineD3DBaseSurfaceImpl_GetParent,
3749 IWineD3DBaseSurfaceImpl_GetDevice,
3750 IWineD3DBaseSurfaceImpl_SetPrivateData,
3751 IWineD3DBaseSurfaceImpl_GetPrivateData,
3752 IWineD3DBaseSurfaceImpl_FreePrivateData,
3753 IWineD3DBaseSurfaceImpl_SetPriority,
3754 IWineD3DBaseSurfaceImpl_GetPriority,
3755 IWineD3DSurfaceImpl_PreLoad,
3756 IWineD3DBaseSurfaceImpl_GetType,
3757 /* IWineD3DSurface */
3758 IWineD3DBaseSurfaceImpl_GetContainer,
3759 IWineD3DBaseSurfaceImpl_GetDesc,
3760 IWineD3DSurfaceImpl_LockRect,
3761 IWineD3DSurfaceImpl_UnlockRect,
3762 IWineD3DSurfaceImpl_GetDC,
3763 IWineD3DSurfaceImpl_ReleaseDC,
3764 IWineD3DSurfaceImpl_Flip,
3765 IWineD3DSurfaceImpl_Blt,
3766 IWineD3DBaseSurfaceImpl_GetBltStatus,
3767 IWineD3DBaseSurfaceImpl_GetFlipStatus,
3768 IWineD3DBaseSurfaceImpl_IsLost,
3769 IWineD3DBaseSurfaceImpl_Restore,
3770 IWineD3DSurfaceImpl_BltFast,
3771 IWineD3DBaseSurfaceImpl_GetPalette,
3772 IWineD3DBaseSurfaceImpl_SetPalette,
3773 IWineD3DBaseSurfaceImpl_RealizePalette,
3774 IWineD3DBaseSurfaceImpl_SetColorKey,
3775 IWineD3DBaseSurfaceImpl_GetPitch,
3776 IWineD3DSurfaceImpl_SetMem,
3777 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
3778 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
3779 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
3780 IWineD3DBaseSurfaceImpl_UpdateOverlay,
3781 IWineD3DBaseSurfaceImpl_SetClipper,
3782 IWineD3DBaseSurfaceImpl_GetClipper,
3783 /* Internal use: */
3784 IWineD3DSurfaceImpl_AddDirtyRect,
3785 IWineD3DSurfaceImpl_LoadTexture,
3786 IWineD3DSurfaceImpl_SaveSnapshot,
3787 IWineD3DBaseSurfaceImpl_SetContainer,
3788 IWineD3DSurfaceImpl_SetGlTextureDesc,
3789 IWineD3DSurfaceImpl_GetGlDesc,
3790 IWineD3DSurfaceImpl_GetData,
3791 IWineD3DSurfaceImpl_SetFormat,
3792 IWineD3DSurfaceImpl_PrivateSetup,
3793 IWineD3DSurfaceImpl_ModifyLocation,
3794 IWineD3DSurfaceImpl_LoadLocation