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
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 if (This->resource.format == WINED3DFMT_DXT1 ||
41 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
42 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
43 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
44 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
46 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
47 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
49 if(This->Flags & SFLAG_PBO) {
50 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
51 checkGLcall("glBindBufferARB");
52 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, NULL));
53 checkGLcall("glGetCompressedTexImageARB()");
54 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
55 checkGLcall("glBindBufferARB");
57 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
58 checkGLcall("glGetCompressedTexImageARB()");
66 if(This->Flags & SFLAG_CONVERTED) {
67 FIXME("Read back converted textures unsupported\n");
71 if (This->Flags & SFLAG_NONPOW2) {
72 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
73 src_pitch = This->bytesPerPixel * This->pow2Width;
74 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
75 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
76 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
78 mem = This->resource.allocatedMemory;
81 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
82 This->glDescription.glFormat, This->glDescription.glType, mem);
84 if(This->Flags & SFLAG_PBO) {
85 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
86 checkGLcall("glBindBufferARB");
88 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
89 This->glDescription.glType, NULL);
90 checkGLcall("glGetTexImage()");
92 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
93 checkGLcall("glBindBufferARB");
95 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
96 This->glDescription.glType, mem);
97 checkGLcall("glGetTexImage()");
100 if (This->Flags & SFLAG_NONPOW2) {
101 LPBYTE src_data, dst_data;
104 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
105 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
106 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
108 * We're doing this...
110 * instead of boxing the texture :
111 * |<-texture width ->| -->pow2width| /\
112 * |111111111111111111| | |
113 * |222 Texture 222222| boxed empty | texture height
114 * |3333 Data 33333333| | |
115 * |444444444444444444| | \/
116 * ----------------------------------- |
117 * | boxed empty | boxed empty | pow2height
119 * -----------------------------------
122 * we're repacking the data to the expected texture width
124 * |<-texture width ->| -->pow2width| /\
125 * |111111111111111111222222222222222| |
126 * |222333333333333333333444444444444| texture height
130 * | empty | pow2height
132 * -----------------------------------
136 * |<-texture width ->| /\
137 * |111111111111111111|
138 * |222222222222222222|texture height
139 * |333333333333333333|
140 * |444444444444444444| \/
141 * --------------------
143 * this also means that any references to allocatedMemory should work with the data as if were a
144 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
146 * internally the texture is still stored in a boxed format so any references to textureName will
147 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
149 * Performance should not be an issue, because applications normally do not lock the surfaces when
150 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
151 * and doesn't have to be re-read.
154 dst_data = This->resource.allocatedMemory;
155 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
156 for (y = 1 ; y < This->currentDesc.Height; y++) {
157 /* skip the first row */
158 src_data += src_pitch;
159 dst_data += dst_pitch;
160 memcpy(dst_data, src_data, dst_pitch);
163 HeapFree(GetProcessHeap(), 0, mem);
166 /* Surface has now been downloaded */
167 This->Flags |= SFLAG_INSYSMEM;
170 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
171 if (This->resource.format == WINED3DFMT_DXT1 ||
172 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
173 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
174 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
175 FIXME("Using DXT1/3/5 without advertized support\n");
177 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
178 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
179 This->Flags |= SFLAG_CLIENT;
182 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
183 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
184 * function uses glCompressedTexImage2D instead of the SubImage call
186 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
189 if(This->Flags & SFLAG_PBO) {
190 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
191 checkGLcall("glBindBufferARB");
192 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
194 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
195 width, height, 0 /* border */, This->resource.size, NULL));
196 checkGLcall("glCompressedTexSubImage2D");
198 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
199 checkGLcall("glBindBufferARB");
201 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
202 width, height, 0 /* border */, This->resource.size, data));
203 checkGLcall("glCompressedTexSubImage2D");
208 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
211 if(This->Flags & SFLAG_PBO) {
212 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
213 checkGLcall("glBindBufferARB");
214 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
216 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
217 checkGLcall("glTexSubImage2D");
219 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
220 checkGLcall("glBindBufferARB");
223 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
224 checkGLcall("glTexSubImage2D");
231 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
232 BOOL enable_client_storage = FALSE;
234 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,
235 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
237 if (This->resource.format == WINED3DFMT_DXT1 ||
238 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
239 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
240 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
241 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
247 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
248 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
249 /* In some cases we want to disable client storage.
250 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
251 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
252 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
253 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
254 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
256 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
257 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
258 This->Flags &= ~SFLAG_CLIENT;
259 enable_client_storage = TRUE;
261 This->Flags |= SFLAG_CLIENT;
262 /* Below point opengl to our allocated texture memory */
265 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type,
266 This->Flags & SFLAG_CLIENT ? This->resource.allocatedMemory : NULL);
267 checkGLcall("glTexImage2D");
269 if(enable_client_storage) {
270 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
271 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
275 This->Flags |= SFLAG_ALLOCATED;
278 /* In D3D the depth stencil dimensions have to be greater than or equal to the
279 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
280 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
281 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
282 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
283 renderbuffer_entry_t *entry;
284 GLuint renderbuffer = 0;
285 unsigned int src_width, src_height;
287 src_width = This->pow2Width;
288 src_height = This->pow2Height;
290 /* A depth stencil smaller than the render target is not valid */
291 if (width > src_width || height > src_height) return;
293 /* Remove any renderbuffer set if the sizes match */
294 if (width == src_width && height == src_height) {
295 This->current_renderbuffer = NULL;
299 /* Look if we've already got a renderbuffer of the correct dimensions */
300 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
301 if (entry->width == width && entry->height == height) {
302 renderbuffer = entry->id;
303 This->current_renderbuffer = entry;
309 const GlPixelFormatDesc *glDesc;
310 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
312 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
313 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
314 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, glDesc->glFormat, width, height));
316 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
317 entry->width = width;
318 entry->height = height;
319 entry->id = renderbuffer;
320 list_add_head(&This->renderbuffers, &entry->entry);
322 This->current_renderbuffer = entry;
325 checkGLcall("set_compatible_renderbuffer");
328 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
329 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
330 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
332 TRACE("(%p) : swapchain %p\n", This, swapchain);
334 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
335 TRACE("Returning GL_BACK\n");
337 } else if (swapchain_impl->frontBuffer == iface) {
338 TRACE("Returning GL_FRONT\n");
342 FIXME("Higher back buffer, returning GL_BACK\n");
346 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
347 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
348 ULONG ref = InterlockedDecrement(&This->resource.ref);
349 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
351 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) This->resource.wineD3DDevice;
352 renderbuffer_entry_t *entry, *entry2;
353 TRACE("(%p) : cleaning up\n", This);
355 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
357 /* Need a context to destroy the texture. Use the currently active render target, but only if
358 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
359 * When destroying the primary rt, Uninit3D will activate a context before doing anything
361 if(device->render_targets && device->render_targets[0]) {
362 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
365 TRACE("Deleting texture %d\n", This->glDescription.textureName);
367 glDeleteTextures(1, &This->glDescription.textureName);
371 if(This->Flags & SFLAG_PBO) {
373 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
376 if(This->Flags & SFLAG_DIBSECTION) {
378 SelectObject(This->hDC, This->dib.holdbitmap);
380 /* Release the DIB section */
381 DeleteObject(This->dib.DIBsection);
382 This->dib.bitmap_data = NULL;
383 This->resource.allocatedMemory = NULL;
385 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
387 HeapFree(GetProcessHeap(), 0, This->palette9);
389 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
390 if(iface == device->ddraw_primary)
391 device->ddraw_primary = NULL;
393 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
394 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
395 HeapFree(GetProcessHeap(), 0, entry);
398 TRACE("(%p) Released\n", This);
399 HeapFree(GetProcessHeap(), 0, This);
405 /* ****************************************************
406 IWineD3DSurface IWineD3DResource parts follow
407 **************************************************** */
409 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
410 /* TODO: check for locks */
411 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
412 IWineD3DBaseTexture *baseTexture = NULL;
413 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
415 TRACE("(%p)Checking to see if the container is a base texture\n", This);
416 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
417 TRACE("Passing to container\n");
418 IWineD3DBaseTexture_PreLoad(baseTexture);
419 IWineD3DBaseTexture_Release(baseTexture);
421 TRACE("(%p) : About to load surface\n", This);
423 if(!device->isInDraw) {
424 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
428 glEnable(This->glDescription.target);/* make sure texture support is enabled in this context */
429 if (!This->glDescription.level) {
430 if (!This->glDescription.textureName) {
431 glGenTextures(1, &This->glDescription.textureName);
432 checkGLcall("glGenTextures");
433 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
435 glBindTexture(This->glDescription.target, This->glDescription.textureName);
436 checkGLcall("glBindTexture");
437 IWineD3DSurface_LoadTexture(iface, FALSE);
438 /* This is where we should be reducing the amount of GLMemoryUsed */
439 } else if (This->glDescription.textureName) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
440 /* assume this is a coding error not a real error for now */
441 FIXME("Mipmap surface has a glTexture bound to it!\n");
443 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
444 /* Tell opengl to try and keep this texture in video ram (well mostly) */
447 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
454 /* ******************************************************
455 IWineD3DSurface IWineD3DSurface parts follow
456 ****************************************************** */
458 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
459 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
460 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
461 if (This->glDescription.textureName == 0 && textureName != 0) {
462 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
463 IWineD3DSurface_AddDirtyRect(iface, NULL);
465 This->glDescription.textureName = textureName;
466 This->glDescription.target = target;
467 This->Flags &= ~SFLAG_ALLOCATED;
470 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
471 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
472 TRACE("(%p) : returning %p\n", This, &This->glDescription);
473 *glDescription = &This->glDescription;
476 /* TODO: think about moving this down to resource? */
477 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
478 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
479 /* 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 */
480 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
481 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
483 return (CONST void*)(This->resource.allocatedMemory);
486 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
487 IWineD3DSwapChainImpl *swapchain;
488 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
492 BYTE *row, *top, *bottom;
496 BOOL srcIsUpsideDown;
498 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
499 static BOOL warned = FALSE;
501 ERR("The application tries to lock the render target, but render target locking is disabled\n");
507 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
508 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
509 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
510 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
511 * context->last_was_blit set on the unlock.
513 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
516 /* Select the correct read buffer, and give some debug output.
517 * There is no need to keep track of the current read buffer or reset it, every part of the code
518 * that reads sets the read buffer as desired.
521 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
522 * Read from the back buffer
524 TRACE("Locking offscreen render target\n");
525 glReadBuffer(myDevice->offscreenBuffer);
526 srcIsUpsideDown = TRUE;
528 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
529 TRACE("Locking %#x buffer\n", buffer);
530 glReadBuffer(buffer);
531 checkGLcall("glReadBuffer");
533 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
534 srcIsUpsideDown = FALSE;
537 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
541 local_rect.right = This->currentDesc.Width;
542 local_rect.bottom = This->currentDesc.Height;
546 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
548 switch(This->resource.format)
552 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
553 /* In case of P8 render targets the index is stored in the alpha component */
555 type = GL_UNSIGNED_BYTE;
557 bpp = This->bytesPerPixel;
559 /* GL can't return palettized data, so read ARGB pixels into a
560 * separate block of memory and convert them into palettized format
561 * in software. Slow, but if the app means to use palettized render
562 * targets and locks it...
564 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
565 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
566 * for the color channels when palettizing the colors.
569 type = GL_UNSIGNED_BYTE;
571 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
573 ERR("Out of memory\n");
577 bpp = This->bytesPerPixel * 3;
584 fmt = This->glDescription.glFormat;
585 type = This->glDescription.glType;
586 bpp = This->bytesPerPixel;
589 if(This->Flags & SFLAG_PBO) {
590 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
591 checkGLcall("glBindBufferARB");
594 glReadPixels(local_rect.left, local_rect.top,
595 local_rect.right - local_rect.left,
596 local_rect.bottom - local_rect.top,
598 vcheckGLcall("glReadPixels");
600 if(This->Flags & SFLAG_PBO) {
601 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
602 checkGLcall("glBindBufferARB");
604 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
605 * to get a pointer to it and perform the flipping in software. This is a lot
606 * faster than calling glReadPixels for each line. In case we want more speed
607 * we should rerender it flipped in a FBO and read the data back from the FBO. */
608 if(!srcIsUpsideDown) {
609 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
610 checkGLcall("glBindBufferARB");
612 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
613 checkGLcall("glMapBufferARB");
617 /* TODO: Merge this with the palettization loop below for P8 targets */
618 if(!srcIsUpsideDown) {
620 /* glReadPixels returns the image upside down, and there is no way to prevent this.
621 Flip the lines in software */
622 len = (local_rect.right - local_rect.left) * bpp;
623 off = local_rect.left * bpp;
625 row = HeapAlloc(GetProcessHeap(), 0, len);
627 ERR("Out of memory\n");
628 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
633 top = mem + pitch * local_rect.top;
634 bottom = ((BYTE *) mem) + pitch * ( local_rect.bottom - local_rect.top - 1);
635 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
636 memcpy(row, top + off, len);
637 memcpy(top + off, bottom + off, len);
638 memcpy(bottom + off, row, len);
642 HeapFree(GetProcessHeap(), 0, row);
644 /* Unmap the temp PBO buffer */
645 if(This->Flags & SFLAG_PBO) {
646 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
647 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
651 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
652 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
653 * the same color but we have no choice.
654 * In case of render targets, the index is stored in the alpha component so no conversion is needed.
656 if((This->resource.format == WINED3DFMT_P8) && !(This->resource.usage & WINED3DUSAGE_RENDERTARGET)) {
658 DWORD width = pitch / 3;
661 pal = This->palette->palents;
663 pal = This->resource.wineD3DDevice->palettes[This->resource.wineD3DDevice->currentPalette];
666 for(y = local_rect.top; y < local_rect.bottom; y++) {
667 for(x = local_rect.left; x < local_rect.right; x++) {
668 /* start lines pixels */
669 BYTE *blue = (BYTE *) ((BYTE *) mem) + y * pitch + x * (sizeof(BYTE) * 3);
670 BYTE *green = blue + 1;
671 BYTE *red = green + 1;
673 for(c = 0; c < 256; c++) {
674 if(*red == pal[c].peRed &&
675 *green == pal[c].peGreen &&
676 *blue == pal[c].peBlue)
678 *((BYTE *) dest + y * width + x) = c;
684 HeapFree(GetProcessHeap(), 0, mem);
689 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
690 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
691 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
694 if(!(This->Flags & SFLAG_DYNLOCK)) {
696 /* MAXLOCKCOUNT is defined in wined3d_private.h */
697 if(This->lockCount > MAXLOCKCOUNT) {
698 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
699 This->Flags |= SFLAG_DYNLOCK;
703 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
704 * Also don't create a PBO for systemmem surfaces.
706 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
710 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
711 error = glGetError();
712 if(This->pbo == 0 || error != GL_NO_ERROR) {
713 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
716 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
718 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
719 checkGLcall("glBindBufferARB");
721 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
722 checkGLcall("glBufferDataARB");
724 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
725 checkGLcall("glBindBufferARB");
727 /* We don't need the system memory anymore and we can't even use it for PBOs */
728 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
729 This->resource.allocatedMemory = NULL;
730 This->resource.heapMemory = NULL;
731 This->Flags |= SFLAG_PBO;
733 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
734 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
737 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
738 This->resource.allocatedMemory =
739 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
740 if(This->Flags & SFLAG_INSYSMEM) {
741 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
746 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
747 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
748 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
749 IWineD3DSwapChain *swapchain = NULL;
751 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
753 /* This is also done in the base class, but we have to verify this before loading any data from
754 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
755 * may interfere, and all other bad things may happen
757 if (This->Flags & SFLAG_LOCKED) {
758 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
759 return WINED3DERR_INVALIDCALL;
762 if (Flags & WINED3DLOCK_DISCARD) {
763 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
764 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
765 This->Flags |= SFLAG_INSYSMEM;
768 if (This->Flags & SFLAG_INSYSMEM) {
769 TRACE("Local copy is up to date, not downloading data\n");
770 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
774 /* Now download the surface content from opengl
775 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
776 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
778 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
779 if(swapchain || iface == myDevice->render_targets[0]) {
780 const RECT *pass_rect = pRect;
782 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
783 * because most caller functions do not need that. So do that here
788 pRect->right == This->currentDesc.Width &&
789 pRect->bottom == This->currentDesc.Height) {
793 switch(wined3d_settings.rendertargetlock_mode) {
796 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
798 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
799 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
800 * This may be faster on some cards
802 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
809 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pRect);
815 if(swapchain) IWineD3DSwapChain_Release(swapchain);
817 } else if(iface == myDevice->stencilBufferTarget) {
818 /** the depth stencil in openGL has a format of GL_FLOAT
819 * which should be good for WINED3DFMT_D16_LOCKABLE
821 * it is unclear what format the stencil buffer is in except.
822 * 'Each index is converted to fixed point...
823 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
824 * mappings in the table GL_PIXEL_MAP_S_TO_S.
825 * glReadPixels(This->lockedRect.left,
826 * This->lockedRect.bottom - j - 1,
827 * This->lockedRect.right - This->lockedRect.left,
829 * GL_DEPTH_COMPONENT,
831 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
833 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
834 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
835 * none of that is the case the problem is not in this function :-)
836 ********************************************/
837 FIXME("Depth stencil locking not supported yet\n");
839 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
840 TRACE("locking an ordinary surface\n");
841 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
845 if(This->Flags & SFLAG_PBO) {
846 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
848 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
849 checkGLcall("glBindBufferARB");
851 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
852 if(This->resource.allocatedMemory) {
853 ERR("The surface already has PBO memory allocated!\n");
856 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
857 checkGLcall("glMapBufferARB");
859 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
860 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
861 checkGLcall("glBindBufferARB");
866 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
869 IWineD3DBaseTexture *pBaseTexture;
872 * as seen in msdn docs
874 IWineD3DSurface_AddDirtyRect(iface, &This->lockedRect);
876 /** Dirtify Container if needed */
877 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
878 TRACE("Making container dirty\n");
879 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
880 IWineD3DBaseTexture_Release(pBaseTexture);
882 TRACE("Surface is standalone, no need to dirty the container\n");
886 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
889 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This) {
891 GLint prev_rasterpos[4];
893 BOOL storechanged = FALSE, memory_allocated = FALSE;
897 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
898 IWineD3DDeviceImpl *myDevice = (IWineD3DDeviceImpl *) This->resource.wineD3DDevice;
899 IWineD3DSwapChainImpl *swapchain;
901 /* Activate the correct context for the render target */
902 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
905 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
907 /* Primary offscreen render target */
908 TRACE("Offscreen render target\n");
909 glDrawBuffer(myDevice->offscreenBuffer);
910 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
912 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
913 TRACE("Unlocking %#x buffer\n", buffer);
914 glDrawBuffer(buffer);
915 checkGLcall("glDrawBuffer");
917 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
920 glDisable(GL_TEXTURE_2D);
921 vcheckGLcall("glDisable(GL_TEXTURE_2D)");
924 vcheckGLcall("glFlush");
925 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
926 vcheckGLcall("glIntegerv");
927 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
928 vcheckGLcall("glIntegerv");
929 glPixelZoom(1.0, -1.0);
930 vcheckGLcall("glPixelZoom");
932 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
933 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
934 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
936 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
937 vcheckGLcall("glRasterPos2f");
939 /* Some drivers(radeon dri, others?) don't like exceptions during
940 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
941 * after ReleaseDC. Reading it will cause an exception, which x11drv will
942 * catch to put the dib section in InSync mode, which leads to a crash
943 * and a blocked x server on my radeon card.
945 * The following lines read the dib section so it is put in inSync mode
946 * before glDrawPixels is called and the crash is prevented. There won't
947 * be any interfering gdi accesses, because UnlockRect is called from
948 * ReleaseDC, and the app won't use the dc any more afterwards.
950 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
952 read = This->resource.allocatedMemory[0];
955 switch (This->resource.format) {
956 /* No special care needed */
957 case WINED3DFMT_A4R4G4B4:
958 case WINED3DFMT_R5G6B5:
959 case WINED3DFMT_A1R5G5B5:
960 case WINED3DFMT_R8G8B8:
961 type = This->glDescription.glType;
962 fmt = This->glDescription.glFormat;
963 mem = This->resource.allocatedMemory;
964 bpp = This->bytesPerPixel;
967 case WINED3DFMT_X4R4G4B4:
970 unsigned short *data;
971 data = (unsigned short *)This->resource.allocatedMemory;
972 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
978 type = This->glDescription.glType;
979 fmt = This->glDescription.glFormat;
980 mem = This->resource.allocatedMemory;
981 bpp = This->bytesPerPixel;
985 case WINED3DFMT_X1R5G5B5:
988 unsigned short *data;
989 data = (unsigned short *)This->resource.allocatedMemory;
990 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
996 type = This->glDescription.glType;
997 fmt = This->glDescription.glFormat;
998 mem = This->resource.allocatedMemory;
999 bpp = This->bytesPerPixel;
1003 case WINED3DFMT_X8R8G8B8:
1005 /* make sure the X byte is set to alpha on, since it
1006 could be any random value. This fixes the intro movie in Pirates! */
1009 data = (unsigned int *)This->resource.allocatedMemory;
1010 size = (This->lockedRect.bottom - This->lockedRect.top) * (This->lockedRect.right - This->lockedRect.left);
1012 *data |= 0xFF000000;
1019 case WINED3DFMT_A8R8G8B8:
1021 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
1022 vcheckGLcall("glPixelStorei");
1023 storechanged = TRUE;
1024 type = This->glDescription.glType;
1025 fmt = This->glDescription.glFormat;
1026 mem = This->resource.allocatedMemory;
1027 bpp = This->bytesPerPixel;
1031 case WINED3DFMT_A2R10G10B10:
1033 glPixelStorei(GL_PACK_SWAP_BYTES, TRUE);
1034 vcheckGLcall("glPixelStorei");
1035 storechanged = TRUE;
1036 type = This->glDescription.glType;
1037 fmt = This->glDescription.glFormat;
1038 mem = This->resource.allocatedMemory;
1039 bpp = This->bytesPerPixel;
1045 int height = This->glRect.bottom - This->glRect.top;
1046 type = GL_UNSIGNED_BYTE;
1049 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * sizeof(DWORD));
1051 ERR("Out of memory\n");
1054 memory_allocated = TRUE;
1055 d3dfmt_convert_surface(This->resource.allocatedMemory,
1063 bpp = This->bytesPerPixel * 4;
1069 FIXME("Unsupported Format %u in locking func\n", This->resource.format);
1072 type = This->glDescription.glType;
1073 fmt = This->glDescription.glFormat;
1074 mem = This->resource.allocatedMemory;
1075 bpp = This->bytesPerPixel;
1078 if(This->Flags & SFLAG_PBO) {
1079 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1080 checkGLcall("glBindBufferARB");
1083 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1084 (This->lockedRect.bottom - This->lockedRect.top)-1,
1086 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1087 checkGLcall("glDrawPixels");
1090 if(This->Flags & SFLAG_PBO) {
1091 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1092 checkGLcall("glBindBufferARB");
1095 glPixelZoom(1.0,1.0);
1096 vcheckGLcall("glPixelZoom");
1098 glRasterPos3iv(&prev_rasterpos[0]);
1099 vcheckGLcall("glRasterPos3iv");
1101 /* Reset to previous pack row length */
1102 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1103 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1105 glPixelStorei(GL_PACK_SWAP_BYTES, prev_store);
1106 vcheckGLcall("glPixelStorei GL_PACK_SWAP_BYTES");
1109 /* Blitting environment requires that 2D texturing is enabled. It was turned off before,
1112 glEnable(GL_TEXTURE_2D);
1113 checkGLcall("glEnable(GL_TEXTURE_2D)");
1115 if(memory_allocated) HeapFree(GetProcessHeap(), 0, mem);
1118 glDrawBuffer(myDevice->offscreenBuffer);
1119 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1120 } else if(swapchain->backBuffer) {
1121 glDrawBuffer(GL_BACK);
1122 checkGLcall("glDrawBuffer(GL_BACK)");
1124 glDrawBuffer(GL_FRONT);
1125 checkGLcall("glDrawBuffer(GL_FRONT)");
1132 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1133 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1134 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1135 IWineD3DSwapChainImpl *swapchain = NULL;
1138 if (!(This->Flags & SFLAG_LOCKED)) {
1139 WARN("trying to Unlock an unlocked surf@%p\n", This);
1140 return WINED3DERR_INVALIDCALL;
1143 if (This->Flags & SFLAG_PBO) {
1144 TRACE("Freeing PBO memory\n");
1145 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1147 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1148 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1149 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1150 checkGLcall("glUnmapBufferARB");
1152 This->resource.allocatedMemory = NULL;
1155 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1157 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1158 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1162 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1163 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1164 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1165 static BOOL warned = FALSE;
1167 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1170 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1174 if(This->dirtyRect.left == 0 &&
1175 This->dirtyRect.top == 0 &&
1176 This->dirtyRect.right == This->currentDesc.Width &&
1177 This->dirtyRect.bottom == This->currentDesc.Height) {
1180 /* TODO: Proper partial rectangle tracking */
1181 fullsurface = FALSE;
1182 This->Flags |= SFLAG_INSYSMEM;
1185 switch(wined3d_settings.rendertargetlock_mode) {
1189 if (This->glDescription.textureName == 0) {
1190 glGenTextures(1, &This->glDescription.textureName);
1191 checkGLcall("glGenTextures");
1193 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
1194 checkGLcall("glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);");
1196 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1202 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1207 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1208 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1209 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1210 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1211 * not fully up to date because only a subrectangle was read in LockRect.
1213 This->Flags &= ~SFLAG_INSYSMEM;
1214 This->Flags |= SFLAG_INDRAWABLE;
1217 This->dirtyRect.left = This->currentDesc.Width;
1218 This->dirtyRect.top = This->currentDesc.Height;
1219 This->dirtyRect.right = 0;
1220 This->dirtyRect.bottom = 0;
1221 } else if(iface == myDevice->stencilBufferTarget) {
1222 FIXME("Depth Stencil buffer locking is not implemented\n");
1224 /* The rest should be a normal texture */
1225 IWineD3DBaseTextureImpl *impl;
1226 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1227 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1228 * states need resetting
1230 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1231 if(impl->baseTexture.bindCount) {
1232 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1234 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1239 This->Flags &= ~SFLAG_LOCKED;
1240 memset(&This->lockedRect, 0, sizeof(RECT));
1244 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1245 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1246 WINED3DLOCKED_RECT lock;
1250 TRACE("(%p)->(%p)\n",This,pHDC);
1252 if(This->Flags & SFLAG_USERPTR) {
1253 ERR("Not supported on surfaces with an application-provided surfaces\n");
1254 return WINEDDERR_NODC;
1257 /* Give more detailed info for ddraw */
1258 if (This->Flags & SFLAG_DCINUSE)
1259 return WINEDDERR_DCALREADYCREATED;
1261 /* Can't GetDC if the surface is locked */
1262 if (This->Flags & SFLAG_LOCKED)
1263 return WINED3DERR_INVALIDCALL;
1265 memset(&lock, 0, sizeof(lock)); /* To be sure */
1267 /* Create a DIB section if there isn't a hdc yet */
1269 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1270 if(This->Flags & SFLAG_CLIENT) {
1271 IWineD3DSurface_PreLoad(iface);
1274 /* Use the dib section from now on if we are not using a PBO */
1275 if(!(This->Flags & SFLAG_PBO))
1276 This->resource.allocatedMemory = This->dib.bitmap_data;
1279 /* Lock the surface */
1280 hr = IWineD3DSurface_LockRect(iface,
1285 if(This->Flags & SFLAG_PBO) {
1286 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1287 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1291 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1292 /* keep the dib section */
1296 if(This->resource.format == WINED3DFMT_P8 ||
1297 This->resource.format == WINED3DFMT_A8P8) {
1300 PALETTEENTRY ent[256];
1302 GetPaletteEntries(This->palette->hpal, 0, 256, ent);
1303 for (n=0; n<256; n++) {
1304 col[n].rgbRed = ent[n].peRed;
1305 col[n].rgbGreen = ent[n].peGreen;
1306 col[n].rgbBlue = ent[n].peBlue;
1307 col[n].rgbReserved = 0;
1310 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1312 for (n=0; n<256; n++) {
1313 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
1314 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
1315 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
1316 col[n].rgbReserved = 0;
1320 SetDIBColorTable(This->hDC, 0, 256, col);
1324 TRACE("returning %p\n",*pHDC);
1325 This->Flags |= SFLAG_DCINUSE;
1330 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1331 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1333 TRACE("(%p)->(%p)\n",This,hDC);
1335 if (!(This->Flags & SFLAG_DCINUSE))
1336 return WINED3DERR_INVALIDCALL;
1338 if (This->hDC !=hDC) {
1339 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1340 return WINED3DERR_INVALIDCALL;
1343 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1344 /* Copy the contents of the DIB over to the PBO */
1345 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1348 /* we locked first, so unlock now */
1349 IWineD3DSurface_UnlockRect(iface);
1351 This->Flags &= ~SFLAG_DCINUSE;
1356 /* ******************************************************
1357 IWineD3DSurface Internal (No mapping to directx api) parts follow
1358 ****************************************************** */
1360 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) {
1361 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1362 const GlPixelFormatDesc *glDesc;
1363 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1365 /* Default values: From the surface */
1366 *format = glDesc->glFormat;
1367 *internal = srgb_mode?glDesc->glGammaInternal:glDesc->glInternal;
1368 *type = glDesc->glType;
1369 *convert = NO_CONVERSION;
1370 *target_bpp = This->bytesPerPixel;
1372 /* Ok, now look if we have to do any conversion */
1373 switch(This->resource.format) {
1378 /* Use conversion when the paletted texture extension is not available, or when it is available make sure it is used
1379 * for texturing as it won't work for calls like glDraw-/glReadPixels and further also use conversion in case of color keying.
1381 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) || colorkey_active || (!use_texturing && GL_SUPPORT(EXT_PALETTED_TEXTURE)) ) {
1383 *internal = GL_RGBA;
1384 *type = GL_UNSIGNED_BYTE;
1386 if(colorkey_active) {
1387 *convert = CONVERT_PALETTED_CK;
1389 *convert = CONVERT_PALETTED;
1392 else if(GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1394 *internal = GL_RGBA;
1395 *type = GL_UNSIGNED_BYTE;
1401 case WINED3DFMT_R3G3B2:
1402 /* **********************
1403 GL_UNSIGNED_BYTE_3_3_2
1404 ********************** */
1405 if (colorkey_active) {
1406 /* This texture format will never be used.. So do not care about color keying
1407 up until the point in time it will be needed :-) */
1408 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1412 case WINED3DFMT_R5G6B5:
1413 if (colorkey_active) {
1414 *convert = CONVERT_CK_565;
1416 *internal = GL_RGBA;
1417 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1421 case WINED3DFMT_X1R5G5B5:
1422 if (colorkey_active) {
1423 *convert = CONVERT_CK_5551;
1425 *internal = GL_RGBA;
1426 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1430 case WINED3DFMT_R8G8B8:
1431 if (colorkey_active) {
1432 *convert = CONVERT_CK_RGB24;
1434 *internal = GL_RGBA;
1435 *type = GL_UNSIGNED_INT_8_8_8_8;
1440 case WINED3DFMT_X8R8G8B8:
1441 if (colorkey_active) {
1442 *convert = CONVERT_RGB32_888;
1444 *internal = GL_RGBA;
1445 *type = GL_UNSIGNED_INT_8_8_8_8;
1449 case WINED3DFMT_V8U8:
1450 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1451 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1452 *format = GL_DUDV_ATI;
1453 *internal = GL_DU8DV8_ATI;
1455 /* No conversion - Just change the gl type */
1458 *convert = CONVERT_V8U8;
1460 *internal = GL_RGB8;
1461 *type = GL_UNSIGNED_BYTE;
1465 case WINED3DFMT_L6V5U5:
1466 *convert = CONVERT_L6V5U5;
1467 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1469 /* Use format and types from table */
1471 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1474 *internal = GL_RGB5;
1475 *type = GL_UNSIGNED_SHORT_5_6_5;
1479 case WINED3DFMT_X8L8V8U8:
1480 *convert = CONVERT_X8L8V8U8;
1482 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1483 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1484 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1485 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1486 * the needed type and format parameter, so the internal format contains a
1487 * 4th component, which is returned as alpha
1490 /* Not supported by GL_ATI_envmap_bumpmap */
1492 *internal = GL_RGB8;
1493 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1497 case WINED3DFMT_Q8W8V8U8:
1498 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1499 *convert = CONVERT_Q8W8V8U8;
1501 *internal = GL_RGBA8;
1502 *type = GL_UNSIGNED_BYTE;
1504 /* Not supported by GL_ATI_envmap_bumpmap */
1507 case WINED3DFMT_V16U16:
1508 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1509 *convert = CONVERT_V16U16;
1511 *internal = GL_RGB16_EXT;
1512 *type = GL_UNSIGNED_SHORT;
1514 /* What should I do here about GL_ATI_envmap_bumpmap?
1515 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1519 case WINED3DFMT_A4L4:
1520 /* A4L4 exists as an internal gl format, but for some reason there is not
1521 * format+type combination to load it. Thus convert it to A8L8, then load it
1522 * with A4L4 internal, but A8L8 format+type
1524 *convert = CONVERT_A4L4;
1525 *format = GL_LUMINANCE_ALPHA;
1526 *internal = GL_LUMINANCE4_ALPHA4;
1527 *type = GL_UNSIGNED_BYTE;
1531 case WINED3DFMT_R32F:
1532 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1533 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1534 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1537 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1539 *convert = CONVERT_R32F;
1541 *internal = GL_RGB32F_ARB;
1546 case WINED3DFMT_R16F:
1547 /* Similar to R32F */
1548 *convert = CONVERT_R16F;
1550 *internal = GL_RGB16F_ARB;
1551 *type = GL_HALF_FLOAT_ARB;
1562 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This) {
1563 BYTE *source, *dest;
1564 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1569 memcpy(dst, src, pitch * height);
1572 case CONVERT_PALETTED:
1573 case CONVERT_PALETTED_CK:
1575 IWineD3DPaletteImpl* pal = This->palette;
1580 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1583 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1585 for (y = 0; y < height; y++)
1587 source = src + pitch * y;
1588 dest = dst + outpitch * y;
1589 /* This is an 1 bpp format, using the width here is fine */
1590 for (x = 0; x < width; x++) {
1591 BYTE color = *source++;
1592 *dest++ = table[color][0];
1593 *dest++ = table[color][1];
1594 *dest++ = table[color][2];
1595 *dest++ = table[color][3];
1601 case CONVERT_CK_565:
1603 /* Converting the 565 format in 5551 packed to emulate color-keying.
1605 Note : in all these conversion, it would be best to average the averaging
1606 pixels to get the color of the pixel that will be color-keyed to
1607 prevent 'color bleeding'. This will be done later on if ever it is
1610 Note2: Nvidia documents say that their driver does not support alpha + color keying
1611 on the same surface and disables color keying in such a case
1617 TRACE("Color keyed 565\n");
1619 for (y = 0; y < height; y++) {
1620 Source = (WORD *) (src + y * pitch);
1621 Dest = (WORD *) (dst + y * outpitch);
1622 for (x = 0; x < width; x++ ) {
1623 WORD color = *Source++;
1624 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1625 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1626 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1635 case CONVERT_CK_5551:
1637 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1641 TRACE("Color keyed 5551\n");
1642 for (y = 0; y < height; y++) {
1643 Source = (WORD *) (src + y * pitch);
1644 Dest = (WORD *) (dst + y * outpitch);
1645 for (x = 0; x < width; x++ ) {
1646 WORD color = *Source++;
1648 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1649 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1653 *Dest &= ~(1 << 15);
1665 unsigned char *Dest;
1666 for(y = 0; y < height; y++) {
1667 Source = (short *) (src + y * pitch);
1668 Dest = (unsigned char *) (dst + y * outpitch);
1669 for (x = 0; x < width; x++ ) {
1670 long color = (*Source++);
1671 /* B */ Dest[0] = 0xff;
1672 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1673 /* R */ Dest[2] = (color) + 128; /* U */
1680 case CONVERT_V16U16:
1684 unsigned short *Dest;
1685 for(y = 0; y < height; y++) {
1686 Source = (DWORD *) (src + y * pitch);
1687 Dest = (unsigned short *) (dst + y * outpitch);
1688 for (x = 0; x < width; x++ ) {
1689 DWORD color = (*Source++);
1690 /* B */ Dest[0] = 0xffff;
1691 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1692 /* R */ Dest[2] = (color ) + 32768; /* U */
1699 case CONVERT_Q8W8V8U8:
1703 unsigned char *Dest;
1704 for(y = 0; y < height; y++) {
1705 Source = (DWORD *) (src + y * pitch);
1706 Dest = (unsigned char *) (dst + y * outpitch);
1707 for (x = 0; x < width; x++ ) {
1708 long color = (*Source++);
1709 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1710 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1711 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1712 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1719 case CONVERT_L6V5U5:
1723 unsigned char *Dest;
1725 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1726 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1727 * fixed function and shaders without further conversion once the surface is
1730 for(y = 0; y < height; y++) {
1731 Source = (WORD *) (src + y * pitch);
1732 Dest = (unsigned char *) (dst + y * outpitch);
1733 for (x = 0; x < width; x++ ) {
1734 short color = (*Source++);
1735 unsigned char l = ((color >> 10) & 0xfc);
1736 char v = ((color >> 5) & 0x3e);
1737 char u = ((color ) & 0x1f);
1739 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1740 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1741 * shift. GL reads a signed value and converts it into an unsigned value.
1743 /* M */ Dest[2] = l << 1;
1745 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1746 * from 5 bit values to 8 bit values.
1748 /* V */ Dest[1] = v << 3;
1749 /* U */ Dest[0] = u << 3;
1754 for(y = 0; y < height; y++) {
1755 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
1756 Source = (WORD *) (src + y * pitch);
1757 for (x = 0; x < width; x++ ) {
1758 short color = (*Source++);
1759 unsigned char l = ((color >> 10) & 0xfc);
1760 short v = ((color >> 5) & 0x3e);
1761 short u = ((color ) & 0x1f);
1762 short v_conv = v + 16;
1763 short u_conv = u + 16;
1765 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
1773 case CONVERT_X8L8V8U8:
1777 unsigned char *Dest;
1779 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1780 /* This implementation works with the fixed function pipeline and shaders
1781 * without further modification after converting the surface.
1783 for(y = 0; y < height; y++) {
1784 Source = (DWORD *) (src + y * pitch);
1785 Dest = (unsigned char *) (dst + y * outpitch);
1786 for (x = 0; x < width; x++ ) {
1787 long color = (*Source++);
1788 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
1789 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
1790 /* U */ Dest[0] = (color & 0xff); /* U */
1791 /* I */ Dest[3] = 255; /* X */
1796 /* Doesn't work correctly with the fixed function pipeline, but can work in
1797 * shaders if the shader is adjusted. (There's no use for this format in gl's
1798 * standard fixed function pipeline anyway).
1800 for(y = 0; y < height; y++) {
1801 Source = (DWORD *) (src + y * pitch);
1802 Dest = (unsigned char *) (dst + y * outpitch);
1803 for (x = 0; x < width; x++ ) {
1804 long color = (*Source++);
1805 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
1806 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1807 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1818 unsigned char *Source;
1819 unsigned char *Dest;
1820 for(y = 0; y < height; y++) {
1821 Source = (unsigned char *) (src + y * pitch);
1822 Dest = (unsigned char *) (dst + y * outpitch);
1823 for (x = 0; x < width; x++ ) {
1824 unsigned char color = (*Source++);
1825 /* A */ Dest[1] = (color & 0xf0) << 0;
1826 /* L */ Dest[0] = (color & 0x0f) << 4;
1838 for(y = 0; y < height; y++) {
1839 Source = (float *) (src + y * pitch);
1840 Dest = (float *) (dst + y * outpitch);
1841 for (x = 0; x < width; x++ ) {
1842 float color = (*Source++);
1858 for(y = 0; y < height; y++) {
1859 Source = (WORD *) (src + y * pitch);
1860 Dest = (WORD *) (dst + y * outpitch);
1861 for (x = 0; x < width; x++ ) {
1862 WORD color = (*Source++);
1872 ERR("Unsupported conversation type %d\n", convert);
1877 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
1878 IWineD3DPaletteImpl* pal = This->palette;
1879 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1880 BOOL index_in_alpha = FALSE;
1883 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
1884 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
1885 * is slow. Further RGB->P8 conversion is not possible because palettes can have
1886 * duplicate entries. Store the color key in the unused alpha component to speed the
1887 * download up and to make conversion unneeded. */
1888 if (device->render_targets && device->render_targets[0]) {
1889 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
1891 if(render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
1892 index_in_alpha = TRUE;
1896 /* Still no palette? Use the device's palette */
1897 /* Get the surface's palette */
1898 for (i = 0; i < 256; i++) {
1899 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1900 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1901 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1903 if(index_in_alpha) {
1905 } else if (colorkey &&
1906 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1907 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1908 /* We should maybe here put a more 'neutral' color than the standard bright purple
1909 one often used by application to prevent the nice purple borders when bi-linear
1917 TRACE("Using surface palette %p\n", pal);
1918 /* Get the surface's palette */
1919 for (i = 0; i < 256; i++) {
1920 table[i][0] = pal->palents[i].peRed;
1921 table[i][1] = pal->palents[i].peGreen;
1922 table[i][2] = pal->palents[i].peBlue;
1924 if(index_in_alpha) {
1927 else if (colorkey &&
1928 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1929 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1930 /* We should maybe here put a more 'neutral' color than the standard bright purple
1931 one often used by application to prevent the nice purple borders when bi-linear
1934 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
1935 table[i][3] = pal->palents[i].peFlags;
1943 const char *fragment_palette_conversion =
1946 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n" /* { 255/256, 0.5/255*255/256, 0, 0 } */
1947 "TEX index.x, fragment.texcoord[0], texture[0], 2D;\n" /* store the red-component of the current pixel */
1948 "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 */
1949 "TEX result.color, index, texture[1], 1D;\n" /* use the red-component as a index in the palette to get the final color */
1952 /* This function is used in case of 8bit paletted textures to upload the palette.
1953 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
1954 extensions like ATI_fragment_shaders is possible.
1956 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
1957 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1959 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1961 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1963 /* Try to use the paletted texture extension */
1964 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
1966 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
1967 GL_EXTCALL(glColorTableEXT(GL_TEXTURE_2D,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
1971 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
1972 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
1973 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
1975 /* Create the fragment program if we don't have it */
1976 if(!device->paletteConversionShader)
1978 glEnable(GL_FRAGMENT_PROGRAM_ARB);
1979 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
1980 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
1981 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), (const GLbyte *)fragment_palette_conversion));
1982 glDisable(GL_FRAGMENT_PROGRAM_ARB);
1985 glEnable(GL_FRAGMENT_PROGRAM_ARB);
1986 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
1988 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
1989 glEnable(GL_TEXTURE_1D);
1990 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
1992 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1993 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
1994 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1995 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
1997 /* Switch back to unit 0 in which the 2D texture will be stored. */
1998 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2000 /* Rebind the texture because it isn't bound anymore */
2001 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2005 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2006 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2008 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2009 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2010 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2015 if(This->palette9) {
2016 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2020 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2022 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2026 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
2027 GLboolean oldwrite[4];
2029 /* Some formats have only some color channels, and the others are 1.0.
2030 * since our rendering renders to all channels, and those pixel formats
2031 * are emulated by using a full texture with the other channels set to 1.0
2032 * manually, clear the unused channels.
2034 * This could be done with hacking colorwriteenable to mask the colors,
2035 * but before drawing the buffer would have to be cleared too, so there's
2038 switch(This->resource.format) {
2039 case WINED3DFMT_R16F:
2040 case WINED3DFMT_R32F:
2041 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2042 /* Do not activate a context, the correct drawable is active already
2043 * though just the read buffer is set, make sure to have the correct draw
2046 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2047 glDisable(GL_SCISSOR_TEST);
2048 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2049 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2050 glClearColor(0.0, 1.0, 1.0, 1.0);
2051 glClear(GL_COLOR_BUFFER_BIT);
2052 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2053 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2054 checkGLcall("Unused channel clear\n");
2061 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2062 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2064 if (!(This->Flags & SFLAG_INTEXTURE)) {
2065 TRACE("Reloading because surface is dirty\n");
2066 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2067 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2068 /* Reload: vice versa OR */
2069 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2070 /* Also reload: Color key is active AND the color key has changed */
2071 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2072 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2073 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2074 TRACE("Reloading because of color keying\n");
2075 /* To perform the color key conversion we need a sysmem copy of
2076 * the surface. Make sure we have it
2078 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2079 } else if(palette9_changed(This)) {
2080 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
2081 /* TODO: This is not necessarily needed with hw palettized texture support */
2082 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2084 TRACE("surface is already in texture\n");
2088 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2089 * These resources are not bound by device size or format restrictions. Because of this,
2090 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2091 * However, these resources can always be created, locked, and copied.
2093 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2095 FIXME("(%p) Operation not supported for scratch textures\n",This);
2096 return WINED3DERR_INVALIDCALL;
2099 This->srgb = srgb_mode;
2100 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* no partial locking for textures yet */);
2104 static unsigned int gen = 0;
2107 if ((gen % 10) == 0) {
2108 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2109 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2112 * debugging crash code
2121 if (!(This->Flags & SFLAG_DONOTFREE)) {
2122 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2123 This->resource.allocatedMemory = NULL;
2124 This->resource.heapMemory = NULL;
2125 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2133 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2136 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2137 char *allocatedMemory;
2139 IWineD3DSwapChain *swapChain = NULL;
2141 GLuint tmpTexture = 0;
2144 Textures may not be stored in ->allocatedgMemory and a GlTexture
2145 so we should lock the surface before saving a snapshot, or at least check that
2147 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2148 by calling GetTexImage and in compressed form by calling
2149 GetCompressedTexImageARB. Queried compressed images can be saved and
2150 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2151 texture images do not need to be processed by the GL and should
2152 significantly improve texture loading performance relative to uncompressed
2155 /* Setup the width and height to be the internal texture width and height. */
2156 width = This->pow2Width;
2157 height = This->pow2Height;
2158 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2159 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2161 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2162 /* if were not a real texture then read the back buffer into a real texture */
2163 /* we don't want to interfere with the back buffer so read the data into a temporary
2164 * texture and then save the data out of the temporary texture
2168 TRACE("(%p) Reading render target into texture\n", This);
2169 glEnable(GL_TEXTURE_2D);
2171 glGenTextures(1, &tmpTexture);
2172 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2174 glTexImage2D(GL_TEXTURE_2D,
2181 GL_UNSIGNED_INT_8_8_8_8_REV,
2184 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2185 vcheckGLcall("glGetIntegerv");
2186 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2187 vcheckGLcall("glReadBuffer");
2188 glCopyTexImage2D(GL_TEXTURE_2D,
2197 checkGLcall("glCopyTexImage2D");
2198 glReadBuffer(prevRead);
2201 } else { /* bind the real texture, and make sure it up to date */
2202 IWineD3DSurface_PreLoad(iface);
2204 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2206 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2207 glGetTexImage(GL_TEXTURE_2D,
2208 This->glDescription.level,
2210 GL_UNSIGNED_INT_8_8_8_8_REV,
2212 checkGLcall("glTexImage2D");
2214 glBindTexture(GL_TEXTURE_2D, 0);
2215 glDeleteTextures(1, &tmpTexture);
2219 f = fopen(filename, "w+");
2221 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2222 return WINED3DERR_INVALIDCALL;
2224 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2225 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2240 fwrite(&width,2,1,f);
2242 fwrite(&height,2,1,f);
2247 /* 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 */
2249 textureRow = allocatedMemory + (width * (height - 1) *4);
2251 textureRow = allocatedMemory;
2252 for (y = 0 ; y < height; y++) {
2253 for (i = 0; i < width; i++) {
2254 color = *((DWORD*)textureRow);
2255 fputc((color >> 16) & 0xFF, f); /* B */
2256 fputc((color >> 8) & 0xFF, f); /* G */
2257 fputc((color >> 0) & 0xFF, f); /* R */
2258 fputc((color >> 24) & 0xFF, f); /* A */
2261 /* take two rows of the pointer to the texture memory */
2263 (textureRow-= width << 3);
2266 TRACE("Closing file\n");
2270 IWineD3DSwapChain_Release(swapChain);
2272 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2277 * Slightly inefficient way to handle multiple dirty rects but it works :)
2279 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2280 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2281 IWineD3DBaseTexture *baseTexture = NULL;
2283 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2284 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
2286 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2287 if (NULL != pDirtyRect) {
2288 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2289 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2290 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2291 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2293 This->dirtyRect.left = 0;
2294 This->dirtyRect.top = 0;
2295 This->dirtyRect.right = This->currentDesc.Width;
2296 This->dirtyRect.bottom = This->currentDesc.Height;
2298 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2299 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2300 /* if the container is a basetexture then mark it dirty. */
2301 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2302 TRACE("Passing to container\n");
2303 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2304 IWineD3DBaseTexture_Release(baseTexture);
2309 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2310 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2312 const GlPixelFormatDesc *glDesc;
2313 getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2315 TRACE("(%p) : Calling base function first\n", This);
2316 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2318 /* Setup some glformat defaults */
2319 This->glDescription.glFormat = glDesc->glFormat;
2320 This->glDescription.glFormatInternal = glDesc->glInternal;
2321 This->glDescription.glType = glDesc->glType;
2323 This->Flags &= ~SFLAG_ALLOCATED;
2324 TRACE("(%p) : glFormat %d, glFotmatInternal %d, glType %d\n", This,
2325 This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2330 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2331 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2333 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2334 WARN("Surface is locked or the HDC is in use\n");
2335 return WINED3DERR_INVALIDCALL;
2338 if(Mem && Mem != This->resource.allocatedMemory) {
2339 void *release = NULL;
2341 /* Do I have to copy the old surface content? */
2342 if(This->Flags & SFLAG_DIBSECTION) {
2343 /* Release the DC. No need to hold the critical section for the update
2344 * Thread because this thread runs only on front buffers, but this method
2345 * fails for render targets in the check above.
2347 SelectObject(This->hDC, This->dib.holdbitmap);
2348 DeleteDC(This->hDC);
2349 /* Release the DIB section */
2350 DeleteObject(This->dib.DIBsection);
2351 This->dib.bitmap_data = NULL;
2352 This->resource.allocatedMemory = NULL;
2354 This->Flags &= ~SFLAG_DIBSECTION;
2355 } else if(!(This->Flags & SFLAG_USERPTR)) {
2356 release = This->resource.heapMemory;
2357 This->resource.heapMemory = NULL;
2359 This->resource.allocatedMemory = Mem;
2360 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2362 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2363 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2365 /* For client textures opengl has to be notified */
2366 if(This->Flags & SFLAG_CLIENT) {
2367 This->Flags &= ~SFLAG_ALLOCATED;
2368 IWineD3DSurface_PreLoad(iface);
2369 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2372 /* Now free the old memory if any */
2373 HeapFree(GetProcessHeap(), 0, release);
2374 } else if(This->Flags & SFLAG_USERPTR) {
2375 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2376 This->resource.allocatedMemory = NULL;
2377 /* HeapMemory should be NULL already */
2378 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2379 This->Flags &= ~SFLAG_USERPTR;
2381 if(This->Flags & SFLAG_CLIENT) {
2382 This->Flags &= ~SFLAG_ALLOCATED;
2383 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2384 IWineD3DSurface_PreLoad(iface);
2390 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2391 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2392 IWineD3DSwapChainImpl *swapchain = NULL;
2394 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2396 /* Flipping is only supported on RenderTargets */
2397 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2400 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2401 * FIXME("(%p) Target override is not supported by now\n", This);
2402 * Additionally, it isn't really possible to support triple-buffering
2403 * properly on opengl at all
2407 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2409 ERR("Flipped surface is not on a swapchain\n");
2410 return WINEDDERR_NOTFLIPPABLE;
2413 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2414 * and only d3d8 and d3d9 apps specify the presentation interval
2416 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2417 /* Most common case first to avoid wasting time on all the other cases */
2418 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2419 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2420 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2421 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2422 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2423 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2424 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2426 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2429 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2430 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2431 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2435 /* Does a direct frame buffer -> texture copy. Stretching is done
2436 * with single pixel copy calls
2438 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2439 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2442 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2445 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2447 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2449 /* Bind the target texture */
2450 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
2451 checkGLcall("glBindTexture");
2453 glReadBuffer(myDevice->offscreenBuffer);
2455 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2456 glReadBuffer(buffer);
2458 checkGLcall("glReadBuffer");
2460 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2461 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2463 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2464 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2466 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2467 ERR("Texture filtering not supported in direct blit\n");
2469 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2470 ERR("Texture filtering not supported in direct blit\n");
2474 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2475 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2476 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2478 glCopyTexSubImage2D(This->glDescription.target,
2479 This->glDescription.level,
2480 drect->x1, drect->y1, /* xoffset, yoffset */
2481 srect->x1, Src->currentDesc.Height - srect->y2,
2482 drect->x2 - drect->x1, drect->y2 - drect->y1);
2484 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2485 /* I have to process this row by row to swap the image,
2486 * otherwise it would be upside down, so stretching in y direction
2487 * doesn't cost extra time
2489 * However, stretching in x direction can be avoided if not necessary
2491 for(row = drect->y1; row < drect->y2; row++) {
2492 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2493 /* Well, that stuff works, but it's very slow.
2494 * find a better way instead
2498 for(col = drect->x1; col < drect->x2; col++) {
2499 glCopyTexSubImage2D(This->glDescription.target,
2500 This->glDescription.level,
2501 drect->x1 + col, row, /* xoffset, yoffset */
2502 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2506 glCopyTexSubImage2D(This->glDescription.target,
2507 This->glDescription.level,
2508 drect->x1, row, /* xoffset, yoffset */
2509 srect->x1, yoffset - (int) (row * yrel),
2510 drect->x2-drect->x1, 1);
2515 vcheckGLcall("glCopyTexSubImage2D");
2519 /* Uses the hardware to stretch and flip the image */
2520 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2521 GLuint src, backup = 0;
2522 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2523 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2524 float left, right, top, bottom; /* Texture coordinates */
2525 UINT fbwidth = Src->currentDesc.Width;
2526 UINT fbheight = Src->currentDesc.Height;
2527 GLenum drawBuffer = GL_BACK;
2529 TRACE("Using hwstretch blit\n");
2530 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2531 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2533 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2535 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2536 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2538 if(GL_LIMITS(aux_buffers) >= 2) {
2539 /* Got more than one aux buffer? Use the 2nd aux buffer */
2540 drawBuffer = GL_AUX1;
2541 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2542 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2543 drawBuffer = GL_AUX0;
2546 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2547 glGenTextures(1, &backup);
2548 checkGLcall("glGenTextures\n");
2549 glBindTexture(GL_TEXTURE_2D, backup);
2550 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2552 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2553 * we are reading from the back buffer, the backup can be used as source texture
2555 if(Src->glDescription.textureName == 0) {
2556 /* Get it a description */
2557 IWineD3DSurface_PreLoad(SrcSurface);
2559 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
2560 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2562 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2563 Src->Flags &= ~SFLAG_INTEXTURE;
2566 glReadBuffer(GL_BACK);
2567 checkGLcall("glReadBuffer(GL_BACK)");
2569 /* TODO: Only back up the part that will be overwritten */
2570 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2571 0, 0 /* read offsets */,
2576 checkGLcall("glCopyTexSubImage2D");
2578 /* No issue with overriding these - the sampler is dirty due to blit usage */
2579 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
2580 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2581 checkGLcall("glTexParameteri");
2582 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
2583 minMipLookup[Filter][WINED3DTEXF_NONE]);
2584 checkGLcall("glTexParameteri");
2586 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2587 src = backup ? backup : Src->glDescription.textureName;
2589 glReadBuffer(GL_FRONT);
2590 checkGLcall("glReadBuffer(GL_FRONT)");
2592 glGenTextures(1, &src);
2593 checkGLcall("glGenTextures(1, &src)");
2594 glBindTexture(GL_TEXTURE_2D, src);
2595 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2597 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2598 * out for power of 2 sizes
2600 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2601 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2602 checkGLcall("glTexImage2D");
2603 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2604 0, 0 /* read offsets */,
2609 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2610 checkGLcall("glTexParameteri");
2611 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2612 checkGLcall("glTexParameteri");
2614 glReadBuffer(GL_BACK);
2615 checkGLcall("glReadBuffer(GL_BACK)");
2617 checkGLcall("glEnd and previous");
2619 left = (float) srect->x1 / (float) Src->pow2Width;
2620 right = (float) srect->x2 / (float) Src->pow2Width;
2623 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2624 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2626 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2627 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2630 /* draw the source texture stretched and upside down. The correct surface is bound already */
2631 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
2632 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
2634 glDrawBuffer(drawBuffer);
2635 glReadBuffer(drawBuffer);
2639 glTexCoord2f(left, bottom);
2640 glVertex2i(0, fbheight);
2643 glTexCoord2f(left, top);
2644 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2647 glTexCoord2f(right, top);
2648 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2651 glTexCoord2f(right, bottom);
2652 glVertex2i(drect->x2 - drect->x1, fbheight);
2654 checkGLcall("glEnd and previous");
2656 /* Now read the stretched and upside down image into the destination texture */
2657 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2658 checkGLcall("glBindTexture");
2659 glCopyTexSubImage2D(This->glDescription.target,
2661 drect->x1, drect->y1, /* xoffset, yoffset */
2662 0, 0, /* We blitted the image to the origin */
2663 drect->x2 - drect->x1, drect->y2 - drect->y1);
2664 checkGLcall("glCopyTexSubImage2D");
2666 /* Write the back buffer backup back */
2667 glBindTexture(GL_TEXTURE_2D, backup ? backup : Src->glDescription.textureName);
2668 checkGLcall("glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName)");
2670 if(drawBuffer == GL_BACK) {
2673 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2677 glTexCoord2f(0.0, 0.0);
2678 glVertex2i(0, fbheight);
2681 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2682 glVertex2i(fbwidth, Src->currentDesc.Height);
2685 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2686 glVertex2i(fbwidth, 0);
2689 /* Restore the old draw buffer */
2690 glDrawBuffer(GL_BACK);
2694 if(src != Src->glDescription.textureName && src != backup) {
2695 glDeleteTextures(1, &src);
2696 checkGLcall("glDeleteTextures(1, &src)");
2699 glDeleteTextures(1, &backup);
2700 checkGLcall("glDeleteTextures(1, &backup)");
2705 /* Not called from the VTable */
2706 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2708 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2709 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2710 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2712 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2714 /* Get the swapchain. One of the surfaces has to be a primary surface */
2715 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2716 WARN("Destination is in sysmem, rejecting gl blt\n");
2717 return WINED3DERR_INVALIDCALL;
2719 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2720 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2722 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2723 WARN("Src is in sysmem, rejecting gl blt\n");
2724 return WINED3DERR_INVALIDCALL;
2726 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2727 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2730 /* Early sort out of cases where no render target is used */
2731 if(!dstSwapchain && !srcSwapchain &&
2732 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2733 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2734 return WINED3DERR_INVALIDCALL;
2737 /* No destination color keying supported */
2738 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
2739 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2740 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2741 return WINED3DERR_INVALIDCALL;
2745 rect.x1 = DestRect->left;
2746 rect.y1 = DestRect->top;
2747 rect.x2 = DestRect->right;
2748 rect.y2 = DestRect->bottom;
2752 rect.x2 = This->currentDesc.Width;
2753 rect.y2 = This->currentDesc.Height;
2756 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2757 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
2758 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
2759 /* Half-life does a Blt from the back buffer to the front buffer,
2760 * Full surface size, no flags... Use present instead
2762 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
2765 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2769 TRACE("Looking if a Present can be done...\n");
2770 /* Source Rectangle must be full surface */
2772 if(SrcRect->left != 0 || SrcRect->top != 0 ||
2773 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
2774 TRACE("No, Source rectangle doesn't match\n");
2780 mySrcRect.right = Src->currentDesc.Width;
2781 mySrcRect.bottom = Src->currentDesc.Height;
2783 /* No stretching may occur */
2784 if(mySrcRect.right != rect.x2 - rect.x1 ||
2785 mySrcRect.bottom != rect.y2 - rect.y1) {
2786 TRACE("No, stretching is done\n");
2790 /* Destination must be full surface or match the clipping rectangle */
2791 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
2795 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
2800 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
2803 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
2804 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
2806 TRACE("No, dest rectangle doesn't match(clipper)\n");
2807 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
2808 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
2814 if(rect.x1 != 0 || rect.y1 != 0 ||
2815 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
2816 TRACE("No, dest rectangle doesn't match(surface size)\n");
2823 /* These flags are unimportant for the flag check, remove them */
2824 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
2825 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
2827 /* The idea behind this is that a glReadPixels and a glDrawPixels call
2828 * take very long, while a flip is fast.
2829 * This applies to Half-Life, which does such Blts every time it finished
2830 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
2831 * menu. This is also used by all apps when they do windowed rendering
2833 * The problem is that flipping is not really the same as copying. After a
2834 * Blt the front buffer is a copy of the back buffer, and the back buffer is
2835 * untouched. Therefore it's necessary to override the swap effect
2836 * and to set it back after the flip.
2838 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
2842 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
2843 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2845 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
2846 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
2848 dstSwapchain->presentParms.SwapEffect = orig_swap;
2855 TRACE("Unsupported blit between buffers on the same swapchain\n");
2856 return WINED3DERR_INVALIDCALL;
2857 } else if((dstSwapchain || This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) &&
2858 (srcSwapchain || SrcSurface == myDevice->render_targets[0]) ) {
2859 ERR("Can't perform hardware blit between 2 different swapchains, falling back to software\n");
2860 return WINED3DERR_INVALIDCALL;
2863 if(srcSwapchain || SrcSurface == myDevice->render_targets[0]) {
2864 /* Blit from render target to texture */
2866 BOOL upsideDown, stretchx;
2868 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
2869 TRACE("Color keying not supported by frame buffer to texture blit\n");
2870 return WINED3DERR_INVALIDCALL;
2871 /* Destination color key is checked above */
2874 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2875 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2878 if(SrcRect->top < SrcRect->bottom) {
2879 srect.y1 = SrcRect->top;
2880 srect.y2 = SrcRect->bottom;
2883 srect.y1 = SrcRect->bottom;
2884 srect.y2 = SrcRect->top;
2887 srect.x1 = SrcRect->left;
2888 srect.x2 = SrcRect->right;
2892 srect.x2 = Src->currentDesc.Width;
2893 srect.y2 = Src->currentDesc.Height;
2896 if(rect.x1 > rect.x2) {
2900 upsideDown = !upsideDown;
2903 TRACE("Reading from an offscreen target\n");
2904 upsideDown = !upsideDown;
2907 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
2913 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2914 * flip the image nor scale it.
2916 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2917 * -> If the app wants a image width an unscaled width, copy it line per line
2918 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
2919 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2920 * back buffer. This is slower than reading line per line, thus not used for flipping
2921 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2924 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
2925 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
2928 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
2929 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
2930 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
2931 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
2932 rect.y2 - rect.y1 > Src->currentDesc.Height) {
2933 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
2934 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
2936 TRACE("Using hardware stretching to flip / stretch the texture\n");
2937 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
2940 if(!(This->Flags & SFLAG_DONOTFREE)) {
2941 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2942 This->resource.allocatedMemory = NULL;
2943 This->resource.heapMemory = NULL;
2945 This->Flags &= ~SFLAG_INSYSMEM;
2947 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
2948 * path is never entered
2950 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
2954 /* Blit from offscreen surface to render target */
2955 float glTexCoord[4];
2956 DWORD oldCKeyFlags = Src->CKeyFlags;
2957 WINEDDCOLORKEY oldBltCKey = This->SrcBltCKey;
2958 RECT SourceRectangle;
2960 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
2963 SourceRectangle.left = SrcRect->left;
2964 SourceRectangle.right = SrcRect->right;
2965 SourceRectangle.top = SrcRect->top;
2966 SourceRectangle.bottom = SrcRect->bottom;
2968 SourceRectangle.left = 0;
2969 SourceRectangle.right = Src->currentDesc.Width;
2970 SourceRectangle.top = 0;
2971 SourceRectangle.bottom = Src->currentDesc.Height;
2974 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
2975 /* Fall back to software */
2976 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
2977 SourceRectangle.left, SourceRectangle.top,
2978 SourceRectangle.right, SourceRectangle.bottom);
2979 return WINED3DERR_INVALIDCALL;
2982 /* Color keying: Check if we have to do a color keyed blt,
2983 * and if not check if a color key is activated.
2985 * Just modify the color keying parameters in the surface and restore them afterwards
2986 * The surface keeps track of the color key last used to load the opengl surface.
2987 * PreLoad will catch the change to the flags and color key and reload if necessary.
2989 if(Flags & WINEDDBLT_KEYSRC) {
2990 /* Use color key from surface */
2991 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
2992 /* Use color key from DDBltFx */
2993 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
2994 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
2996 /* Do not use color key */
2997 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3000 /* Now load the surface */
3001 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3004 /* Activate the destination context, set it up for blitting */
3005 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3009 TRACE("Drawing to offscreen buffer\n");
3010 glDrawBuffer(myDevice->offscreenBuffer);
3011 checkGLcall("glDrawBuffer");
3013 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
3014 TRACE("Drawing to %#x buffer\n", buffer);
3015 glDrawBuffer(buffer);
3016 checkGLcall("glDrawBuffer");
3019 /* Bind the texture */
3020 glBindTexture(GL_TEXTURE_2D, Src->glDescription.textureName);
3021 checkGLcall("glBindTexture");
3023 /* Filtering for StretchRect */
3024 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
3025 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
3026 checkGLcall("glTexParameteri");
3027 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
3028 minMipLookup[Filter][WINED3DTEXF_NONE]);
3029 checkGLcall("glTexParameteri");
3030 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
3031 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
3032 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3033 checkGLcall("glTexEnvi");
3035 /* This is for color keying */
3036 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3037 glEnable(GL_ALPHA_TEST);
3038 checkGLcall("glEnable GL_ALPHA_TEST");
3039 glAlphaFunc(GL_NOTEQUAL, 0.0);
3040 checkGLcall("glAlphaFunc\n");
3042 glDisable(GL_ALPHA_TEST);
3043 checkGLcall("glDisable GL_ALPHA_TEST");
3046 /* Draw a textured quad
3050 glColor3d(1.0f, 1.0f, 1.0f);
3051 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3056 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3057 glVertex3f(rect.x1, rect.y2, 0.0);
3059 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3064 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3069 checkGLcall("glEnd");
3071 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3072 glDisable(GL_ALPHA_TEST);
3073 checkGLcall("glDisable(GL_ALPHA_TEST)");
3076 /* Unbind the texture */
3077 glBindTexture(GL_TEXTURE_2D, 0);
3078 checkGLcall("glEnable glBindTexture");
3080 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3081 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3083 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3084 glDrawBuffer(GL_BACK);
3085 checkGLcall("glDrawBuffer");
3087 /* Restore the color key parameters */
3088 Src->CKeyFlags = oldCKeyFlags;
3089 This->SrcBltCKey = oldBltCKey;
3093 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3094 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3097 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3098 /* TODO: This should be moved to ModifyLocation() */
3099 if(!(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO)) {
3100 This->Flags |= SFLAG_INTEXTURE;
3105 /* Source-Less Blit to render target */
3106 if (Flags & WINEDDBLT_COLORFILL) {
3107 /* This is easy to handle for the D3D Device... */
3110 TRACE("Colorfill\n");
3112 /* The color as given in the Blt function is in the format of the frame-buffer...
3113 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3115 if (This->resource.format == WINED3DFMT_P8) {
3116 if (This->palette) {
3117 color = ((0xFF000000) |
3118 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3119 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3120 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3125 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3126 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3129 color = ((0xFF000000) |
3130 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3131 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3132 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3135 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3136 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3137 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3139 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3140 color = DDBltFx->u5.dwFillColor;
3143 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3144 return WINED3DERR_INVALIDCALL;
3147 TRACE("Calling GetSwapChain with mydevice = %p\n", myDevice);
3148 if(dstSwapchain && dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]) {
3149 glDrawBuffer(GL_BACK);
3150 checkGLcall("glDrawBuffer(GL_BACK)");
3151 } else if (dstSwapchain && This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer) {
3152 glDrawBuffer(GL_FRONT);
3153 checkGLcall("glDrawBuffer(GL_FRONT)");
3154 } else if(This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3155 glDrawBuffer(myDevice->offscreenBuffer);
3156 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer3)");
3158 TRACE("Surface is higher back buffer, falling back to software\n");
3159 return WINED3DERR_INVALIDCALL;
3162 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3164 IWineD3DDevice_Clear( (IWineD3DDevice *) myDevice,
3165 1 /* Number of rectangles */,
3167 WINED3DCLEAR_TARGET,
3172 /* Restore the original draw buffer */
3174 glDrawBuffer(myDevice->offscreenBuffer);
3175 } else if(dstSwapchain->backBuffer && dstSwapchain->backBuffer[0]) {
3176 glDrawBuffer(GL_BACK);
3178 vcheckGLcall("glDrawBuffer");
3184 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3185 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3186 return WINED3DERR_INVALIDCALL;
3189 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3191 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3194 if (Flags & WINEDDBLT_DEPTHFILL) {
3195 switch(This->resource.format) {
3196 case WINED3DFMT_D16:
3197 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3199 case WINED3DFMT_D15S1:
3200 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3202 case WINED3DFMT_D24S8:
3203 case WINED3DFMT_D24X8:
3204 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3206 case WINED3DFMT_D32:
3207 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3211 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3214 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3215 DestRect == NULL ? 0 : 1,
3216 (WINED3DRECT *) DestRect,
3217 WINED3DCLEAR_ZBUFFER,
3223 FIXME("(%p): Unsupp depthstencil blit\n", This);
3224 return WINED3DERR_INVALIDCALL;
3227 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3228 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3229 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3230 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3231 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3232 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3234 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3235 * except depth blits, which seem to work
3237 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3238 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3239 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3240 return WINED3DERR_INVALIDCALL;
3241 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3242 TRACE("Z Blit override handled the blit\n");
3247 /* Special cases for RenderTargets */
3248 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3249 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3250 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3253 /* For the rest call the X11 surface implementation.
3254 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3255 * other Blts are rather rare
3257 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3260 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3261 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3262 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3263 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3264 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3266 if(myDevice->inScene &&
3267 (iface == myDevice->stencilBufferTarget ||
3268 (Source && Source == myDevice->stencilBufferTarget))) {
3269 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3270 return WINED3DERR_INVALIDCALL;
3273 /* Special cases for RenderTargets */
3274 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3275 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3277 RECT SrcRect, DstRect;
3281 SrcRect.left = rsrc->left;
3282 SrcRect.top= rsrc->top;
3283 SrcRect.bottom = rsrc->bottom;
3284 SrcRect.right = rsrc->right;
3288 SrcRect.right = srcImpl->currentDesc.Width;
3289 SrcRect.bottom = srcImpl->currentDesc.Height;
3292 DstRect.left = dstx;
3294 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3295 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3297 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3298 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3299 Flags |= WINEDDBLT_KEYSRC;
3300 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3301 Flags |= WINEDDBLT_KEYDEST;
3302 if(trans & WINEDDBLTFAST_WAIT)
3303 Flags |= WINEDDBLT_WAIT;
3304 if(trans & WINEDDBLTFAST_DONOTWAIT)
3305 Flags |= WINEDDBLT_DONOTWAIT;
3307 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3311 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3314 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3315 /** Check against the maximum texture sizes supported by the video card **/
3316 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3317 unsigned int pow2Width, pow2Height;
3318 const GlPixelFormatDesc *glDesc;
3320 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3321 /* Setup some glformat defaults */
3322 This->glDescription.glFormat = glDesc->glFormat;
3323 This->glDescription.glFormatInternal = glDesc->glInternal;
3324 This->glDescription.glType = glDesc->glType;
3326 This->glDescription.textureName = 0;
3327 This->glDescription.target = GL_TEXTURE_2D;
3329 /* Non-power2 support */
3330 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO)) {
3331 pow2Width = This->currentDesc.Width;
3332 pow2Height = This->currentDesc.Height;
3334 /* Find the nearest pow2 match */
3335 pow2Width = pow2Height = 1;
3336 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3337 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3339 This->pow2Width = pow2Width;
3340 This->pow2Height = pow2Height;
3342 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3343 WINED3DFORMAT Format = This->resource.format;
3344 /** TODO: add support for non power two compressed textures **/
3345 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3346 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5) {
3347 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3348 This, This->currentDesc.Width, This->currentDesc.Height);
3349 return WINED3DERR_NOTAVAILABLE;
3353 if(pow2Width != This->currentDesc.Width ||
3354 pow2Height != This->currentDesc.Height) {
3355 This->Flags |= SFLAG_NONPOW2;
3358 TRACE("%p\n", This);
3359 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3360 /* one of three options
3361 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)
3362 2: Set the texture to the maximum size (bad idea)
3363 3: WARN and return WINED3DERR_NOTAVAILABLE;
3364 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.
3366 WARN("(%p) Creating an oversized surface\n", This);
3367 This->Flags |= SFLAG_OVERSIZE;
3369 /* This will be initialized on the first blt */
3370 This->glRect.left = 0;
3371 This->glRect.top = 0;
3372 This->glRect.right = 0;
3373 This->glRect.bottom = 0;
3375 /* No oversize, gl rect is the full texture size */
3376 This->Flags &= ~SFLAG_OVERSIZE;
3377 This->glRect.left = 0;
3378 This->glRect.top = 0;
3379 This->glRect.right = This->pow2Width;
3380 This->glRect.bottom = This->pow2Height;
3383 This->Flags |= SFLAG_INSYSMEM;
3388 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
3389 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3391 TRACE("(%p)->(%s, %s)\n", iface,
3392 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3393 persistent ? "TRUE" : "FALSE");
3395 /* TODO: For offscreen textures with fbo offscreen rendering the drawable is the same as the texture.*/
3397 This->Flags &= ~SFLAG_LOCATIONS;
3398 This->Flags |= flag;
3400 This->Flags &= ~flag;
3408 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
3409 struct coords coords[4];
3411 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3418 rect.right = This->currentDesc.Width;
3419 rect.bottom = This->currentDesc.Height;
3422 ActivateContext(device, device->render_targets[0], CTXUSAGE_BLIT);
3425 if(This->glDescription.target == GL_TEXTURE_2D) {
3426 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
3427 checkGLcall("GL_TEXTURE_2D, This->glDescription.textureName)");
3428 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3429 checkGLcall("glTexParameteri");
3430 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3431 checkGLcall("glTexParameteri");
3433 coords[0].x = rect.left / This->pow2Width;
3436 coords[1].x = rect.left / This->pow2Width;
3439 coords[2].x = rect.right / This->pow2Width;
3442 coords[3].x = rect.right / This->pow2Width;
3445 coords[0].y = rect.top / This->pow2Height;
3446 coords[1].y = rect.bottom / This->pow2Height;
3447 coords[2].y = rect.bottom / This->pow2Height;
3448 coords[3].y = rect.top / This->pow2Height;
3450 /* Must be a cube map */
3451 glDisable(GL_TEXTURE_2D);
3452 checkGLcall("glDisable(GL_TEXTURE_2D)");
3453 glEnable(GL_TEXTURE_CUBE_MAP_ARB);
3454 checkGLcall("glEnable(GL_TEXTURE_CUBE_MAP_ARB)");
3455 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName);
3456 checkGLcall("GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName)");
3457 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3458 checkGLcall("glTexParameteri");
3459 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3460 checkGLcall("glTexParameteri");
3462 switch(This->glDescription.target) {
3463 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
3464 coords[0].x = 1; coords[0].y = -1; coords[0].z = 1;
3465 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3466 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3467 coords[3].x = 1; coords[3].y = -1; coords[3].z = -1;
3470 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
3471 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3472 coords[1].x = -1; coords[1].y = 1; coords[1].z = 1;
3473 coords[2].x = -1; coords[2].y = 1; coords[2].z = -1;
3474 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3477 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
3478 coords[0].x = -1; coords[0].y = 1; coords[0].z = 1;
3479 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3480 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3481 coords[3].x = -1; coords[3].y = 1; coords[3].z = -1;
3484 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
3485 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3486 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3487 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3488 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3491 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
3492 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3493 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3494 coords[2].x = 1; coords[2].y = -1; coords[2].z = 1;
3495 coords[3].x = -1; coords[3].y = -1; coords[3].z = 1;
3498 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
3499 coords[0].x = -1; coords[0].y = -1; coords[0].z = -1;
3500 coords[1].x = 1; coords[1].y = -1; coords[1].z = -1;
3501 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3502 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3505 ERR("Unexpected texture target\n");
3512 glTexCoord3iv((GLint *) &coords[0]);
3513 glVertex2i(rect.left, device->render_offscreen ? rect.bottom : rect.top);
3515 glTexCoord3iv((GLint *) &coords[1]);
3516 glVertex2i(0, device->render_offscreen ? rect.top : rect.bottom);
3518 glTexCoord3iv((GLint *) &coords[2]);
3519 glVertex2i(rect.right, device->render_offscreen ? rect.top : rect.bottom);
3521 glTexCoord3iv((GLint *) &coords[3]);
3522 glVertex2i(rect.right, device->render_offscreen ? rect.bottom : rect.top);
3524 checkGLcall("glEnd");
3526 if(This->glDescription.target != GL_TEXTURE_2D) {
3527 glEnable(GL_TEXTURE_2D);
3528 checkGLcall("glEnable(GL_TEXTURE_2D)");
3529 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3530 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3535 /*****************************************************************************
3536 * IWineD3DSurface::LoadLocation
3538 * Copies the current surface data from wherever it is to the requested
3539 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
3540 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
3541 * multiple locations, the gl texture is prefered over the drawable, which is
3542 * prefered over system memory. The PBO counts as system memory. If rect is
3543 * not NULL, only the specified rectangle is copied(only supported for
3544 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
3545 * location is marked up to date after the copy.
3548 * flag: Surface location flag to be updated
3549 * rect: rectangle to be copied
3552 * WINED3D_OK on success
3553 * WINED3DERR_DEVICELOST on an internal error
3555 *****************************************************************************/
3556 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
3557 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3558 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3559 IWineD3DDeviceImpl *myDevice;
3560 GLenum format, internal, type;
3561 CONVERT_TYPES convert;
3563 int width, pitch, outpitch;
3566 TRACE("(%p)->(%s, %p)\n", iface,
3567 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3570 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
3573 /* TODO: For fbo targets, texture == drawable */
3574 if(This->Flags & flag) {
3575 TRACE("Location already up to date\n");
3579 if(!(This->Flags & SFLAG_LOCATIONS)) {
3580 ERR("Surface does not have any up to date location\n");
3581 This->Flags |= SFLAG_LOST;
3582 return WINED3DERR_DEVICELOST;
3584 myDevice = This->resource.wineD3DDevice;
3586 if(flag == SFLAG_INSYSMEM) {
3587 surface_prepare_system_memory(This);
3589 /* Download the surface to system memory */
3590 if(This->Flags & SFLAG_INTEXTURE) {
3591 if (0 == This->glDescription.textureName) {
3592 ERR("Surface does not have a texture, but SFLAG_INTEXTURE is set\n");
3595 if(myDevice->createParms.BehaviorFlags & WINED3DCREATE_MULTITHREADED) {
3596 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
3600 /* Make sure that a proper texture unit is selected, bind the texture
3601 * and dirtify the sampler to restore the texture on the next draw
3603 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
3604 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
3605 checkGLcall("glActiveTextureARB");
3607 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(0));
3608 IWineD3DSurface_PreLoad(iface);
3610 surface_download_data(This);
3613 read_from_framebuffer(This, rect,
3614 This->resource.allocatedMemory,
3615 IWineD3DSurface_GetPitch(iface));
3617 } else if(flag == SFLAG_INDRAWABLE) {
3618 if(This->Flags & SFLAG_INTEXTURE) {
3619 surface_blt_to_drawable(This, rect);
3621 flush_to_framebuffer_drawpixels(This);
3623 } else /* if(flag == SFLAG_INTEXTURE) */ {
3624 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
3626 if (This->Flags & SFLAG_INDRAWABLE) {
3630 glGetIntegerv(GL_READ_BUFFER, &prevRead);
3631 vcheckGLcall("glGetIntegerv");
3632 glReadBuffer(This->resource.wineD3DDevice->offscreenBuffer);
3633 vcheckGLcall("glReadBuffer");
3635 if(!(This->Flags & SFLAG_ALLOCATED)) {
3636 surface_allocate_surface(This, internal, This->pow2Width,
3637 This->pow2Height, format, type);
3640 clear_unused_channels(This);
3642 glCopyTexSubImage2D(This->glDescription.target,
3643 This->glDescription.level,
3645 This->currentDesc.Width,
3646 This->currentDesc.Height);
3647 checkGLcall("glCopyTexSubImage2D");
3649 glReadBuffer(prevRead);
3650 vcheckGLcall("glReadBuffer");
3654 TRACE("Updated target %d\n", This->glDescription.target);
3656 /* The only place where LoadTexture() might get called when isInDraw=1
3657 * is ActivateContext where lastActiveRenderTarget is preloaded.
3659 if(iface == device->lastActiveRenderTarget && device->isInDraw)
3660 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
3662 /* Otherwise: System memory copy must be most up to date */
3664 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
3665 This->Flags |= SFLAG_GLCKEY;
3666 This->glCKey = This->SrcBltCKey;
3668 else This->Flags &= ~SFLAG_GLCKEY;
3670 /* The width is in 'length' not in bytes */
3671 width = This->currentDesc.Width;
3672 pitch = IWineD3DSurface_GetPitch(iface);
3674 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
3675 int height = This->currentDesc.Height;
3677 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
3678 outpitch = width * bpp;
3679 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
3681 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
3683 ERR("Out of memory %d, %d!\n", outpitch, height);
3684 return WINED3DERR_OUTOFVIDEOMEMORY;
3686 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
3688 This->Flags |= SFLAG_CONVERTED;
3689 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
3690 d3dfmt_p8_upload_palette(iface, convert);
3691 This->Flags &= ~SFLAG_CONVERTED;
3692 mem = This->resource.allocatedMemory;
3694 This->Flags &= ~SFLAG_CONVERTED;
3695 mem = This->resource.allocatedMemory;
3698 /* Make sure the correct pitch is used */
3699 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
3701 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
3702 TRACE("non power of two support\n");
3703 if(!(This->Flags & SFLAG_ALLOCATED)) {
3704 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
3706 if (mem || (This->Flags & SFLAG_PBO)) {
3707 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
3710 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
3711 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
3713 if(!(This->Flags & SFLAG_ALLOCATED)) {
3714 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
3716 if (mem || (This->Flags & SFLAG_PBO)) {
3717 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
3721 /* Restore the default pitch */
3722 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
3724 /* Don't delete PBO memory */
3725 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
3726 HeapFree(GetProcessHeap(), 0, mem);
3731 This->Flags |= flag;
3737 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
3740 IWineD3DBaseSurfaceImpl_QueryInterface,
3741 IWineD3DBaseSurfaceImpl_AddRef,
3742 IWineD3DSurfaceImpl_Release,
3743 /* IWineD3DResource */
3744 IWineD3DBaseSurfaceImpl_GetParent,
3745 IWineD3DBaseSurfaceImpl_GetDevice,
3746 IWineD3DBaseSurfaceImpl_SetPrivateData,
3747 IWineD3DBaseSurfaceImpl_GetPrivateData,
3748 IWineD3DBaseSurfaceImpl_FreePrivateData,
3749 IWineD3DBaseSurfaceImpl_SetPriority,
3750 IWineD3DBaseSurfaceImpl_GetPriority,
3751 IWineD3DSurfaceImpl_PreLoad,
3752 IWineD3DBaseSurfaceImpl_GetType,
3753 /* IWineD3DSurface */
3754 IWineD3DBaseSurfaceImpl_GetContainer,
3755 IWineD3DBaseSurfaceImpl_GetDesc,
3756 IWineD3DSurfaceImpl_LockRect,
3757 IWineD3DSurfaceImpl_UnlockRect,
3758 IWineD3DSurfaceImpl_GetDC,
3759 IWineD3DSurfaceImpl_ReleaseDC,
3760 IWineD3DSurfaceImpl_Flip,
3761 IWineD3DSurfaceImpl_Blt,
3762 IWineD3DBaseSurfaceImpl_GetBltStatus,
3763 IWineD3DBaseSurfaceImpl_GetFlipStatus,
3764 IWineD3DBaseSurfaceImpl_IsLost,
3765 IWineD3DBaseSurfaceImpl_Restore,
3766 IWineD3DSurfaceImpl_BltFast,
3767 IWineD3DBaseSurfaceImpl_GetPalette,
3768 IWineD3DBaseSurfaceImpl_SetPalette,
3769 IWineD3DBaseSurfaceImpl_RealizePalette,
3770 IWineD3DBaseSurfaceImpl_SetColorKey,
3771 IWineD3DBaseSurfaceImpl_GetPitch,
3772 IWineD3DSurfaceImpl_SetMem,
3773 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
3774 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
3775 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
3776 IWineD3DBaseSurfaceImpl_UpdateOverlay,
3777 IWineD3DBaseSurfaceImpl_SetClipper,
3778 IWineD3DBaseSurfaceImpl_GetClipper,
3780 IWineD3DSurfaceImpl_AddDirtyRect,
3781 IWineD3DSurfaceImpl_LoadTexture,
3782 IWineD3DSurfaceImpl_SaveSnapshot,
3783 IWineD3DBaseSurfaceImpl_SetContainer,
3784 IWineD3DSurfaceImpl_SetGlTextureDesc,
3785 IWineD3DSurfaceImpl_GetGlDesc,
3786 IWineD3DSurfaceImpl_GetData,
3787 IWineD3DSurfaceImpl_SetFormat,
3788 IWineD3DSurfaceImpl_PrivateSetup,
3789 IWineD3DSurfaceImpl_ModifyLocation,
3790 IWineD3DSurfaceImpl_LoadLocation