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_bind_and_dirtify(IWineD3DSurfaceImpl *This) {
40 /* Make sure that a proper texture unit is selected, bind the texture
41 * and dirtify the sampler to restore the texture on the next draw. */
42 if (GL_SUPPORT(ARB_MULTITEXTURE)) {
43 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
44 checkGLcall("glActiveTextureARB");
46 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(0));
47 IWineD3DSurface_BindTexture((IWineD3DSurface *)This);
50 /* This call just downloads data, the caller is responsible for activating the
51 * right context and binding the correct texture. */
52 static void surface_download_data(IWineD3DSurfaceImpl *This) {
53 if (0 == This->glDescription.textureName) {
54 ERR("Surface does not have a texture, but SFLAG_INTEXTURE is set\n");
58 if(This->Flags & SFLAG_CONVERTED) {
59 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(This->resource.format));
65 if (This->resource.format == WINED3DFMT_DXT1 ||
66 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
67 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
68 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) { /* We can assume this as the texture would not have been created otherwise */
69 FIXME("(%p) : Attempting to lock a compressed texture when texture compression isn't supported by opengl\n", This);
71 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
72 This->glDescription.glFormat, This->glDescription.glType, This->resource.allocatedMemory);
74 if(This->Flags & SFLAG_PBO) {
75 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
76 checkGLcall("glBindBufferARB");
77 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, NULL));
78 checkGLcall("glGetCompressedTexImageARB()");
79 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
80 checkGLcall("glBindBufferARB");
82 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, This->resource.allocatedMemory));
83 checkGLcall("glGetCompressedTexImageARB()");
92 if (This->Flags & SFLAG_NONPOW2) {
93 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
94 src_pitch = This->bytesPerPixel * This->pow2Width;
95 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
96 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
97 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
99 mem = This->resource.allocatedMemory;
102 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
103 This->glDescription.glFormat, This->glDescription.glType, mem);
105 if(This->Flags & SFLAG_PBO) {
106 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
107 checkGLcall("glBindBufferARB");
109 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
110 This->glDescription.glType, NULL);
111 checkGLcall("glGetTexImage()");
113 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
114 checkGLcall("glBindBufferARB");
116 glGetTexImage(This->glDescription.target, This->glDescription.level, This->glDescription.glFormat,
117 This->glDescription.glType, mem);
118 checkGLcall("glGetTexImage()");
122 if (This->Flags & SFLAG_NONPOW2) {
123 LPBYTE src_data, dst_data;
126 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
127 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
128 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
130 * We're doing this...
132 * instead of boxing the texture :
133 * |<-texture width ->| -->pow2width| /\
134 * |111111111111111111| | |
135 * |222 Texture 222222| boxed empty | texture height
136 * |3333 Data 33333333| | |
137 * |444444444444444444| | \/
138 * ----------------------------------- |
139 * | boxed empty | boxed empty | pow2height
141 * -----------------------------------
144 * we're repacking the data to the expected texture width
146 * |<-texture width ->| -->pow2width| /\
147 * |111111111111111111222222222222222| |
148 * |222333333333333333333444444444444| texture height
152 * | empty | pow2height
154 * -----------------------------------
158 * |<-texture width ->| /\
159 * |111111111111111111|
160 * |222222222222222222|texture height
161 * |333333333333333333|
162 * |444444444444444444| \/
163 * --------------------
165 * this also means that any references to allocatedMemory should work with the data as if were a
166 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
168 * internally the texture is still stored in a boxed format so any references to textureName will
169 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
171 * Performance should not be an issue, because applications normally do not lock the surfaces when
172 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
173 * and doesn't have to be re-read.
176 dst_data = This->resource.allocatedMemory;
177 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
178 for (y = 1 ; y < This->currentDesc.Height; y++) {
179 /* skip the first row */
180 src_data += src_pitch;
181 dst_data += dst_pitch;
182 memcpy(dst_data, src_data, dst_pitch);
185 HeapFree(GetProcessHeap(), 0, mem);
189 /* Surface has now been downloaded */
190 This->Flags |= SFLAG_INSYSMEM;
193 /* This call just uploads data, the caller is responsible for activating the
194 * right context and binding the correct texture. */
195 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
196 if (This->resource.format == WINED3DFMT_DXT1 ||
197 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
198 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
199 if (!GL_SUPPORT(EXT_TEXTURE_COMPRESSION_S3TC)) {
200 FIXME("Using DXT1/3/5 without advertized support\n");
202 /* glCompressedTexSubImage2D for uploading and glTexImage2D for allocating does not work well on some drivers(r200 dri, MacOS ATI driver)
203 * glCompressedTexImage2D does not accept NULL pointers. So for compressed textures surface_allocate_surface does nothing, and this
204 * function uses glCompressedTexImage2D instead of the SubImage call
206 TRACE("(%p) : Calling glCompressedTexSubImage2D w %d, h %d, data %p\n", This, width, height, data);
209 if(This->Flags & SFLAG_PBO) {
210 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
211 checkGLcall("glBindBufferARB");
212 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
214 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
215 width, height, 0 /* border */, This->resource.size, NULL));
216 checkGLcall("glCompressedTexSubImage2D");
218 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
219 checkGLcall("glBindBufferARB");
221 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
222 width, height, 0 /* border */, This->resource.size, data));
223 checkGLcall("glCompressedTexSubImage2D");
228 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
231 if(This->Flags & SFLAG_PBO) {
232 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
233 checkGLcall("glBindBufferARB");
234 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
236 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
237 checkGLcall("glTexSubImage2D");
239 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
240 checkGLcall("glBindBufferARB");
243 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
244 checkGLcall("glTexSubImage2D");
251 /* This call just allocates the texture, the caller is responsible for
252 * activating the right context and binding the correct texture. */
253 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
254 BOOL enable_client_storage = FALSE;
257 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,
258 This->glDescription.target, This->glDescription.level, debug_d3dformat(This->resource.format), internal, width, height, format, type);
260 if (This->resource.format == WINED3DFMT_DXT1 ||
261 This->resource.format == WINED3DFMT_DXT2 || This->resource.format == WINED3DFMT_DXT3 ||
262 This->resource.format == WINED3DFMT_DXT4 || This->resource.format == WINED3DFMT_DXT5) {
263 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
264 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
266 /* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
267 * once, unfortunately
269 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
270 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
271 This->Flags |= SFLAG_CLIENT;
272 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
273 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
274 width, height, 0 /* border */, This->resource.size, mem));
282 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
283 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
284 /* In some cases we want to disable client storage.
285 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
286 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
287 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
288 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
289 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
291 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
292 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
293 This->Flags &= ~SFLAG_CLIENT;
294 enable_client_storage = TRUE;
296 This->Flags |= SFLAG_CLIENT;
298 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
299 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
301 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
304 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
305 checkGLcall("glTexImage2D");
307 if(enable_client_storage) {
308 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
309 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
313 This->Flags |= SFLAG_ALLOCATED;
316 /* In D3D the depth stencil dimensions have to be greater than or equal to the
317 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
318 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
319 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
320 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
321 renderbuffer_entry_t *entry;
322 GLuint renderbuffer = 0;
323 unsigned int src_width, src_height;
325 src_width = This->pow2Width;
326 src_height = This->pow2Height;
328 /* A depth stencil smaller than the render target is not valid */
329 if (width > src_width || height > src_height) return;
331 /* Remove any renderbuffer set if the sizes match */
332 if (width == src_width && height == src_height) {
333 This->current_renderbuffer = NULL;
337 /* Look if we've already got a renderbuffer of the correct dimensions */
338 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
339 if (entry->width == width && entry->height == height) {
340 renderbuffer = entry->id;
341 This->current_renderbuffer = entry;
347 const GlPixelFormatDesc *glDesc;
348 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
350 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
351 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
352 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, glDesc->glFormat, width, height));
354 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
355 entry->width = width;
356 entry->height = height;
357 entry->id = renderbuffer;
358 list_add_head(&This->renderbuffers, &entry->entry);
360 This->current_renderbuffer = entry;
363 checkGLcall("set_compatible_renderbuffer");
366 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
367 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
368 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
370 TRACE("(%p) : swapchain %p\n", This, swapchain);
372 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
373 TRACE("Returning GL_BACK\n");
375 } else if (swapchain_impl->frontBuffer == iface) {
376 TRACE("Returning GL_FRONT\n");
380 FIXME("Higher back buffer, returning GL_BACK\n");
384 ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface) {
385 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
386 ULONG ref = InterlockedDecrement(&This->resource.ref);
387 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
389 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
390 renderbuffer_entry_t *entry, *entry2;
391 TRACE("(%p) : cleaning up\n", This);
393 if (This->glDescription.textureName != 0) { /* release the openGL texture.. */
395 /* Need a context to destroy the texture. Use the currently active render target, but only if
396 * the primary render target exists. Otherwise lastActiveRenderTarget is garbage, see above.
397 * When destroying the primary rt, Uninit3D will activate a context before doing anything
399 if(device->render_targets && device->render_targets[0]) {
400 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
403 TRACE("Deleting texture %d\n", This->glDescription.textureName);
405 glDeleteTextures(1, &This->glDescription.textureName);
409 if(This->Flags & SFLAG_PBO) {
411 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
414 if(This->Flags & SFLAG_DIBSECTION) {
416 SelectObject(This->hDC, This->dib.holdbitmap);
418 /* Release the DIB section */
419 DeleteObject(This->dib.DIBsection);
420 This->dib.bitmap_data = NULL;
421 This->resource.allocatedMemory = NULL;
423 if(This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem(iface, NULL);
425 HeapFree(GetProcessHeap(), 0, This->palette9);
427 IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
428 if(iface == device->ddraw_primary)
429 device->ddraw_primary = NULL;
431 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
432 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
433 HeapFree(GetProcessHeap(), 0, entry);
436 TRACE("(%p) Released\n", This);
437 HeapFree(GetProcessHeap(), 0, This);
443 /* ****************************************************
444 IWineD3DSurface IWineD3DResource parts follow
445 **************************************************** */
447 void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
448 /* TODO: check for locks */
449 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
450 IWineD3DBaseTexture *baseTexture = NULL;
451 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
453 TRACE("(%p)Checking to see if the container is a base texture\n", This);
454 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
455 TRACE("Passing to container\n");
456 IWineD3DBaseTexture_PreLoad(baseTexture);
457 IWineD3DBaseTexture_Release(baseTexture);
459 TRACE("(%p) : About to load surface\n", This);
461 if(!device->isInDraw) {
462 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
466 glEnable(This->glDescription.target);/* make sure texture support is enabled in this context */
467 if (!This->glDescription.level) {
468 if (!This->glDescription.textureName) {
469 glGenTextures(1, &This->glDescription.textureName);
470 checkGLcall("glGenTextures");
471 TRACE("Surface %p given name %d\n", This, This->glDescription.textureName);
473 glBindTexture(This->glDescription.target, This->glDescription.textureName);
474 checkGLcall("glBindTexture");
475 IWineD3DSurface_LoadTexture(iface, FALSE);
476 /* This is where we should be reducing the amount of GLMemoryUsed */
477 } else if (This->glDescription.textureName) { /* NOTE: the level 0 surface of a mpmapped texture must be loaded first! */
478 /* assume this is a coding error not a real error for now */
479 FIXME("Mipmap surface has a glTexture bound to it!\n");
481 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
482 /* Tell opengl to try and keep this texture in video ram (well mostly) */
485 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
492 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
493 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
494 This->resource.allocatedMemory =
495 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
498 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
499 checkGLcall("glBindBuffer(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
500 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
501 checkGLcall("glGetBufferSubData");
502 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
503 checkGLcall("glDeleteBuffers");
507 This->Flags &= ~SFLAG_PBO;
510 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
511 IWineD3DBaseTexture *texture = NULL;
512 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
513 TRACE("(%p)\n", iface);
515 /* Default pool resources are supposed to be destroyed before Reset is called.
516 * Implicit resources stay however. So this means we have an implicit render target
517 * or depth stencil, and the content isn't supposed to survive the reset anyway
519 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
520 TRACE("Default pool - nothing to do\n");
524 /* Load the surface into system memory */
525 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
527 /* Destroy PBOs, but load them into real sysmem before */
528 if(This->Flags & SFLAG_PBO) {
529 surface_remove_pbo(This);
532 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
535 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
538 glDeleteTextures(1, &This->glDescription.textureName);
539 This->glDescription.textureName = 0;
542 IWineD3DBaseTexture_Release(texture);
547 /* ******************************************************
548 IWineD3DSurface IWineD3DSurface parts follow
549 ****************************************************** */
551 void WINAPI IWineD3DSurfaceImpl_SetGlTextureDesc(IWineD3DSurface *iface, UINT textureName, int target) {
552 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
553 TRACE("(%p) : setting textureName %u, target %i\n", This, textureName, target);
554 if (This->glDescription.textureName == 0 && textureName != 0) {
555 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
556 IWineD3DSurface_AddDirtyRect(iface, NULL);
558 This->glDescription.textureName = textureName;
559 This->glDescription.target = target;
560 This->Flags &= ~SFLAG_ALLOCATED;
563 void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription) {
564 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
565 TRACE("(%p) : returning %p\n", This, &This->glDescription);
566 *glDescription = &This->glDescription;
569 /* TODO: think about moving this down to resource? */
570 const void *WINAPI IWineD3DSurfaceImpl_GetData(IWineD3DSurface *iface) {
571 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
572 /* 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 */
573 if (This->resource.pool != WINED3DPOOL_SYSTEMMEM) {
574 FIXME(" (%p)Attempting to get system memory for a non-system memory texture\n", iface);
576 return (CONST void*)(This->resource.allocatedMemory);
579 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
580 IWineD3DSwapChainImpl *swapchain;
581 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
585 BYTE *row, *top, *bottom;
589 BOOL srcIsUpsideDown;
591 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
592 static BOOL warned = FALSE;
594 ERR("The application tries to lock the render target, but render target locking is disabled\n");
600 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
601 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
602 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
603 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
604 * context->last_was_blit set on the unlock.
606 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
609 /* Select the correct read buffer, and give some debug output.
610 * There is no need to keep track of the current read buffer or reset it, every part of the code
611 * that reads sets the read buffer as desired.
614 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
615 * Read from the back buffer
617 TRACE("Locking offscreen render target\n");
618 glReadBuffer(myDevice->offscreenBuffer);
619 srcIsUpsideDown = TRUE;
621 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
622 TRACE("Locking %#x buffer\n", buffer);
623 glReadBuffer(buffer);
624 checkGLcall("glReadBuffer");
626 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
627 srcIsUpsideDown = FALSE;
630 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
634 local_rect.right = This->currentDesc.Width;
635 local_rect.bottom = This->currentDesc.Height;
639 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
641 switch(This->resource.format)
645 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
646 /* In case of P8 render targets the index is stored in the alpha component */
648 type = GL_UNSIGNED_BYTE;
650 bpp = This->bytesPerPixel;
652 /* GL can't return palettized data, so read ARGB pixels into a
653 * separate block of memory and convert them into palettized format
654 * in software. Slow, but if the app means to use palettized render
655 * targets and locks it...
657 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
658 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
659 * for the color channels when palettizing the colors.
662 type = GL_UNSIGNED_BYTE;
664 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
666 ERR("Out of memory\n");
670 bpp = This->bytesPerPixel * 3;
677 fmt = This->glDescription.glFormat;
678 type = This->glDescription.glType;
679 bpp = This->bytesPerPixel;
682 if(This->Flags & SFLAG_PBO) {
683 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
684 checkGLcall("glBindBufferARB");
687 glReadPixels(local_rect.left, local_rect.top,
688 local_rect.right - local_rect.left,
689 local_rect.bottom - local_rect.top,
691 vcheckGLcall("glReadPixels");
693 if(This->Flags & SFLAG_PBO) {
694 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
695 checkGLcall("glBindBufferARB");
697 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
698 * to get a pointer to it and perform the flipping in software. This is a lot
699 * faster than calling glReadPixels for each line. In case we want more speed
700 * we should rerender it flipped in a FBO and read the data back from the FBO. */
701 if(!srcIsUpsideDown) {
702 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
703 checkGLcall("glBindBufferARB");
705 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
706 checkGLcall("glMapBufferARB");
710 /* TODO: Merge this with the palettization loop below for P8 targets */
711 if(!srcIsUpsideDown) {
713 /* glReadPixels returns the image upside down, and there is no way to prevent this.
714 Flip the lines in software */
715 len = (local_rect.right - local_rect.left) * bpp;
716 off = local_rect.left * bpp;
718 row = HeapAlloc(GetProcessHeap(), 0, len);
720 ERR("Out of memory\n");
721 if(This->resource.format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
726 top = mem + pitch * local_rect.top;
727 bottom = mem + pitch * ( local_rect.bottom - local_rect.top - 1);
728 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
729 memcpy(row, top + off, len);
730 memcpy(top + off, bottom + off, len);
731 memcpy(bottom + off, row, len);
735 HeapFree(GetProcessHeap(), 0, row);
737 /* Unmap the temp PBO buffer */
738 if(This->Flags & SFLAG_PBO) {
739 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
740 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
744 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
745 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
746 * the same color but we have no choice.
747 * In case of render targets, the index is stored in the alpha component so no conversion is needed.
749 if((This->resource.format == WINED3DFMT_P8) && !(This->resource.usage & WINED3DUSAGE_RENDERTARGET)) {
751 DWORD width = pitch / 3;
754 pal = This->palette->palents;
756 pal = This->resource.wineD3DDevice->palettes[This->resource.wineD3DDevice->currentPalette];
759 for(y = local_rect.top; y < local_rect.bottom; y++) {
760 for(x = local_rect.left; x < local_rect.right; x++) {
761 /* start lines pixels */
762 BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
763 BYTE *green = blue + 1;
764 BYTE *red = green + 1;
766 for(c = 0; c < 256; c++) {
767 if(*red == pal[c].peRed &&
768 *green == pal[c].peGreen &&
769 *blue == pal[c].peBlue)
771 *((BYTE *) dest + y * width + x) = c;
777 HeapFree(GetProcessHeap(), 0, mem);
782 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
783 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
784 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
787 if(!(This->Flags & SFLAG_DYNLOCK)) {
789 /* MAXLOCKCOUNT is defined in wined3d_private.h */
790 if(This->lockCount > MAXLOCKCOUNT) {
791 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
792 This->Flags |= SFLAG_DYNLOCK;
796 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
797 * Also don't create a PBO for systemmem surfaces.
799 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
803 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
804 error = glGetError();
805 if(This->pbo == 0 || error != GL_NO_ERROR) {
806 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
809 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
811 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
812 checkGLcall("glBindBufferARB");
814 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
815 checkGLcall("glBufferDataARB");
817 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
818 checkGLcall("glBindBufferARB");
820 /* We don't need the system memory anymore and we can't even use it for PBOs */
821 if(!(This->Flags & SFLAG_CLIENT)) {
822 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
823 This->resource.heapMemory = NULL;
825 This->resource.allocatedMemory = NULL;
826 This->Flags |= SFLAG_PBO;
828 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
829 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
832 if(!This->resource.heapMemory) {
833 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
835 This->resource.allocatedMemory =
836 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
837 if(This->Flags & SFLAG_INSYSMEM) {
838 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
843 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
844 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
845 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
846 IWineD3DSwapChain *swapchain = NULL;
848 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
850 /* This is also done in the base class, but we have to verify this before loading any data from
851 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
852 * may interfere, and all other bad things may happen
854 if (This->Flags & SFLAG_LOCKED) {
855 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
856 return WINED3DERR_INVALIDCALL;
858 This->Flags |= SFLAG_LOCKED;
860 if (!(This->Flags & SFLAG_LOCKABLE))
862 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
865 if (Flags & WINED3DLOCK_DISCARD) {
866 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
867 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
868 This->Flags |= SFLAG_INSYSMEM;
871 if (This->Flags & SFLAG_INSYSMEM) {
872 TRACE("Local copy is up to date, not downloading data\n");
873 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
877 /* Now download the surface content from opengl
878 * Use the render target readback if the surface is on a swapchain(=onscreen render target) or the current primary target
879 * Offscreen targets which are not active at the moment or are higher targets(fbos) can be locked with the texture path
881 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
882 if(swapchain || iface == myDevice->render_targets[0]) {
883 const RECT *pass_rect = pRect;
885 /* IWineD3DSurface_LoadLocation does not check if the rectangle specifies the full surfaces
886 * because most caller functions do not need that. So do that here
891 pRect->right == This->currentDesc.Width &&
892 pRect->bottom == This->currentDesc.Height) {
896 switch(wined3d_settings.rendertargetlock_mode) {
899 FIXME("Reading from render target with a texture isn't implemented yet, falling back to framebuffer reading\n");
901 /* Disabled for now. LoadLocation prefers the texture over the drawable as the source. So if we copy to the
902 * texture first, then to sysmem, we'll avoid glReadPixels and use glCopyTexImage and glGetTexImage2D instead.
903 * This may be faster on some cards
905 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* No partial texture copy yet */);
912 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pRect);
918 if(swapchain) IWineD3DSwapChain_Release(swapchain);
920 } else if(iface == myDevice->stencilBufferTarget) {
921 /** the depth stencil in openGL has a format of GL_FLOAT
922 * which should be good for WINED3DFMT_D16_LOCKABLE
924 * it is unclear what format the stencil buffer is in except.
925 * 'Each index is converted to fixed point...
926 * If GL_MAP_STENCIL is GL_TRUE, indices are replaced by their
927 * mappings in the table GL_PIXEL_MAP_S_TO_S.
928 * glReadPixels(This->lockedRect.left,
929 * This->lockedRect.bottom - j - 1,
930 * This->lockedRect.right - This->lockedRect.left,
932 * GL_DEPTH_COMPONENT,
934 * (char *)pLockedRect->pBits + (pLockedRect->Pitch * (j-This->lockedRect.top)));
936 * Depth Stencil surfaces which are not the current depth stencil target should have their data in a
937 * gl texture(next path), or in local memory(early return because of set SFLAG_INSYSMEM above). If
938 * none of that is the case the problem is not in this function :-)
939 ********************************************/
940 FIXME("Depth stencil locking not supported yet\n");
942 /* This path is for normal surfaces, offscreen render targets and everything else that is in a gl texture */
943 TRACE("locking an ordinary surface\n");
944 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
948 if(This->Flags & SFLAG_PBO) {
949 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
951 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
952 checkGLcall("glBindBufferARB");
954 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
955 if(This->resource.allocatedMemory) {
956 ERR("The surface already has PBO memory allocated!\n");
959 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
960 checkGLcall("glMapBufferARB");
962 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
963 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
964 checkGLcall("glBindBufferARB");
969 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
972 IWineD3DBaseTexture *pBaseTexture;
975 * as seen in msdn docs
977 IWineD3DSurface_AddDirtyRect(iface, pRect);
979 /** Dirtify Container if needed */
980 if (WINED3D_OK == IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture) && pBaseTexture != NULL) {
981 TRACE("Making container dirty\n");
982 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
983 IWineD3DBaseTexture_Release(pBaseTexture);
985 TRACE("Surface is standalone, no need to dirty the container\n");
989 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
992 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
994 GLint prev_rasterpos[4];
996 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
997 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
998 IWineD3DSwapChainImpl *swapchain;
1000 /* Activate the correct context for the render target */
1001 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1004 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
1006 /* Primary offscreen render target */
1007 TRACE("Offscreen render target\n");
1008 glDrawBuffer(myDevice->offscreenBuffer);
1009 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1011 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1012 TRACE("Unlocking %#x buffer\n", buffer);
1013 glDrawBuffer(buffer);
1014 checkGLcall("glDrawBuffer");
1016 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1020 vcheckGLcall("glFlush");
1021 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1022 vcheckGLcall("glIntegerv");
1023 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1024 vcheckGLcall("glIntegerv");
1025 glPixelZoom(1.0, -1.0);
1026 vcheckGLcall("glPixelZoom");
1028 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1029 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1030 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1032 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1033 vcheckGLcall("glRasterPos2f");
1035 /* Some drivers(radeon dri, others?) don't like exceptions during
1036 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1037 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1038 * catch to put the dib section in InSync mode, which leads to a crash
1039 * and a blocked x server on my radeon card.
1041 * The following lines read the dib section so it is put in inSync mode
1042 * before glDrawPixels is called and the crash is prevented. There won't
1043 * be any interfering gdi accesses, because UnlockRect is called from
1044 * ReleaseDC, and the app won't use the dc any more afterwards.
1046 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1048 read = This->resource.allocatedMemory[0];
1051 if(This->Flags & SFLAG_PBO) {
1052 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1053 checkGLcall("glBindBufferARB");
1056 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1057 (This->lockedRect.bottom - This->lockedRect.top)-1,
1059 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1060 checkGLcall("glDrawPixels");
1062 if(This->Flags & SFLAG_PBO) {
1063 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1064 checkGLcall("glBindBufferARB");
1067 glPixelZoom(1.0,1.0);
1068 vcheckGLcall("glPixelZoom");
1070 glRasterPos3iv(&prev_rasterpos[0]);
1071 vcheckGLcall("glRasterPos3iv");
1073 /* Reset to previous pack row length */
1074 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1075 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1078 glDrawBuffer(myDevice->offscreenBuffer);
1079 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1080 } else if(swapchain->backBuffer) {
1081 glDrawBuffer(GL_BACK);
1082 checkGLcall("glDrawBuffer(GL_BACK)");
1084 glDrawBuffer(GL_FRONT);
1085 checkGLcall("glDrawBuffer(GL_FRONT)");
1092 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1093 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1094 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1095 IWineD3DSwapChainImpl *swapchain = NULL;
1098 if (!(This->Flags & SFLAG_LOCKED)) {
1099 WARN("trying to Unlock an unlocked surf@%p\n", This);
1100 return WINED3DERR_INVALIDCALL;
1103 if (This->Flags & SFLAG_PBO) {
1104 TRACE("Freeing PBO memory\n");
1105 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1107 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1108 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1109 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1110 checkGLcall("glUnmapBufferARB");
1112 This->resource.allocatedMemory = NULL;
1115 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1117 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1118 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1122 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1123 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1124 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1126 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1127 static BOOL warned = FALSE;
1129 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1135 if(This->dirtyRect.left == 0 &&
1136 This->dirtyRect.top == 0 &&
1137 This->dirtyRect.right == This->currentDesc.Width &&
1138 This->dirtyRect.bottom == This->currentDesc.Height) {
1141 /* TODO: Proper partial rectangle tracking */
1142 fullsurface = FALSE;
1143 This->Flags |= SFLAG_INSYSMEM;
1146 switch(wined3d_settings.rendertargetlock_mode) {
1149 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1151 if (This->glDescription.textureName == 0) {
1152 glGenTextures(1, &This->glDescription.textureName);
1153 checkGLcall("glGenTextures");
1155 glBindTexture(This->glDescription.target, This->glDescription.textureName);
1156 checkGLcall("glBindTexture(This->glDescription.target, This->glDescription.textureName)");
1158 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1164 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1169 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1170 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1171 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1172 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1173 * not fully up to date because only a subrectangle was read in LockRect.
1175 This->Flags &= ~SFLAG_INSYSMEM;
1176 This->Flags |= SFLAG_INDRAWABLE;
1179 This->dirtyRect.left = This->currentDesc.Width;
1180 This->dirtyRect.top = This->currentDesc.Height;
1181 This->dirtyRect.right = 0;
1182 This->dirtyRect.bottom = 0;
1183 } else if(iface == myDevice->stencilBufferTarget) {
1184 FIXME("Depth Stencil buffer locking is not implemented\n");
1186 /* The rest should be a normal texture */
1187 IWineD3DBaseTextureImpl *impl;
1188 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1189 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1190 * states need resetting
1192 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1193 if(impl->baseTexture.bindCount) {
1194 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1196 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1201 This->Flags &= ~SFLAG_LOCKED;
1202 memset(&This->lockedRect, 0, sizeof(RECT));
1206 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1207 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1208 WINED3DLOCKED_RECT lock;
1212 TRACE("(%p)->(%p)\n",This,pHDC);
1214 if(This->Flags & SFLAG_USERPTR) {
1215 ERR("Not supported on surfaces with an application-provided surfaces\n");
1216 return WINEDDERR_NODC;
1219 /* Give more detailed info for ddraw */
1220 if (This->Flags & SFLAG_DCINUSE)
1221 return WINEDDERR_DCALREADYCREATED;
1223 /* Can't GetDC if the surface is locked */
1224 if (This->Flags & SFLAG_LOCKED)
1225 return WINED3DERR_INVALIDCALL;
1227 memset(&lock, 0, sizeof(lock)); /* To be sure */
1229 /* Create a DIB section if there isn't a hdc yet */
1231 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1232 if(This->Flags & SFLAG_CLIENT) {
1233 IWineD3DSurface_PreLoad(iface);
1236 /* Use the dib section from now on if we are not using a PBO */
1237 if(!(This->Flags & SFLAG_PBO))
1238 This->resource.allocatedMemory = This->dib.bitmap_data;
1241 /* Lock the surface */
1242 hr = IWineD3DSurface_LockRect(iface,
1247 if(This->Flags & SFLAG_PBO) {
1248 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1249 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1253 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1254 /* keep the dib section */
1258 if(This->resource.format == WINED3DFMT_P8 ||
1259 This->resource.format == WINED3DFMT_A8P8) {
1262 PALETTEENTRY ent[256];
1264 GetPaletteEntries(This->palette->hpal, 0, 256, ent);
1265 for (n=0; n<256; n++) {
1266 col[n].rgbRed = ent[n].peRed;
1267 col[n].rgbGreen = ent[n].peGreen;
1268 col[n].rgbBlue = ent[n].peBlue;
1269 col[n].rgbReserved = 0;
1272 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1274 for (n=0; n<256; n++) {
1275 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
1276 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
1277 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
1278 col[n].rgbReserved = 0;
1282 SetDIBColorTable(This->hDC, 0, 256, col);
1286 TRACE("returning %p\n",*pHDC);
1287 This->Flags |= SFLAG_DCINUSE;
1292 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1293 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1295 TRACE("(%p)->(%p)\n",This,hDC);
1297 if (!(This->Flags & SFLAG_DCINUSE))
1298 return WINED3DERR_INVALIDCALL;
1300 if (This->hDC !=hDC) {
1301 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1302 return WINED3DERR_INVALIDCALL;
1305 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1306 /* Copy the contents of the DIB over to the PBO */
1307 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1310 /* we locked first, so unlock now */
1311 IWineD3DSurface_UnlockRect(iface);
1313 This->Flags &= ~SFLAG_DCINUSE;
1318 /* ******************************************************
1319 IWineD3DSurface Internal (No mapping to directx api) parts follow
1320 ****************************************************** */
1322 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) {
1323 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1324 const GlPixelFormatDesc *glDesc;
1325 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1326 BOOL p8_render_target = FALSE;
1327 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1329 /* Default values: From the surface */
1330 *format = glDesc->glFormat;
1331 *type = glDesc->glType;
1332 *convert = NO_CONVERSION;
1333 *target_bpp = This->bytesPerPixel;
1336 *internal = glDesc->glGammaInternal;
1337 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
1338 *internal = glDesc->rtInternal;
1340 *internal = glDesc->glInternal;
1343 /* Ok, now look if we have to do any conversion */
1344 switch(This->resource.format) {
1350 if (device->render_targets && device->render_targets[0]) {
1351 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
1352 if((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET) && (render_target->resource.format == WINED3DFMT_P8))
1353 p8_render_target = TRUE;
1356 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1357 * of the two is available make sure texturing is requested as neither of the two works in
1358 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1359 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1360 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1361 * conflicts with this.
1363 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) || (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) && p8_render_target)) || colorkey_active || !use_texturing ) {
1365 *internal = GL_RGBA;
1366 *type = GL_UNSIGNED_BYTE;
1368 if(colorkey_active) {
1369 *convert = CONVERT_PALETTED_CK;
1371 *convert = CONVERT_PALETTED;
1374 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1376 *internal = GL_RGBA;
1377 *type = GL_UNSIGNED_BYTE;
1383 case WINED3DFMT_R3G3B2:
1384 /* **********************
1385 GL_UNSIGNED_BYTE_3_3_2
1386 ********************** */
1387 if (colorkey_active) {
1388 /* This texture format will never be used.. So do not care about color keying
1389 up until the point in time it will be needed :-) */
1390 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1394 case WINED3DFMT_R5G6B5:
1395 if (colorkey_active) {
1396 *convert = CONVERT_CK_565;
1398 *internal = GL_RGBA;
1399 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1403 case WINED3DFMT_X1R5G5B5:
1404 if (colorkey_active) {
1405 *convert = CONVERT_CK_5551;
1407 *internal = GL_RGBA;
1408 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1412 case WINED3DFMT_R8G8B8:
1413 if (colorkey_active) {
1414 *convert = CONVERT_CK_RGB24;
1416 *internal = GL_RGBA;
1417 *type = GL_UNSIGNED_INT_8_8_8_8;
1422 case WINED3DFMT_X8R8G8B8:
1423 if (colorkey_active) {
1424 *convert = CONVERT_RGB32_888;
1426 *internal = GL_RGBA;
1427 *type = GL_UNSIGNED_INT_8_8_8_8;
1431 case WINED3DFMT_V8U8:
1432 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1433 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1434 *format = GL_DUDV_ATI;
1435 *internal = GL_DU8DV8_ATI;
1437 /* No conversion - Just change the gl type */
1440 *convert = CONVERT_V8U8;
1442 *internal = GL_RGB8;
1443 *type = GL_UNSIGNED_BYTE;
1447 case WINED3DFMT_L6V5U5:
1448 *convert = CONVERT_L6V5U5;
1449 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1451 /* Use format and types from table */
1453 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1456 *internal = GL_RGB5;
1457 *type = GL_UNSIGNED_SHORT_5_6_5;
1461 case WINED3DFMT_X8L8V8U8:
1462 *convert = CONVERT_X8L8V8U8;
1464 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1465 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1466 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1467 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1468 * the needed type and format parameter, so the internal format contains a
1469 * 4th component, which is returned as alpha
1472 /* Not supported by GL_ATI_envmap_bumpmap */
1474 *internal = GL_RGB8;
1475 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1479 case WINED3DFMT_Q8W8V8U8:
1480 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1481 *convert = CONVERT_Q8W8V8U8;
1483 *internal = GL_RGBA8;
1484 *type = GL_UNSIGNED_BYTE;
1486 /* Not supported by GL_ATI_envmap_bumpmap */
1489 case WINED3DFMT_V16U16:
1490 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1491 *convert = CONVERT_V16U16;
1493 *internal = GL_RGB16_EXT;
1494 *type = GL_UNSIGNED_SHORT;
1496 /* What should I do here about GL_ATI_envmap_bumpmap?
1497 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1501 case WINED3DFMT_A4L4:
1502 /* A4L4 exists as an internal gl format, but for some reason there is not
1503 * format+type combination to load it. Thus convert it to A8L8, then load it
1504 * with A4L4 internal, but A8L8 format+type
1506 *convert = CONVERT_A4L4;
1507 *format = GL_LUMINANCE_ALPHA;
1508 *internal = GL_LUMINANCE4_ALPHA4;
1509 *type = GL_UNSIGNED_BYTE;
1513 case WINED3DFMT_R32F:
1514 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1515 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1516 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1519 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1521 *convert = CONVERT_R32F;
1523 *internal = GL_RGB32F_ARB;
1528 case WINED3DFMT_R16F:
1529 /* Similar to R32F */
1530 *convert = CONVERT_R16F;
1532 *internal = GL_RGB16F_ARB;
1533 *type = GL_HALF_FLOAT_ARB;
1537 case WINED3DFMT_G16R16:
1538 *convert = CONVERT_G16R16;
1540 *internal = GL_RGB16_EXT;
1541 *type = GL_UNSIGNED_SHORT;
1552 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This) {
1553 BYTE *source, *dest;
1554 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1559 memcpy(dst, src, pitch * height);
1562 case CONVERT_PALETTED:
1563 case CONVERT_PALETTED_CK:
1565 IWineD3DPaletteImpl* pal = This->palette;
1570 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1573 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1575 for (y = 0; y < height; y++)
1577 source = src + pitch * y;
1578 dest = dst + outpitch * y;
1579 /* This is an 1 bpp format, using the width here is fine */
1580 for (x = 0; x < width; x++) {
1581 BYTE color = *source++;
1582 *dest++ = table[color][0];
1583 *dest++ = table[color][1];
1584 *dest++ = table[color][2];
1585 *dest++ = table[color][3];
1591 case CONVERT_CK_565:
1593 /* Converting the 565 format in 5551 packed to emulate color-keying.
1595 Note : in all these conversion, it would be best to average the averaging
1596 pixels to get the color of the pixel that will be color-keyed to
1597 prevent 'color bleeding'. This will be done later on if ever it is
1600 Note2: Nvidia documents say that their driver does not support alpha + color keying
1601 on the same surface and disables color keying in such a case
1607 TRACE("Color keyed 565\n");
1609 for (y = 0; y < height; y++) {
1610 Source = (WORD *) (src + y * pitch);
1611 Dest = (WORD *) (dst + y * outpitch);
1612 for (x = 0; x < width; x++ ) {
1613 WORD color = *Source++;
1614 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1615 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1616 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1625 case CONVERT_CK_5551:
1627 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1631 TRACE("Color keyed 5551\n");
1632 for (y = 0; y < height; y++) {
1633 Source = (WORD *) (src + y * pitch);
1634 Dest = (WORD *) (dst + y * outpitch);
1635 for (x = 0; x < width; x++ ) {
1636 WORD color = *Source++;
1638 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1639 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1643 *Dest &= ~(1 << 15);
1655 unsigned char *Dest;
1656 for(y = 0; y < height; y++) {
1657 Source = (short *) (src + y * pitch);
1658 Dest = dst + y * outpitch;
1659 for (x = 0; x < width; x++ ) {
1660 long color = (*Source++);
1661 /* B */ Dest[0] = 0xff;
1662 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1663 /* R */ Dest[2] = (color) + 128; /* U */
1670 case CONVERT_V16U16:
1674 unsigned short *Dest;
1675 for(y = 0; y < height; y++) {
1676 Source = (DWORD *) (src + y * pitch);
1677 Dest = (unsigned short *) (dst + y * outpitch);
1678 for (x = 0; x < width; x++ ) {
1679 DWORD color = (*Source++);
1680 /* B */ Dest[0] = 0xffff;
1681 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1682 /* R */ Dest[2] = (color ) + 32768; /* U */
1689 case CONVERT_Q8W8V8U8:
1693 unsigned char *Dest;
1694 for(y = 0; y < height; y++) {
1695 Source = (DWORD *) (src + y * pitch);
1696 Dest = dst + y * outpitch;
1697 for (x = 0; x < width; x++ ) {
1698 long color = (*Source++);
1699 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1700 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1701 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1702 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1709 case CONVERT_L6V5U5:
1713 unsigned char *Dest;
1715 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1716 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1717 * fixed function and shaders without further conversion once the surface is
1720 for(y = 0; y < height; y++) {
1721 Source = (WORD *) (src + y * pitch);
1722 Dest = dst + y * outpitch;
1723 for (x = 0; x < width; x++ ) {
1724 short color = (*Source++);
1725 unsigned char l = ((color >> 10) & 0xfc);
1726 char v = ((color >> 5) & 0x3e);
1727 char u = ((color ) & 0x1f);
1729 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1730 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1731 * shift. GL reads a signed value and converts it into an unsigned value.
1733 /* M */ Dest[2] = l << 1;
1735 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1736 * from 5 bit values to 8 bit values.
1738 /* V */ Dest[1] = v << 3;
1739 /* U */ Dest[0] = u << 3;
1744 for(y = 0; y < height; y++) {
1745 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
1746 Source = (WORD *) (src + y * pitch);
1747 for (x = 0; x < width; x++ ) {
1748 short color = (*Source++);
1749 unsigned char l = ((color >> 10) & 0xfc);
1750 short v = ((color >> 5) & 0x3e);
1751 short u = ((color ) & 0x1f);
1752 short v_conv = v + 16;
1753 short u_conv = u + 16;
1755 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
1763 case CONVERT_X8L8V8U8:
1767 unsigned char *Dest;
1769 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1770 /* This implementation works with the fixed function pipeline and shaders
1771 * without further modification after converting the surface.
1773 for(y = 0; y < height; y++) {
1774 Source = (DWORD *) (src + y * pitch);
1775 Dest = dst + y * outpitch;
1776 for (x = 0; x < width; x++ ) {
1777 long color = (*Source++);
1778 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
1779 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
1780 /* U */ Dest[0] = (color & 0xff); /* U */
1781 /* I */ Dest[3] = 255; /* X */
1786 /* Doesn't work correctly with the fixed function pipeline, but can work in
1787 * shaders if the shader is adjusted. (There's no use for this format in gl's
1788 * standard fixed function pipeline anyway).
1790 for(y = 0; y < height; y++) {
1791 Source = (DWORD *) (src + y * pitch);
1792 Dest = dst + y * outpitch;
1793 for (x = 0; x < width; x++ ) {
1794 long color = (*Source++);
1795 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
1796 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1797 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1808 unsigned char *Source;
1809 unsigned char *Dest;
1810 for(y = 0; y < height; y++) {
1811 Source = src + y * pitch;
1812 Dest = dst + y * outpitch;
1813 for (x = 0; x < width; x++ ) {
1814 unsigned char color = (*Source++);
1815 /* A */ Dest[1] = (color & 0xf0) << 0;
1816 /* L */ Dest[0] = (color & 0x0f) << 4;
1828 for(y = 0; y < height; y++) {
1829 Source = (float *) (src + y * pitch);
1830 Dest = (float *) (dst + y * outpitch);
1831 for (x = 0; x < width; x++ ) {
1832 float color = (*Source++);
1848 for(y = 0; y < height; y++) {
1849 Source = (WORD *) (src + y * pitch);
1850 Dest = (WORD *) (dst + y * outpitch);
1851 for (x = 0; x < width; x++ ) {
1852 WORD color = (*Source++);
1862 case CONVERT_G16R16:
1868 for(y = 0; y < height; y++) {
1869 Source = (WORD *) (src + y * pitch);
1870 Dest = (WORD *) (dst + y * outpitch);
1871 for (x = 0; x < width; x++ ) {
1872 WORD green = (*Source++);
1873 WORD red = (*Source++);
1884 ERR("Unsupported conversation type %d\n", convert);
1889 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
1890 IWineD3DPaletteImpl* pal = This->palette;
1891 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1892 BOOL index_in_alpha = FALSE;
1895 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
1896 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
1897 * is slow. Further RGB->P8 conversion is not possible because palettes can have
1898 * duplicate entries. Store the color key in the unused alpha component to speed the
1899 * download up and to make conversion unneeded. */
1900 if (device->render_targets && device->render_targets[0]) {
1901 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
1903 if((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET) && (render_target->resource.format == WINED3DFMT_P8))
1904 index_in_alpha = TRUE;
1908 /* Still no palette? Use the device's palette */
1909 /* Get the surface's palette */
1910 for (i = 0; i < 256; i++) {
1911 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1912 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1913 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1915 if(index_in_alpha) {
1917 } else if (colorkey &&
1918 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1919 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1920 /* We should maybe here put a more 'neutral' color than the standard bright purple
1921 one often used by application to prevent the nice purple borders when bi-linear
1929 TRACE("Using surface palette %p\n", pal);
1930 /* Get the surface's palette */
1931 for (i = 0; i < 256; i++) {
1932 table[i][0] = pal->palents[i].peRed;
1933 table[i][1] = pal->palents[i].peGreen;
1934 table[i][2] = pal->palents[i].peBlue;
1936 if(index_in_alpha) {
1939 else if (colorkey &&
1940 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1941 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1942 /* We should maybe here put a more 'neutral' color than the standard bright purple
1943 one often used by application to prevent the nice purple borders when bi-linear
1946 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
1947 table[i][3] = pal->palents[i].peFlags;
1955 const char *fragment_palette_conversion =
1958 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n" /* { 255/256, 0.5/255*255/256, 0, 0 } */
1959 "TEX index.x, fragment.texcoord[0], texture[0], 2D;\n" /* store the red-component of the current pixel */
1960 "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 */
1961 "TEX result.color, index, texture[1], 1D;\n" /* use the red-component as a index in the palette to get the final color */
1964 /* This function is used in case of 8bit paletted textures to upload the palette.
1965 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
1966 extensions like ATI_fragment_shaders is possible.
1968 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
1969 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1971 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1973 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1975 /* Try to use the paletted texture extension */
1976 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
1978 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
1979 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
1983 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
1984 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
1985 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
1987 /* Create the fragment program if we don't have it */
1988 if(!device->paletteConversionShader)
1990 glEnable(GL_FRAGMENT_PROGRAM_ARB);
1991 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
1992 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
1993 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), (const GLbyte *)fragment_palette_conversion));
1994 glDisable(GL_FRAGMENT_PROGRAM_ARB);
1997 glEnable(GL_FRAGMENT_PROGRAM_ARB);
1998 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2000 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2001 glEnable(GL_TEXTURE_1D);
2002 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2004 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2005 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2006 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2007 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2009 /* Switch back to unit 0 in which the 2D texture will be stored. */
2010 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2012 /* Rebind the texture because it isn't bound anymore */
2013 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2017 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2018 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2020 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2021 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2022 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2027 if(This->palette9) {
2028 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2032 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2034 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2038 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
2039 GLboolean oldwrite[4];
2041 /* Some formats have only some color channels, and the others are 1.0.
2042 * since our rendering renders to all channels, and those pixel formats
2043 * are emulated by using a full texture with the other channels set to 1.0
2044 * manually, clear the unused channels.
2046 * This could be done with hacking colorwriteenable to mask the colors,
2047 * but before drawing the buffer would have to be cleared too, so there's
2050 switch(This->resource.format) {
2051 case WINED3DFMT_R16F:
2052 case WINED3DFMT_R32F:
2053 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2054 /* Do not activate a context, the correct drawable is active already
2055 * though just the read buffer is set, make sure to have the correct draw
2058 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2059 glDisable(GL_SCISSOR_TEST);
2060 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2061 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2062 glClearColor(0.0, 1.0, 1.0, 1.0);
2063 glClear(GL_COLOR_BUFFER_BIT);
2064 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2065 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2066 checkGLcall("Unused channel clear\n");
2073 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2074 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2076 if (!(This->Flags & SFLAG_INTEXTURE)) {
2077 TRACE("Reloading because surface is dirty\n");
2078 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2079 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2080 /* Reload: vice versa OR */
2081 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2082 /* Also reload: Color key is active AND the color key has changed */
2083 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2084 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2085 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2086 TRACE("Reloading because of color keying\n");
2087 /* To perform the color key conversion we need a sysmem copy of
2088 * the surface. Make sure we have it
2090 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2091 } else if(palette9_changed(This)) {
2092 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
2093 /* TODO: This is not necessarily needed with hw palettized texture support */
2094 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2096 TRACE("surface is already in texture\n");
2100 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2101 * These resources are not bound by device size or format restrictions. Because of this,
2102 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2103 * However, these resources can always be created, locked, and copied.
2105 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2107 FIXME("(%p) Operation not supported for scratch textures\n",This);
2108 return WINED3DERR_INVALIDCALL;
2111 This->srgb = srgb_mode;
2112 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* no partial locking for textures yet */);
2116 static unsigned int gen = 0;
2119 if ((gen % 10) == 0) {
2120 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2121 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2124 * debugging crash code
2133 if (!(This->Flags & SFLAG_DONOTFREE)) {
2134 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2135 This->resource.allocatedMemory = NULL;
2136 This->resource.heapMemory = NULL;
2137 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2143 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface) {
2144 /* TODO: check for locks */
2145 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2146 IWineD3DBaseTexture *baseTexture = NULL;
2147 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2149 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2150 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2151 TRACE("Passing to container\n");
2152 IWineD3DBaseTexture_BindTexture(baseTexture);
2153 IWineD3DBaseTexture_Release(baseTexture);
2155 TRACE("(%p) : Binding surface\n", This);
2157 if(!device->isInDraw) {
2158 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2161 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2169 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2172 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2173 char *allocatedMemory;
2175 IWineD3DSwapChain *swapChain = NULL;
2177 GLuint tmpTexture = 0;
2180 Textures may not be stored in ->allocatedgMemory and a GlTexture
2181 so we should lock the surface before saving a snapshot, or at least check that
2183 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2184 by calling GetTexImage and in compressed form by calling
2185 GetCompressedTexImageARB. Queried compressed images can be saved and
2186 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2187 texture images do not need to be processed by the GL and should
2188 significantly improve texture loading performance relative to uncompressed
2191 /* Setup the width and height to be the internal texture width and height. */
2192 width = This->pow2Width;
2193 height = This->pow2Height;
2194 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2195 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2197 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2198 /* if were not a real texture then read the back buffer into a real texture */
2199 /* we don't want to interfere with the back buffer so read the data into a temporary
2200 * texture and then save the data out of the temporary texture
2204 TRACE("(%p) Reading render target into texture\n", This);
2205 glEnable(GL_TEXTURE_2D);
2207 glGenTextures(1, &tmpTexture);
2208 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2210 glTexImage2D(GL_TEXTURE_2D,
2217 GL_UNSIGNED_INT_8_8_8_8_REV,
2220 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2221 vcheckGLcall("glGetIntegerv");
2222 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2223 vcheckGLcall("glReadBuffer");
2224 glCopyTexImage2D(GL_TEXTURE_2D,
2233 checkGLcall("glCopyTexImage2D");
2234 glReadBuffer(prevRead);
2237 } else { /* bind the real texture, and make sure it up to date */
2238 IWineD3DSurface_PreLoad(iface);
2240 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2242 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2243 glGetTexImage(GL_TEXTURE_2D,
2244 This->glDescription.level,
2246 GL_UNSIGNED_INT_8_8_8_8_REV,
2248 checkGLcall("glTexImage2D");
2250 glBindTexture(GL_TEXTURE_2D, 0);
2251 glDeleteTextures(1, &tmpTexture);
2255 f = fopen(filename, "w+");
2257 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2258 return WINED3DERR_INVALIDCALL;
2260 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2261 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2276 fwrite(&width,2,1,f);
2278 fwrite(&height,2,1,f);
2283 /* 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 */
2285 textureRow = allocatedMemory + (width * (height - 1) *4);
2287 textureRow = allocatedMemory;
2288 for (y = 0 ; y < height; y++) {
2289 for (i = 0; i < width; i++) {
2290 color = *((DWORD*)textureRow);
2291 fputc((color >> 16) & 0xFF, f); /* B */
2292 fputc((color >> 8) & 0xFF, f); /* G */
2293 fputc((color >> 0) & 0xFF, f); /* R */
2294 fputc((color >> 24) & 0xFF, f); /* A */
2297 /* take two rows of the pointer to the texture memory */
2299 (textureRow-= width << 3);
2302 TRACE("Closing file\n");
2306 IWineD3DSwapChain_Release(swapChain);
2308 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2313 * Slightly inefficient way to handle multiple dirty rects but it works :)
2315 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2316 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2317 IWineD3DBaseTexture *baseTexture = NULL;
2319 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2320 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
2322 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2323 if (NULL != pDirtyRect) {
2324 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2325 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2326 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2327 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2329 This->dirtyRect.left = 0;
2330 This->dirtyRect.top = 0;
2331 This->dirtyRect.right = This->currentDesc.Width;
2332 This->dirtyRect.bottom = This->currentDesc.Height;
2334 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2335 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2336 /* if the container is a basetexture then mark it dirty. */
2337 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2338 TRACE("Passing to container\n");
2339 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2340 IWineD3DBaseTexture_Release(baseTexture);
2345 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2346 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2348 const GlPixelFormatDesc *glDesc;
2349 getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2351 TRACE("(%p) : Calling base function first\n", This);
2352 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2354 /* Setup some glformat defaults */
2355 This->glDescription.glFormat = glDesc->glFormat;
2356 This->glDescription.glFormatInternal = glDesc->glInternal;
2357 This->glDescription.glType = glDesc->glType;
2359 This->Flags &= ~SFLAG_ALLOCATED;
2360 TRACE("(%p) : glFormat %d, glFotmatInternal %d, glType %d\n", This,
2361 This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2366 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2367 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2369 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2370 WARN("Surface is locked or the HDC is in use\n");
2371 return WINED3DERR_INVALIDCALL;
2374 if(Mem && Mem != This->resource.allocatedMemory) {
2375 void *release = NULL;
2377 /* Do I have to copy the old surface content? */
2378 if(This->Flags & SFLAG_DIBSECTION) {
2379 /* Release the DC. No need to hold the critical section for the update
2380 * Thread because this thread runs only on front buffers, but this method
2381 * fails for render targets in the check above.
2383 SelectObject(This->hDC, This->dib.holdbitmap);
2384 DeleteDC(This->hDC);
2385 /* Release the DIB section */
2386 DeleteObject(This->dib.DIBsection);
2387 This->dib.bitmap_data = NULL;
2388 This->resource.allocatedMemory = NULL;
2390 This->Flags &= ~SFLAG_DIBSECTION;
2391 } else if(!(This->Flags & SFLAG_USERPTR)) {
2392 release = This->resource.heapMemory;
2393 This->resource.heapMemory = NULL;
2395 This->resource.allocatedMemory = Mem;
2396 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2398 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2399 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2401 /* For client textures opengl has to be notified */
2402 if(This->Flags & SFLAG_CLIENT) {
2403 This->Flags &= ~SFLAG_ALLOCATED;
2404 IWineD3DSurface_PreLoad(iface);
2405 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2408 /* Now free the old memory if any */
2409 HeapFree(GetProcessHeap(), 0, release);
2410 } else if(This->Flags & SFLAG_USERPTR) {
2411 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2412 This->resource.allocatedMemory = NULL;
2413 /* HeapMemory should be NULL already */
2414 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2415 This->Flags &= ~SFLAG_USERPTR;
2417 if(This->Flags & SFLAG_CLIENT) {
2418 This->Flags &= ~SFLAG_ALLOCATED;
2419 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2420 IWineD3DSurface_PreLoad(iface);
2426 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2427 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2428 IWineD3DSwapChainImpl *swapchain = NULL;
2430 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2432 /* Flipping is only supported on RenderTargets */
2433 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2436 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2437 * FIXME("(%p) Target override is not supported by now\n", This);
2438 * Additionally, it isn't really possible to support triple-buffering
2439 * properly on opengl at all
2443 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2445 ERR("Flipped surface is not on a swapchain\n");
2446 return WINEDDERR_NOTFLIPPABLE;
2449 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2450 * and only d3d8 and d3d9 apps specify the presentation interval
2452 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2453 /* Most common case first to avoid wasting time on all the other cases */
2454 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2455 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2456 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2457 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2458 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2459 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2460 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2462 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2465 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2466 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2467 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2471 /* Does a direct frame buffer -> texture copy. Stretching is done
2472 * with single pixel copy calls
2474 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2475 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2478 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2481 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2483 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2485 /* TODO: Do we need GL_TEXTURE_2D enabled fpr copyteximage? */
2486 glEnable(This->glDescription.target);
2487 checkGLcall("glEnable(This->glDescription.target)");
2489 /* Bind the target texture */
2490 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2491 checkGLcall("glBindTexture");
2493 glReadBuffer(myDevice->offscreenBuffer);
2495 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2496 glReadBuffer(buffer);
2498 checkGLcall("glReadBuffer");
2500 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2501 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2503 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2504 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2506 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2507 ERR("Texture filtering not supported in direct blit\n");
2509 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2510 ERR("Texture filtering not supported in direct blit\n");
2514 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2515 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2516 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2518 glCopyTexSubImage2D(This->glDescription.target,
2519 This->glDescription.level,
2520 drect->x1, drect->y1, /* xoffset, yoffset */
2521 srect->x1, Src->currentDesc.Height - srect->y2,
2522 drect->x2 - drect->x1, drect->y2 - drect->y1);
2524 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2525 /* I have to process this row by row to swap the image,
2526 * otherwise it would be upside down, so stretching in y direction
2527 * doesn't cost extra time
2529 * However, stretching in x direction can be avoided if not necessary
2531 for(row = drect->y1; row < drect->y2; row++) {
2532 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2533 /* Well, that stuff works, but it's very slow.
2534 * find a better way instead
2538 for(col = drect->x1; col < drect->x2; col++) {
2539 glCopyTexSubImage2D(This->glDescription.target,
2540 This->glDescription.level,
2541 drect->x1 + col, row, /* xoffset, yoffset */
2542 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2546 glCopyTexSubImage2D(This->glDescription.target,
2547 This->glDescription.level,
2548 drect->x1, row, /* xoffset, yoffset */
2549 srect->x1, yoffset - (int) (row * yrel),
2550 drect->x2-drect->x1, 1);
2554 vcheckGLcall("glCopyTexSubImage2D");
2556 /* Leave the opengl state valid for blitting */
2557 glDisable(This->glDescription.target);
2558 checkGLcall("glDisable(This->glDescription.target)");
2563 /* Uses the hardware to stretch and flip the image */
2564 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2565 GLuint src, backup = 0;
2566 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2567 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2568 float left, right, top, bottom; /* Texture coordinates */
2569 UINT fbwidth = Src->currentDesc.Width;
2570 UINT fbheight = Src->currentDesc.Height;
2571 GLenum drawBuffer = GL_BACK;
2572 GLenum texture_target;
2574 TRACE("Using hwstretch blit\n");
2575 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2576 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2579 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2581 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2582 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2584 if(GL_LIMITS(aux_buffers) >= 2) {
2585 /* Got more than one aux buffer? Use the 2nd aux buffer */
2586 drawBuffer = GL_AUX1;
2587 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2588 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2589 drawBuffer = GL_AUX0;
2592 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2593 glGenTextures(1, &backup);
2594 checkGLcall("glGenTextures\n");
2595 glBindTexture(GL_TEXTURE_2D, backup);
2596 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2597 texture_target = GL_TEXTURE_2D;
2599 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2600 * we are reading from the back buffer, the backup can be used as source texture
2602 if(Src->glDescription.textureName == 0) {
2603 /* Get it a description */
2604 IWineD3DSurface_PreLoad(SrcSurface);
2606 texture_target = Src->glDescription.target;
2607 glBindTexture(texture_target, Src->glDescription.textureName);
2608 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
2609 glEnable(texture_target);
2610 checkGLcall("glEnable(texture_target)");
2612 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2613 Src->Flags &= ~SFLAG_INTEXTURE;
2616 glReadBuffer(GL_BACK);
2617 checkGLcall("glReadBuffer(GL_BACK)");
2619 /* TODO: Only back up the part that will be overwritten */
2620 glCopyTexSubImage2D(texture_target, 0,
2621 0, 0 /* read offsets */,
2626 checkGLcall("glCopyTexSubImage2D");
2628 /* No issue with overriding these - the sampler is dirty due to blit usage */
2629 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
2630 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2631 checkGLcall("glTexParameteri");
2632 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2633 minMipLookup[Filter][WINED3DTEXF_NONE]);
2634 checkGLcall("glTexParameteri");
2636 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2637 src = backup ? backup : Src->glDescription.textureName;
2639 glReadBuffer(GL_FRONT);
2640 checkGLcall("glReadBuffer(GL_FRONT)");
2642 glGenTextures(1, &src);
2643 checkGLcall("glGenTextures(1, &src)");
2644 glBindTexture(GL_TEXTURE_2D, src);
2645 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2647 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2648 * out for power of 2 sizes
2650 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2651 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2652 checkGLcall("glTexImage2D");
2653 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2654 0, 0 /* read offsets */,
2659 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2660 checkGLcall("glTexParameteri");
2661 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2662 checkGLcall("glTexParameteri");
2664 glReadBuffer(GL_BACK);
2665 checkGLcall("glReadBuffer(GL_BACK)");
2667 if(texture_target != GL_TEXTURE_2D) {
2668 glDisable(texture_target);
2669 glEnable(GL_TEXTURE_2D);
2670 texture_target = GL_TEXTURE_2D;
2673 checkGLcall("glEnd and previous");
2675 left = (float) srect->x1 / (float) Src->pow2Width;
2676 right = (float) srect->x2 / (float) Src->pow2Width;
2679 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2680 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2682 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2683 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2686 /* draw the source texture stretched and upside down. The correct surface is bound already */
2687 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
2688 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
2690 glDrawBuffer(drawBuffer);
2691 glReadBuffer(drawBuffer);
2695 glTexCoord2f(left, bottom);
2696 glVertex2i(0, fbheight);
2699 glTexCoord2f(left, top);
2700 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2703 glTexCoord2f(right, top);
2704 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2707 glTexCoord2f(right, bottom);
2708 glVertex2i(drect->x2 - drect->x1, fbheight);
2710 checkGLcall("glEnd and previous");
2712 if(texture_target != This->glDescription.target) {
2713 glDisable(texture_target);
2714 glEnable(This->glDescription.target);
2715 texture_target = This->glDescription.target;
2718 /* Now read the stretched and upside down image into the destination texture */
2719 glBindTexture(texture_target, This->glDescription.textureName);
2720 checkGLcall("glBindTexture");
2721 glCopyTexSubImage2D(texture_target,
2723 drect->x1, drect->y1, /* xoffset, yoffset */
2724 0, 0, /* We blitted the image to the origin */
2725 drect->x2 - drect->x1, drect->y2 - drect->y1);
2726 checkGLcall("glCopyTexSubImage2D");
2728 if(drawBuffer == GL_BACK) {
2729 /* Write the back buffer backup back */
2731 if(texture_target != GL_TEXTURE_2D) {
2732 glDisable(texture_target);
2733 glEnable(GL_TEXTURE_2D);
2734 texture_target = GL_TEXTURE_2D;
2736 glBindTexture(GL_TEXTURE_2D, backup);
2737 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
2739 if(texture_target != Src->glDescription.target) {
2740 glDisable(texture_target);
2741 glEnable(Src->glDescription.target);
2742 texture_target = Src->glDescription.target;
2744 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
2745 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2750 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2754 glTexCoord2f(0.0, 0.0);
2755 glVertex2i(0, fbheight);
2758 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2759 glVertex2i(fbwidth, Src->currentDesc.Height);
2762 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2763 glVertex2i(fbwidth, 0);
2766 /* Restore the old draw buffer */
2767 glDrawBuffer(GL_BACK);
2769 glDisable(texture_target);
2770 checkGLcall("glDisable(texture_target)");
2773 if(src != Src->glDescription.textureName && src != backup) {
2774 glDeleteTextures(1, &src);
2775 checkGLcall("glDeleteTextures(1, &src)");
2778 glDeleteTextures(1, &backup);
2779 checkGLcall("glDeleteTextures(1, &backup)");
2785 /* Not called from the VTable */
2786 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2788 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2789 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2790 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2792 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2794 /* Get the swapchain. One of the surfaces has to be a primary surface */
2795 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2796 WARN("Destination is in sysmem, rejecting gl blt\n");
2797 return WINED3DERR_INVALIDCALL;
2799 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2800 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2802 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2803 WARN("Src is in sysmem, rejecting gl blt\n");
2804 return WINED3DERR_INVALIDCALL;
2806 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2807 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2810 /* Early sort out of cases where no render target is used */
2811 if(!dstSwapchain && !srcSwapchain &&
2812 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2813 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2814 return WINED3DERR_INVALIDCALL;
2817 /* No destination color keying supported */
2818 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
2819 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2820 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2821 return WINED3DERR_INVALIDCALL;
2825 rect.x1 = DestRect->left;
2826 rect.y1 = DestRect->top;
2827 rect.x2 = DestRect->right;
2828 rect.y2 = DestRect->bottom;
2832 rect.x2 = This->currentDesc.Width;
2833 rect.y2 = This->currentDesc.Height;
2836 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2837 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
2838 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
2839 /* Half-life does a Blt from the back buffer to the front buffer,
2840 * Full surface size, no flags... Use present instead
2842 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
2845 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2849 TRACE("Looking if a Present can be done...\n");
2850 /* Source Rectangle must be full surface */
2852 if(SrcRect->left != 0 || SrcRect->top != 0 ||
2853 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
2854 TRACE("No, Source rectangle doesn't match\n");
2860 mySrcRect.right = Src->currentDesc.Width;
2861 mySrcRect.bottom = Src->currentDesc.Height;
2863 /* No stretching may occur */
2864 if(mySrcRect.right != rect.x2 - rect.x1 ||
2865 mySrcRect.bottom != rect.y2 - rect.y1) {
2866 TRACE("No, stretching is done\n");
2870 /* Destination must be full surface or match the clipping rectangle */
2871 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
2875 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
2880 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
2883 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
2884 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
2886 TRACE("No, dest rectangle doesn't match(clipper)\n");
2887 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
2888 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
2894 if(rect.x1 != 0 || rect.y1 != 0 ||
2895 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
2896 TRACE("No, dest rectangle doesn't match(surface size)\n");
2903 /* These flags are unimportant for the flag check, remove them */
2904 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
2905 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
2907 /* The idea behind this is that a glReadPixels and a glDrawPixels call
2908 * take very long, while a flip is fast.
2909 * This applies to Half-Life, which does such Blts every time it finished
2910 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
2911 * menu. This is also used by all apps when they do windowed rendering
2913 * The problem is that flipping is not really the same as copying. After a
2914 * Blt the front buffer is a copy of the back buffer, and the back buffer is
2915 * untouched. Therefore it's necessary to override the swap effect
2916 * and to set it back after the flip.
2918 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
2922 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
2923 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2925 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
2926 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
2928 dstSwapchain->presentParms.SwapEffect = orig_swap;
2935 TRACE("Unsupported blit between buffers on the same swapchain\n");
2936 return WINED3DERR_INVALIDCALL;
2937 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
2938 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
2939 return WINED3DERR_INVALIDCALL;
2940 } else if(dstSwapchain && srcSwapchain) {
2941 FIXME("Implement hardware blit between two different swapchains\n");
2942 return WINED3DERR_INVALIDCALL;
2943 } else if(dstSwapchain) {
2944 if(SrcSurface == myDevice->render_targets[0]) {
2945 TRACE("Blit from active render target to a swapchain\n");
2946 /* Handled with regular texture -> swapchain blit */
2948 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2949 FIXME("Implement blit from a swapchain to the active render target\n");
2950 return WINED3DERR_INVALIDCALL;
2953 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
2954 /* Blit from render target to texture */
2956 BOOL upsideDown, stretchx;
2958 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
2959 TRACE("Color keying not supported by frame buffer to texture blit\n");
2960 return WINED3DERR_INVALIDCALL;
2961 /* Destination color key is checked above */
2964 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2965 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2968 if(SrcRect->top < SrcRect->bottom) {
2969 srect.y1 = SrcRect->top;
2970 srect.y2 = SrcRect->bottom;
2973 srect.y1 = SrcRect->bottom;
2974 srect.y2 = SrcRect->top;
2977 srect.x1 = SrcRect->left;
2978 srect.x2 = SrcRect->right;
2982 srect.x2 = Src->currentDesc.Width;
2983 srect.y2 = Src->currentDesc.Height;
2986 if(rect.x1 > rect.x2) {
2990 upsideDown = !upsideDown;
2993 TRACE("Reading from an offscreen target\n");
2994 upsideDown = !upsideDown;
2997 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3003 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3004 * flip the image nor scale it.
3006 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3007 * -> If the app wants a image width an unscaled width, copy it line per line
3008 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3009 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3010 * back buffer. This is slower than reading line per line, thus not used for flipping
3011 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3014 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3015 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3018 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3019 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3020 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3021 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3022 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3023 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3024 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3026 TRACE("Using hardware stretching to flip / stretch the texture\n");
3027 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3030 if(!(This->Flags & SFLAG_DONOTFREE)) {
3031 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3032 This->resource.allocatedMemory = NULL;
3033 This->resource.heapMemory = NULL;
3035 This->Flags &= ~SFLAG_INSYSMEM;
3037 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3038 * path is never entered
3040 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3044 /* Blit from offscreen surface to render target */
3045 float glTexCoord[4];
3046 DWORD oldCKeyFlags = Src->CKeyFlags;
3047 WINEDDCOLORKEY oldBltCKey = This->SrcBltCKey;
3048 RECT SourceRectangle;
3050 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3053 SourceRectangle.left = SrcRect->left;
3054 SourceRectangle.right = SrcRect->right;
3055 SourceRectangle.top = SrcRect->top;
3056 SourceRectangle.bottom = SrcRect->bottom;
3058 SourceRectangle.left = 0;
3059 SourceRectangle.right = Src->currentDesc.Width;
3060 SourceRectangle.top = 0;
3061 SourceRectangle.bottom = Src->currentDesc.Height;
3063 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3064 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3065 TRACE("Using stretch_rect_fbo\n");
3066 /* The source is always a texture, but never the currently active render target, and the texture
3067 * contents are never upside down
3069 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3070 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3074 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3075 /* Fall back to software */
3076 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3077 SourceRectangle.left, SourceRectangle.top,
3078 SourceRectangle.right, SourceRectangle.bottom);
3079 return WINED3DERR_INVALIDCALL;
3082 /* Color keying: Check if we have to do a color keyed blt,
3083 * and if not check if a color key is activated.
3085 * Just modify the color keying parameters in the surface and restore them afterwards
3086 * The surface keeps track of the color key last used to load the opengl surface.
3087 * PreLoad will catch the change to the flags and color key and reload if necessary.
3089 if(Flags & WINEDDBLT_KEYSRC) {
3090 /* Use color key from surface */
3091 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3092 /* Use color key from DDBltFx */
3093 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3094 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3096 /* Do not use color key */
3097 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3100 /* Now load the surface */
3101 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3104 /* Activate the destination context, set it up for blitting */
3105 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3108 glEnable(Src->glDescription.target);
3109 checkGLcall("glEnable(Src->glDescription.target)");
3112 TRACE("Drawing to offscreen buffer\n");
3113 glDrawBuffer(myDevice->offscreenBuffer);
3114 checkGLcall("glDrawBuffer");
3116 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
3117 TRACE("Drawing to %#x buffer\n", buffer);
3118 glDrawBuffer(buffer);
3119 checkGLcall("glDrawBuffer");
3122 /* Bind the texture */
3123 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3124 checkGLcall("glBindTexture");
3126 /* Filtering for StretchRect */
3127 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3128 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
3129 checkGLcall("glTexParameteri");
3130 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3131 minMipLookup[Filter][WINED3DTEXF_NONE]);
3132 checkGLcall("glTexParameteri");
3133 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3134 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3135 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3136 checkGLcall("glTexEnvi");
3138 /* This is for color keying */
3139 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3140 glEnable(GL_ALPHA_TEST);
3141 checkGLcall("glEnable GL_ALPHA_TEST");
3142 glAlphaFunc(GL_NOTEQUAL, 0.0);
3143 checkGLcall("glAlphaFunc\n");
3145 glDisable(GL_ALPHA_TEST);
3146 checkGLcall("glDisable GL_ALPHA_TEST");
3149 /* Draw a textured quad
3153 glColor3d(1.0f, 1.0f, 1.0f);
3154 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3159 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3160 glVertex3f(rect.x1, rect.y2, 0.0);
3162 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3167 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3172 checkGLcall("glEnd");
3174 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3175 glDisable(GL_ALPHA_TEST);
3176 checkGLcall("glDisable(GL_ALPHA_TEST)");
3179 /* Flush in case the drawable is used by multiple GL contexts */
3180 if(dstSwapchain && (dstSwapchain->num_contexts >= 2))
3183 glBindTexture(Src->glDescription.target, 0);
3184 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3185 /* Leave the opengl state valid for blitting */
3186 glDisable(Src->glDescription.target);
3187 checkGLcall("glDisable(Src->glDescription.target)");
3189 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3190 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3192 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3193 glDrawBuffer(GL_BACK);
3194 checkGLcall("glDrawBuffer");
3196 /* Restore the color key parameters */
3197 Src->CKeyFlags = oldCKeyFlags;
3198 This->SrcBltCKey = oldBltCKey;
3202 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3203 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3206 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3207 /* TODO: This should be moved to ModifyLocation() */
3208 if(!(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO)) {
3209 This->Flags |= SFLAG_INTEXTURE;
3214 /* Source-Less Blit to render target */
3215 if (Flags & WINEDDBLT_COLORFILL) {
3216 /* This is easy to handle for the D3D Device... */
3219 TRACE("Colorfill\n");
3221 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3222 must be true if we are here */
3223 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3224 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3225 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3226 TRACE("Surface is higher back buffer, falling back to software\n");
3227 return WINED3DERR_INVALIDCALL;
3230 /* The color as given in the Blt function is in the format of the frame-buffer...
3231 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3233 if (This->resource.format == WINED3DFMT_P8) {
3234 if (This->palette) {
3235 color = ((0xFF000000) |
3236 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3237 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3238 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3243 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3244 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3247 color = ((0xFF000000) |
3248 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3249 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3250 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3253 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3254 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3255 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3257 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3258 color = DDBltFx->u5.dwFillColor;
3261 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3262 return WINED3DERR_INVALIDCALL;
3265 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3266 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3267 1, /* Number of rectangles */
3268 &rect, WINED3DCLEAR_TARGET, color,
3275 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3276 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3277 return WINED3DERR_INVALIDCALL;
3280 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3282 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3285 if (Flags & WINEDDBLT_DEPTHFILL) {
3286 switch(This->resource.format) {
3287 case WINED3DFMT_D16:
3288 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3290 case WINED3DFMT_D15S1:
3291 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3293 case WINED3DFMT_D24S8:
3294 case WINED3DFMT_D24X8:
3295 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3297 case WINED3DFMT_D32:
3298 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3302 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3305 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3306 DestRect == NULL ? 0 : 1,
3307 (WINED3DRECT *) DestRect,
3308 WINED3DCLEAR_ZBUFFER,
3314 FIXME("(%p): Unsupp depthstencil blit\n", This);
3315 return WINED3DERR_INVALIDCALL;
3318 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3319 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3320 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3321 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3322 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3323 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3325 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3326 * except depth blits, which seem to work
3328 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3329 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3330 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3331 return WINED3DERR_INVALIDCALL;
3332 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3333 TRACE("Z Blit override handled the blit\n");
3338 /* Special cases for RenderTargets */
3339 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3340 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3341 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3344 /* For the rest call the X11 surface implementation.
3345 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3346 * other Blts are rather rare
3348 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3351 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3352 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3353 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3354 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3355 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3357 if(myDevice->inScene &&
3358 (iface == myDevice->stencilBufferTarget ||
3359 (Source && Source == myDevice->stencilBufferTarget))) {
3360 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3361 return WINED3DERR_INVALIDCALL;
3364 /* Special cases for RenderTargets */
3365 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3366 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3368 RECT SrcRect, DstRect;
3372 SrcRect.left = rsrc->left;
3373 SrcRect.top= rsrc->top;
3374 SrcRect.bottom = rsrc->bottom;
3375 SrcRect.right = rsrc->right;
3379 SrcRect.right = srcImpl->currentDesc.Width;
3380 SrcRect.bottom = srcImpl->currentDesc.Height;
3383 DstRect.left = dstx;
3385 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3386 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3388 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3389 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3390 Flags |= WINEDDBLT_KEYSRC;
3391 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3392 Flags |= WINEDDBLT_KEYDEST;
3393 if(trans & WINEDDBLTFAST_WAIT)
3394 Flags |= WINEDDBLT_WAIT;
3395 if(trans & WINEDDBLTFAST_DONOTWAIT)
3396 Flags |= WINEDDBLT_DONOTWAIT;
3398 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3402 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3405 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3406 /** Check against the maximum texture sizes supported by the video card **/
3407 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3408 unsigned int pow2Width, pow2Height;
3409 const GlPixelFormatDesc *glDesc;
3411 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3412 /* Setup some glformat defaults */
3413 This->glDescription.glFormat = glDesc->glFormat;
3414 This->glDescription.glFormatInternal = glDesc->glInternal;
3415 This->glDescription.glType = glDesc->glType;
3417 This->glDescription.textureName = 0;
3418 This->glDescription.target = GL_TEXTURE_2D;
3420 /* Non-power2 support */
3421 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO)) {
3422 pow2Width = This->currentDesc.Width;
3423 pow2Height = This->currentDesc.Height;
3425 /* Find the nearest pow2 match */
3426 pow2Width = pow2Height = 1;
3427 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3428 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3430 This->pow2Width = pow2Width;
3431 This->pow2Height = pow2Height;
3433 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3434 WINED3DFORMAT Format = This->resource.format;
3435 /** TODO: add support for non power two compressed textures **/
3436 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3437 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5) {
3438 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3439 This, This->currentDesc.Width, This->currentDesc.Height);
3440 return WINED3DERR_NOTAVAILABLE;
3444 if(pow2Width != This->currentDesc.Width ||
3445 pow2Height != This->currentDesc.Height) {
3446 This->Flags |= SFLAG_NONPOW2;
3449 TRACE("%p\n", This);
3450 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3451 /* one of three options
3452 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)
3453 2: Set the texture to the maximum size (bad idea)
3454 3: WARN and return WINED3DERR_NOTAVAILABLE;
3455 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.
3457 WARN("(%p) Creating an oversized surface\n", This);
3458 This->Flags |= SFLAG_OVERSIZE;
3460 /* This will be initialized on the first blt */
3461 This->glRect.left = 0;
3462 This->glRect.top = 0;
3463 This->glRect.right = 0;
3464 This->glRect.bottom = 0;
3466 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
3467 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
3468 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
3469 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
3471 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE) &&
3472 !((This->resource.format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE) && (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
3474 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3475 This->pow2Width = This->currentDesc.Width;
3476 This->pow2Height = This->currentDesc.Height;
3477 This->Flags &= ~SFLAG_NONPOW2;
3480 /* No oversize, gl rect is the full texture size */
3481 This->Flags &= ~SFLAG_OVERSIZE;
3482 This->glRect.left = 0;
3483 This->glRect.top = 0;
3484 This->glRect.right = This->pow2Width;
3485 This->glRect.bottom = This->pow2Height;
3488 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3489 switch(wined3d_settings.offscreen_rendering_mode) {
3490 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3491 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3492 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3496 This->Flags |= SFLAG_INSYSMEM;
3501 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
3502 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3503 IWineD3DBaseTexture *texture;
3505 TRACE("(%p)->(%s, %s)\n", iface,
3506 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3507 persistent ? "TRUE" : "FALSE");
3509 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3510 IWineD3DSwapChain *swapchain = NULL;
3512 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3513 TRACE("Surface %p is an onscreen surface\n", iface);
3515 IWineD3DSwapChain_Release(swapchain);
3517 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
3518 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3523 if((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) {
3524 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3525 TRACE("Passing to container\n");
3526 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3527 IWineD3DBaseTexture_Release(texture);
3530 This->Flags &= ~SFLAG_LOCATIONS;
3531 This->Flags |= flag;
3533 if((This->Flags & SFLAG_INTEXTURE) && (flag & SFLAG_INTEXTURE)) {
3534 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3535 TRACE("Passing to container\n");
3536 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3537 IWineD3DBaseTexture_Release(texture);
3540 This->Flags &= ~flag;
3548 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
3549 struct coords coords[4];
3551 IWineD3DSwapChain *swapchain = NULL;
3552 IWineD3DBaseTexture *texture = NULL;
3554 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3561 rect.right = This->currentDesc.Width;
3562 rect.bottom = This->currentDesc.Height;
3565 ActivateContext(device, device->render_targets[0], CTXUSAGE_BLIT);
3568 if(This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
3569 glEnable(GL_TEXTURE_RECTANGLE_ARB);
3570 checkGLcall("glEnable(GL_TEXTURE_RECTANGLE_ARB)");
3571 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName);
3572 checkGLcall("GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName)");
3573 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3574 checkGLcall("glTexParameteri");
3575 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3576 checkGLcall("glTexParameteri");
3578 coords[0].x = rect.left;
3581 coords[1].x = rect.left;
3584 coords[2].x = rect.right;
3587 coords[3].x = rect.right;
3590 coords[0].y = rect.top;
3591 coords[1].y = rect.bottom;
3592 coords[2].y = rect.bottom;
3593 coords[3].y = rect.top;
3594 } else if(This->glDescription.target == GL_TEXTURE_2D) {
3595 glEnable(GL_TEXTURE_2D);
3596 checkGLcall("glEnable(GL_TEXTURE_2D)");
3597 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
3598 checkGLcall("GL_TEXTURE_2D, This->glDescription.textureName)");
3599 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3600 checkGLcall("glTexParameteri");
3601 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3602 checkGLcall("glTexParameteri");
3604 coords[0].x = (float)rect.left / This->pow2Width;
3607 coords[1].x = (float)rect.left / This->pow2Width;
3610 coords[2].x = (float)rect.right / This->pow2Width;
3613 coords[3].x = (float)rect.right / This->pow2Width;
3616 coords[0].y = (float)rect.top / This->pow2Height;
3617 coords[1].y = (float)rect.bottom / This->pow2Height;
3618 coords[2].y = (float)rect.bottom / This->pow2Height;
3619 coords[3].y = (float)rect.top / This->pow2Height;
3621 /* Must be a cube map */
3622 glEnable(GL_TEXTURE_CUBE_MAP_ARB);
3623 checkGLcall("glEnable(GL_TEXTURE_CUBE_MAP_ARB)");
3624 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName);
3625 checkGLcall("GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName)");
3626 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3627 checkGLcall("glTexParameteri");
3628 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3629 checkGLcall("glTexParameteri");
3631 switch(This->glDescription.target) {
3632 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
3633 coords[0].x = 1; coords[0].y = -1; coords[0].z = 1;
3634 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3635 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3636 coords[3].x = 1; coords[3].y = -1; coords[3].z = -1;
3639 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
3640 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3641 coords[1].x = -1; coords[1].y = 1; coords[1].z = 1;
3642 coords[2].x = -1; coords[2].y = 1; coords[2].z = -1;
3643 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3646 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
3647 coords[0].x = -1; coords[0].y = 1; coords[0].z = 1;
3648 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3649 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3650 coords[3].x = -1; coords[3].y = 1; coords[3].z = -1;
3653 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
3654 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3655 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3656 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3657 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3660 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
3661 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3662 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3663 coords[2].x = 1; coords[2].y = -1; coords[2].z = 1;
3664 coords[3].x = -1; coords[3].y = -1; coords[3].z = 1;
3667 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
3668 coords[0].x = -1; coords[0].y = -1; coords[0].z = -1;
3669 coords[1].x = 1; coords[1].y = -1; coords[1].z = -1;
3670 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3671 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3674 ERR("Unexpected texture target\n");
3681 glTexCoord3fv(&coords[0].x);
3682 glVertex2i(rect.left, device->render_offscreen ? rect.bottom : rect.top);
3684 glTexCoord3fv(&coords[1].x);
3685 glVertex2i(rect.left, device->render_offscreen ? rect.top : rect.bottom);
3687 glTexCoord3fv(&coords[2].x);
3688 glVertex2i(rect.right, device->render_offscreen ? rect.top : rect.bottom);
3690 glTexCoord3fv(&coords[3].x);
3691 glVertex2i(rect.right, device->render_offscreen ? rect.bottom : rect.top);
3693 checkGLcall("glEnd");
3695 if(This->glDescription.target != GL_TEXTURE_2D) {
3696 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3697 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3699 glDisable(GL_TEXTURE_2D);
3700 checkGLcall("glDisable(GL_TEXTURE_2D)");
3703 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain);
3704 if(hr == WINED3D_OK && swapchain) {
3705 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
3706 if(((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
3709 IWineD3DSwapChain_Release(swapchain);
3711 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
3712 * reset properly next draw
3714 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture);
3715 if(hr == WINED3D_OK && texture) {
3716 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
3717 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
3718 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
3719 IWineD3DBaseTexture_Release(texture);
3725 /*****************************************************************************
3726 * IWineD3DSurface::LoadLocation
3728 * Copies the current surface data from wherever it is to the requested
3729 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
3730 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
3731 * multiple locations, the gl texture is preferred over the drawable, which is
3732 * preferred over system memory. The PBO counts as system memory. If rect is
3733 * not NULL, only the specified rectangle is copied (only supported for
3734 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
3735 * location is marked up to date after the copy.
3738 * flag: Surface location flag to be updated
3739 * rect: rectangle to be copied
3742 * WINED3D_OK on success
3743 * WINED3DERR_DEVICELOST on an internal error
3745 *****************************************************************************/
3746 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
3747 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3748 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3749 IWineD3DSwapChain *swapchain = NULL;
3750 GLenum format, internal, type;
3751 CONVERT_TYPES convert;
3753 int width, pitch, outpitch;
3756 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3757 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3758 TRACE("Surface %p is an onscreen surface\n", iface);
3760 IWineD3DSwapChain_Release(swapchain);
3762 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
3763 * Prefer SFLAG_INTEXTURE. */
3764 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
3768 TRACE("(%p)->(%s, %p)\n", iface,
3769 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3772 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
3775 if(This->Flags & flag) {
3776 TRACE("Location already up to date\n");
3780 if(!(This->Flags & SFLAG_LOCATIONS)) {
3781 ERR("Surface does not have any up to date location\n");
3782 This->Flags |= SFLAG_LOST;
3783 return WINED3DERR_DEVICELOST;
3786 if(flag == SFLAG_INSYSMEM) {
3787 surface_prepare_system_memory(This);
3789 /* Download the surface to system memory */
3790 if(This->Flags & SFLAG_INTEXTURE) {
3791 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
3792 surface_bind_and_dirtify(This);
3794 surface_download_data(This);
3796 read_from_framebuffer(This, rect,
3797 This->resource.allocatedMemory,
3798 IWineD3DSurface_GetPitch(iface));
3800 } else if(flag == SFLAG_INDRAWABLE) {
3801 if(This->Flags & SFLAG_INTEXTURE) {
3802 surface_blt_to_drawable(This, rect);
3804 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
3806 /* The width is in 'length' not in bytes */
3807 width = This->currentDesc.Width;
3808 pitch = IWineD3DSurface_GetPitch(iface);
3810 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
3811 int height = This->currentDesc.Height;
3813 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
3814 outpitch = width * bpp;
3815 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
3817 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
3819 ERR("Out of memory %d, %d!\n", outpitch, height);
3820 return WINED3DERR_OUTOFVIDEOMEMORY;
3822 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
3824 This->Flags |= SFLAG_CONVERTED;
3826 This->Flags &= ~SFLAG_CONVERTED;
3827 mem = This->resource.allocatedMemory;
3830 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
3832 /* Don't delete PBO memory */
3833 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
3834 HeapFree(GetProcessHeap(), 0, mem);
3836 } else /* if(flag == SFLAG_INTEXTURE) */ {
3837 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
3839 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
3840 surface_bind_and_dirtify(This);
3842 if (This->Flags & SFLAG_INDRAWABLE) {
3846 glGetIntegerv(GL_READ_BUFFER, &prevRead);
3847 vcheckGLcall("glGetIntegerv");
3848 glReadBuffer(This->resource.wineD3DDevice->offscreenBuffer);
3849 vcheckGLcall("glReadBuffer");
3851 if(!(This->Flags & SFLAG_ALLOCATED)) {
3852 surface_allocate_surface(This, internal, This->pow2Width,
3853 This->pow2Height, format, type);
3856 clear_unused_channels(This);
3858 glCopyTexSubImage2D(This->glDescription.target,
3859 This->glDescription.level,
3861 This->currentDesc.Width,
3862 This->currentDesc.Height);
3863 checkGLcall("glCopyTexSubImage2D");
3865 glReadBuffer(prevRead);
3866 vcheckGLcall("glReadBuffer");
3870 TRACE("Updated target %d\n", This->glDescription.target);
3872 /* The only place where LoadTexture() might get called when isInDraw=1
3873 * is ActivateContext where lastActiveRenderTarget is preloaded.
3875 if(iface == device->lastActiveRenderTarget && device->isInDraw)
3876 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
3878 /* Otherwise: System memory copy must be most up to date */
3880 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
3881 This->Flags |= SFLAG_GLCKEY;
3882 This->glCKey = This->SrcBltCKey;
3884 else This->Flags &= ~SFLAG_GLCKEY;
3886 /* The width is in 'length' not in bytes */
3887 width = This->currentDesc.Width;
3888 pitch = IWineD3DSurface_GetPitch(iface);
3890 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
3891 int height = This->currentDesc.Height;
3893 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
3894 outpitch = width * bpp;
3895 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
3897 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
3899 ERR("Out of memory %d, %d!\n", outpitch, height);
3900 return WINED3DERR_OUTOFVIDEOMEMORY;
3902 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
3904 This->Flags |= SFLAG_CONVERTED;
3905 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
3906 d3dfmt_p8_upload_palette(iface, convert);
3907 This->Flags &= ~SFLAG_CONVERTED;
3908 mem = This->resource.allocatedMemory;
3910 This->Flags &= ~SFLAG_CONVERTED;
3911 mem = This->resource.allocatedMemory;
3914 /* Make sure the correct pitch is used */
3915 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
3917 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
3918 TRACE("non power of two support\n");
3919 if(!(This->Flags & SFLAG_ALLOCATED)) {
3920 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
3922 if (mem || (This->Flags & SFLAG_PBO)) {
3923 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
3926 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
3927 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
3929 if(!(This->Flags & SFLAG_ALLOCATED)) {
3930 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
3932 if (mem || (This->Flags & SFLAG_PBO)) {
3933 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
3937 /* Restore the default pitch */
3938 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
3940 /* Don't delete PBO memory */
3941 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
3942 HeapFree(GetProcessHeap(), 0, mem);
3947 This->Flags |= flag;
3950 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !swapchain
3951 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
3952 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
3953 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3959 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
3960 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3961 IWineD3DSwapChain *swapchain = NULL;
3963 /* Update the drawable size method */
3965 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
3968 This->get_drawable_size = get_drawable_size_swapchain;
3969 IWineD3DSwapChain_Release(swapchain);
3970 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3971 switch(wined3d_settings.offscreen_rendering_mode) {
3972 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3973 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3974 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3978 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
3981 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
3984 IWineD3DBaseSurfaceImpl_QueryInterface,
3985 IWineD3DBaseSurfaceImpl_AddRef,
3986 IWineD3DSurfaceImpl_Release,
3987 /* IWineD3DResource */
3988 IWineD3DBaseSurfaceImpl_GetParent,
3989 IWineD3DBaseSurfaceImpl_GetDevice,
3990 IWineD3DBaseSurfaceImpl_SetPrivateData,
3991 IWineD3DBaseSurfaceImpl_GetPrivateData,
3992 IWineD3DBaseSurfaceImpl_FreePrivateData,
3993 IWineD3DBaseSurfaceImpl_SetPriority,
3994 IWineD3DBaseSurfaceImpl_GetPriority,
3995 IWineD3DSurfaceImpl_PreLoad,
3996 IWineD3DSurfaceImpl_UnLoad,
3997 IWineD3DBaseSurfaceImpl_GetType,
3998 /* IWineD3DSurface */
3999 IWineD3DBaseSurfaceImpl_GetContainer,
4000 IWineD3DBaseSurfaceImpl_GetDesc,
4001 IWineD3DSurfaceImpl_LockRect,
4002 IWineD3DSurfaceImpl_UnlockRect,
4003 IWineD3DSurfaceImpl_GetDC,
4004 IWineD3DSurfaceImpl_ReleaseDC,
4005 IWineD3DSurfaceImpl_Flip,
4006 IWineD3DSurfaceImpl_Blt,
4007 IWineD3DBaseSurfaceImpl_GetBltStatus,
4008 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4009 IWineD3DBaseSurfaceImpl_IsLost,
4010 IWineD3DBaseSurfaceImpl_Restore,
4011 IWineD3DSurfaceImpl_BltFast,
4012 IWineD3DBaseSurfaceImpl_GetPalette,
4013 IWineD3DBaseSurfaceImpl_SetPalette,
4014 IWineD3DBaseSurfaceImpl_RealizePalette,
4015 IWineD3DBaseSurfaceImpl_SetColorKey,
4016 IWineD3DBaseSurfaceImpl_GetPitch,
4017 IWineD3DSurfaceImpl_SetMem,
4018 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4019 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4020 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4021 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4022 IWineD3DBaseSurfaceImpl_SetClipper,
4023 IWineD3DBaseSurfaceImpl_GetClipper,
4025 IWineD3DSurfaceImpl_AddDirtyRect,
4026 IWineD3DSurfaceImpl_LoadTexture,
4027 IWineD3DSurfaceImpl_BindTexture,
4028 IWineD3DSurfaceImpl_SaveSnapshot,
4029 IWineD3DSurfaceImpl_SetContainer,
4030 IWineD3DSurfaceImpl_SetGlTextureDesc,
4031 IWineD3DSurfaceImpl_GetGlDesc,
4032 IWineD3DSurfaceImpl_GetData,
4033 IWineD3DSurfaceImpl_SetFormat,
4034 IWineD3DSurfaceImpl_PrivateSetup,
4035 IWineD3DSurfaceImpl_ModifyLocation,
4036 IWineD3DSurfaceImpl_LoadLocation