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 = (IWineD3DDeviceImpl *) 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 = ((BYTE *) 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 = (BYTE *) ((BYTE *) 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 BOOL storechanged = FALSE;
997 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
998 IWineD3DDeviceImpl *myDevice = (IWineD3DDeviceImpl *) This->resource.wineD3DDevice;
999 IWineD3DSwapChainImpl *swapchain;
1001 /* Activate the correct context for the render target */
1002 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1005 IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain);
1007 /* Primary offscreen render target */
1008 TRACE("Offscreen render target\n");
1009 glDrawBuffer(myDevice->offscreenBuffer);
1010 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1012 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1013 TRACE("Unlocking %#x buffer\n", buffer);
1014 glDrawBuffer(buffer);
1015 checkGLcall("glDrawBuffer");
1017 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1021 vcheckGLcall("glFlush");
1022 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1023 vcheckGLcall("glIntegerv");
1024 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1025 vcheckGLcall("glIntegerv");
1026 glPixelZoom(1.0, -1.0);
1027 vcheckGLcall("glPixelZoom");
1029 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1030 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1031 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1033 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1034 vcheckGLcall("glRasterPos2f");
1036 /* Some drivers(radeon dri, others?) don't like exceptions during
1037 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1038 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1039 * catch to put the dib section in InSync mode, which leads to a crash
1040 * and a blocked x server on my radeon card.
1042 * The following lines read the dib section so it is put in inSync mode
1043 * before glDrawPixels is called and the crash is prevented. There won't
1044 * be any interfering gdi accesses, because UnlockRect is called from
1045 * ReleaseDC, and the app won't use the dc any more afterwards.
1047 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1049 read = This->resource.allocatedMemory[0];
1052 if(This->Flags & SFLAG_PBO) {
1053 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1054 checkGLcall("glBindBufferARB");
1057 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1058 (This->lockedRect.bottom - This->lockedRect.top)-1,
1060 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1061 checkGLcall("glDrawPixels");
1063 if(This->Flags & SFLAG_PBO) {
1064 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1065 checkGLcall("glBindBufferARB");
1068 glPixelZoom(1.0,1.0);
1069 vcheckGLcall("glPixelZoom");
1071 glRasterPos3iv(&prev_rasterpos[0]);
1072 vcheckGLcall("glRasterPos3iv");
1074 /* Reset to previous pack row length */
1075 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1076 vcheckGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1078 glPixelStorei(GL_PACK_SWAP_BYTES, prev_store);
1079 vcheckGLcall("glPixelStorei GL_PACK_SWAP_BYTES");
1083 glDrawBuffer(myDevice->offscreenBuffer);
1084 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1085 } else if(swapchain->backBuffer) {
1086 glDrawBuffer(GL_BACK);
1087 checkGLcall("glDrawBuffer(GL_BACK)");
1089 glDrawBuffer(GL_FRONT);
1090 checkGLcall("glDrawBuffer(GL_FRONT)");
1097 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1098 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1099 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1100 IWineD3DSwapChainImpl *swapchain = NULL;
1103 if (!(This->Flags & SFLAG_LOCKED)) {
1104 WARN("trying to Unlock an unlocked surf@%p\n", This);
1105 return WINED3DERR_INVALIDCALL;
1108 if (This->Flags & SFLAG_PBO) {
1109 TRACE("Freeing PBO memory\n");
1110 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1112 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1113 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1114 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1115 checkGLcall("glUnmapBufferARB");
1117 This->resource.allocatedMemory = NULL;
1120 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1122 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1123 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1127 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain);
1128 if(swapchain || (myDevice->render_targets && iface == myDevice->render_targets[0])) {
1129 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1130 static BOOL warned = FALSE;
1132 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1135 if(swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1139 if(This->dirtyRect.left == 0 &&
1140 This->dirtyRect.top == 0 &&
1141 This->dirtyRect.right == This->currentDesc.Width &&
1142 This->dirtyRect.bottom == This->currentDesc.Height) {
1145 /* TODO: Proper partial rectangle tracking */
1146 fullsurface = FALSE;
1147 This->Flags |= SFLAG_INSYSMEM;
1150 switch(wined3d_settings.rendertargetlock_mode) {
1153 ActivateContext(myDevice, iface, CTXUSAGE_BLIT);
1155 if (This->glDescription.textureName == 0) {
1156 glGenTextures(1, &This->glDescription.textureName);
1157 checkGLcall("glGenTextures");
1159 glBindTexture(This->glDescription.target, This->glDescription.textureName);
1160 checkGLcall("glBindTexture(This->glDescription.target, This->glDescription.textureName)");
1162 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1168 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1173 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1174 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1175 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1176 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1177 * not fully up to date because only a subrectangle was read in LockRect.
1179 This->Flags &= ~SFLAG_INSYSMEM;
1180 This->Flags |= SFLAG_INDRAWABLE;
1183 This->dirtyRect.left = This->currentDesc.Width;
1184 This->dirtyRect.top = This->currentDesc.Height;
1185 This->dirtyRect.right = 0;
1186 This->dirtyRect.bottom = 0;
1187 } else if(iface == myDevice->stencilBufferTarget) {
1188 FIXME("Depth Stencil buffer locking is not implemented\n");
1190 /* The rest should be a normal texture */
1191 IWineD3DBaseTextureImpl *impl;
1192 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1193 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1194 * states need resetting
1196 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1197 if(impl->baseTexture.bindCount) {
1198 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1200 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1205 This->Flags &= ~SFLAG_LOCKED;
1206 memset(&This->lockedRect, 0, sizeof(RECT));
1210 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
1211 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1212 WINED3DLOCKED_RECT lock;
1216 TRACE("(%p)->(%p)\n",This,pHDC);
1218 if(This->Flags & SFLAG_USERPTR) {
1219 ERR("Not supported on surfaces with an application-provided surfaces\n");
1220 return WINEDDERR_NODC;
1223 /* Give more detailed info for ddraw */
1224 if (This->Flags & SFLAG_DCINUSE)
1225 return WINEDDERR_DCALREADYCREATED;
1227 /* Can't GetDC if the surface is locked */
1228 if (This->Flags & SFLAG_LOCKED)
1229 return WINED3DERR_INVALIDCALL;
1231 memset(&lock, 0, sizeof(lock)); /* To be sure */
1233 /* Create a DIB section if there isn't a hdc yet */
1235 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1236 if(This->Flags & SFLAG_CLIENT) {
1237 IWineD3DSurface_PreLoad(iface);
1240 /* Use the dib section from now on if we are not using a PBO */
1241 if(!(This->Flags & SFLAG_PBO))
1242 This->resource.allocatedMemory = This->dib.bitmap_data;
1245 /* Lock the surface */
1246 hr = IWineD3DSurface_LockRect(iface,
1251 if(This->Flags & SFLAG_PBO) {
1252 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1253 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1257 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1258 /* keep the dib section */
1262 if(This->resource.format == WINED3DFMT_P8 ||
1263 This->resource.format == WINED3DFMT_A8P8) {
1266 PALETTEENTRY ent[256];
1268 GetPaletteEntries(This->palette->hpal, 0, 256, ent);
1269 for (n=0; n<256; n++) {
1270 col[n].rgbRed = ent[n].peRed;
1271 col[n].rgbGreen = ent[n].peGreen;
1272 col[n].rgbBlue = ent[n].peBlue;
1273 col[n].rgbReserved = 0;
1276 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1278 for (n=0; n<256; n++) {
1279 col[n].rgbRed = device->palettes[device->currentPalette][n].peRed;
1280 col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
1281 col[n].rgbBlue = device->palettes[device->currentPalette][n].peBlue;
1282 col[n].rgbReserved = 0;
1286 SetDIBColorTable(This->hDC, 0, 256, col);
1290 TRACE("returning %p\n",*pHDC);
1291 This->Flags |= SFLAG_DCINUSE;
1296 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
1297 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1299 TRACE("(%p)->(%p)\n",This,hDC);
1301 if (!(This->Flags & SFLAG_DCINUSE))
1302 return WINED3DERR_INVALIDCALL;
1304 if (This->hDC !=hDC) {
1305 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1306 return WINED3DERR_INVALIDCALL;
1309 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1310 /* Copy the contents of the DIB over to the PBO */
1311 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1314 /* we locked first, so unlock now */
1315 IWineD3DSurface_UnlockRect(iface);
1317 This->Flags &= ~SFLAG_DCINUSE;
1322 /* ******************************************************
1323 IWineD3DSurface Internal (No mapping to directx api) parts follow
1324 ****************************************************** */
1326 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) {
1327 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1328 const GlPixelFormatDesc *glDesc;
1329 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1330 BOOL p8_render_target = FALSE;
1331 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
1333 /* Default values: From the surface */
1334 *format = glDesc->glFormat;
1335 *type = glDesc->glType;
1336 *convert = NO_CONVERSION;
1337 *target_bpp = This->bytesPerPixel;
1340 *internal = glDesc->glGammaInternal;
1341 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
1342 *internal = glDesc->rtInternal;
1344 *internal = glDesc->glInternal;
1347 /* Ok, now look if we have to do any conversion */
1348 switch(This->resource.format) {
1354 if (device->render_targets && device->render_targets[0]) {
1355 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
1356 if((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET) && (render_target->resource.format == WINED3DFMT_P8))
1357 p8_render_target = TRUE;
1360 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1361 * of the two is available make sure texturing is requested as neither of the two works in
1362 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1363 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1364 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1365 * conflicts with this.
1367 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) || (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) && p8_render_target)) || colorkey_active || !use_texturing ) {
1369 *internal = GL_RGBA;
1370 *type = GL_UNSIGNED_BYTE;
1372 if(colorkey_active) {
1373 *convert = CONVERT_PALETTED_CK;
1375 *convert = CONVERT_PALETTED;
1378 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1380 *internal = GL_RGBA;
1381 *type = GL_UNSIGNED_BYTE;
1387 case WINED3DFMT_R3G3B2:
1388 /* **********************
1389 GL_UNSIGNED_BYTE_3_3_2
1390 ********************** */
1391 if (colorkey_active) {
1392 /* This texture format will never be used.. So do not care about color keying
1393 up until the point in time it will be needed :-) */
1394 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1398 case WINED3DFMT_R5G6B5:
1399 if (colorkey_active) {
1400 *convert = CONVERT_CK_565;
1402 *internal = GL_RGBA;
1403 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1407 case WINED3DFMT_X1R5G5B5:
1408 if (colorkey_active) {
1409 *convert = CONVERT_CK_5551;
1411 *internal = GL_RGBA;
1412 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1416 case WINED3DFMT_R8G8B8:
1417 if (colorkey_active) {
1418 *convert = CONVERT_CK_RGB24;
1420 *internal = GL_RGBA;
1421 *type = GL_UNSIGNED_INT_8_8_8_8;
1426 case WINED3DFMT_X8R8G8B8:
1427 if (colorkey_active) {
1428 *convert = CONVERT_RGB32_888;
1430 *internal = GL_RGBA;
1431 *type = GL_UNSIGNED_INT_8_8_8_8;
1435 case WINED3DFMT_V8U8:
1436 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1437 else if(GL_SUPPORT(ATI_ENVMAP_BUMPMAP)) {
1438 *format = GL_DUDV_ATI;
1439 *internal = GL_DU8DV8_ATI;
1441 /* No conversion - Just change the gl type */
1444 *convert = CONVERT_V8U8;
1446 *internal = GL_RGB8;
1447 *type = GL_UNSIGNED_BYTE;
1451 case WINED3DFMT_L6V5U5:
1452 *convert = CONVERT_L6V5U5;
1453 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1455 /* Use format and types from table */
1457 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1460 *internal = GL_RGB5;
1461 *type = GL_UNSIGNED_SHORT_5_6_5;
1465 case WINED3DFMT_X8L8V8U8:
1466 *convert = CONVERT_X8L8V8U8;
1468 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1469 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1470 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1471 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1472 * the needed type and format parameter, so the internal format contains a
1473 * 4th component, which is returned as alpha
1476 /* Not supported by GL_ATI_envmap_bumpmap */
1478 *internal = GL_RGB8;
1479 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1483 case WINED3DFMT_Q8W8V8U8:
1484 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1485 *convert = CONVERT_Q8W8V8U8;
1487 *internal = GL_RGBA8;
1488 *type = GL_UNSIGNED_BYTE;
1490 /* Not supported by GL_ATI_envmap_bumpmap */
1493 case WINED3DFMT_V16U16:
1494 if(GL_SUPPORT(NV_TEXTURE_SHADER3)) break;
1495 *convert = CONVERT_V16U16;
1497 *internal = GL_RGB16_EXT;
1498 *type = GL_UNSIGNED_SHORT;
1500 /* What should I do here about GL_ATI_envmap_bumpmap?
1501 * Convert it or allow data loss by loading it into a 8 bit / channel texture?
1505 case WINED3DFMT_A4L4:
1506 /* A4L4 exists as an internal gl format, but for some reason there is not
1507 * format+type combination to load it. Thus convert it to A8L8, then load it
1508 * with A4L4 internal, but A8L8 format+type
1510 *convert = CONVERT_A4L4;
1511 *format = GL_LUMINANCE_ALPHA;
1512 *internal = GL_LUMINANCE4_ALPHA4;
1513 *type = GL_UNSIGNED_BYTE;
1517 case WINED3DFMT_R32F:
1518 /* Can be loaded in theory with fmt=GL_RED, type=GL_FLOAT, but this fails. The reason
1519 * is that D3D expects the undefined green, blue and alpha channels to return 1.0
1520 * when sampling, but OpenGL sets green and blue to 0.0 instead. Thus we have to inject
1523 * The alpha channel defaults to 1.0 in opengl, so nothing has to be done about it.
1525 *convert = CONVERT_R32F;
1527 *internal = GL_RGB32F_ARB;
1532 case WINED3DFMT_R16F:
1533 /* Similar to R32F */
1534 *convert = CONVERT_R16F;
1536 *internal = GL_RGB16F_ARB;
1537 *type = GL_HALF_FLOAT_ARB;
1541 case WINED3DFMT_G16R16:
1542 *convert = CONVERT_G16R16;
1544 *internal = GL_RGB16_EXT;
1545 *type = GL_UNSIGNED_SHORT;
1556 HRESULT d3dfmt_convert_surface(BYTE *src, BYTE *dst, UINT pitch, UINT width, UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This) {
1557 BYTE *source, *dest;
1558 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
1563 memcpy(dst, src, pitch * height);
1566 case CONVERT_PALETTED:
1567 case CONVERT_PALETTED_CK:
1569 IWineD3DPaletteImpl* pal = This->palette;
1574 /* TODO: If we are a sublevel, try to get the palette from level 0 */
1577 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1579 for (y = 0; y < height; y++)
1581 source = src + pitch * y;
1582 dest = dst + outpitch * y;
1583 /* This is an 1 bpp format, using the width here is fine */
1584 for (x = 0; x < width; x++) {
1585 BYTE color = *source++;
1586 *dest++ = table[color][0];
1587 *dest++ = table[color][1];
1588 *dest++ = table[color][2];
1589 *dest++ = table[color][3];
1595 case CONVERT_CK_565:
1597 /* Converting the 565 format in 5551 packed to emulate color-keying.
1599 Note : in all these conversion, it would be best to average the averaging
1600 pixels to get the color of the pixel that will be color-keyed to
1601 prevent 'color bleeding'. This will be done later on if ever it is
1604 Note2: Nvidia documents say that their driver does not support alpha + color keying
1605 on the same surface and disables color keying in such a case
1611 TRACE("Color keyed 565\n");
1613 for (y = 0; y < height; y++) {
1614 Source = (WORD *) (src + y * pitch);
1615 Dest = (WORD *) (dst + y * outpitch);
1616 for (x = 0; x < width; x++ ) {
1617 WORD color = *Source++;
1618 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
1619 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1620 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1629 case CONVERT_CK_5551:
1631 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
1635 TRACE("Color keyed 5551\n");
1636 for (y = 0; y < height; y++) {
1637 Source = (WORD *) (src + y * pitch);
1638 Dest = (WORD *) (dst + y * outpitch);
1639 for (x = 0; x < width; x++ ) {
1640 WORD color = *Source++;
1642 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
1643 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
1647 *Dest &= ~(1 << 15);
1659 unsigned char *Dest;
1660 for(y = 0; y < height; y++) {
1661 Source = (short *) (src + y * pitch);
1662 Dest = (unsigned char *) (dst + y * outpitch);
1663 for (x = 0; x < width; x++ ) {
1664 long color = (*Source++);
1665 /* B */ Dest[0] = 0xff;
1666 /* G */ Dest[1] = (color >> 8) + 128; /* V */
1667 /* R */ Dest[2] = (color) + 128; /* U */
1674 case CONVERT_V16U16:
1678 unsigned short *Dest;
1679 for(y = 0; y < height; y++) {
1680 Source = (DWORD *) (src + y * pitch);
1681 Dest = (unsigned short *) (dst + y * outpitch);
1682 for (x = 0; x < width; x++ ) {
1683 DWORD color = (*Source++);
1684 /* B */ Dest[0] = 0xffff;
1685 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
1686 /* R */ Dest[2] = (color ) + 32768; /* U */
1693 case CONVERT_Q8W8V8U8:
1697 unsigned char *Dest;
1698 for(y = 0; y < height; y++) {
1699 Source = (DWORD *) (src + y * pitch);
1700 Dest = (unsigned char *) (dst + y * outpitch);
1701 for (x = 0; x < width; x++ ) {
1702 long color = (*Source++);
1703 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
1704 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1705 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1706 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
1713 case CONVERT_L6V5U5:
1717 unsigned char *Dest;
1719 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1720 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
1721 * fixed function and shaders without further conversion once the surface is
1724 for(y = 0; y < height; y++) {
1725 Source = (WORD *) (src + y * pitch);
1726 Dest = (unsigned char *) (dst + y * outpitch);
1727 for (x = 0; x < width; x++ ) {
1728 short color = (*Source++);
1729 unsigned char l = ((color >> 10) & 0xfc);
1730 char v = ((color >> 5) & 0x3e);
1731 char u = ((color ) & 0x1f);
1733 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
1734 * and doubles the positive range. Thus shift left only once, gl does the 2nd
1735 * shift. GL reads a signed value and converts it into an unsigned value.
1737 /* M */ Dest[2] = l << 1;
1739 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
1740 * from 5 bit values to 8 bit values.
1742 /* V */ Dest[1] = v << 3;
1743 /* U */ Dest[0] = u << 3;
1748 for(y = 0; y < height; y++) {
1749 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
1750 Source = (WORD *) (src + y * pitch);
1751 for (x = 0; x < width; x++ ) {
1752 short color = (*Source++);
1753 unsigned char l = ((color >> 10) & 0xfc);
1754 short v = ((color >> 5) & 0x3e);
1755 short u = ((color ) & 0x1f);
1756 short v_conv = v + 16;
1757 short u_conv = u + 16;
1759 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
1767 case CONVERT_X8L8V8U8:
1771 unsigned char *Dest;
1773 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1774 /* This implementation works with the fixed function pipeline and shaders
1775 * without further modification after converting the surface.
1777 for(y = 0; y < height; y++) {
1778 Source = (DWORD *) (src + y * pitch);
1779 Dest = (unsigned char *) (dst + y * outpitch);
1780 for (x = 0; x < width; x++ ) {
1781 long color = (*Source++);
1782 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
1783 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
1784 /* U */ Dest[0] = (color & 0xff); /* U */
1785 /* I */ Dest[3] = 255; /* X */
1790 /* Doesn't work correctly with the fixed function pipeline, but can work in
1791 * shaders if the shader is adjusted. (There's no use for this format in gl's
1792 * standard fixed function pipeline anyway).
1794 for(y = 0; y < height; y++) {
1795 Source = (DWORD *) (src + y * pitch);
1796 Dest = (unsigned char *) (dst + y * outpitch);
1797 for (x = 0; x < width; x++ ) {
1798 long color = (*Source++);
1799 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
1800 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
1801 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
1812 unsigned char *Source;
1813 unsigned char *Dest;
1814 for(y = 0; y < height; y++) {
1815 Source = (unsigned char *) (src + y * pitch);
1816 Dest = (unsigned char *) (dst + y * outpitch);
1817 for (x = 0; x < width; x++ ) {
1818 unsigned char color = (*Source++);
1819 /* A */ Dest[1] = (color & 0xf0) << 0;
1820 /* L */ Dest[0] = (color & 0x0f) << 4;
1832 for(y = 0; y < height; y++) {
1833 Source = (float *) (src + y * pitch);
1834 Dest = (float *) (dst + y * outpitch);
1835 for (x = 0; x < width; x++ ) {
1836 float color = (*Source++);
1852 for(y = 0; y < height; y++) {
1853 Source = (WORD *) (src + y * pitch);
1854 Dest = (WORD *) (dst + y * outpitch);
1855 for (x = 0; x < width; x++ ) {
1856 WORD color = (*Source++);
1866 case CONVERT_G16R16:
1872 for(y = 0; y < height; y++) {
1873 Source = (WORD *) (src + y * pitch);
1874 Dest = (WORD *) (dst + y * outpitch);
1875 for (x = 0; x < width; x++ ) {
1876 WORD green = (*Source++);
1877 WORD red = (*Source++);
1888 ERR("Unsupported conversation type %d\n", convert);
1893 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey) {
1894 IWineD3DPaletteImpl* pal = This->palette;
1895 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1896 BOOL index_in_alpha = FALSE;
1899 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
1900 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
1901 * is slow. Further RGB->P8 conversion is not possible because palettes can have
1902 * duplicate entries. Store the color key in the unused alpha component to speed the
1903 * download up and to make conversion unneeded. */
1904 if (device->render_targets && device->render_targets[0]) {
1905 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
1907 if((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET) && (render_target->resource.format == WINED3DFMT_P8))
1908 index_in_alpha = TRUE;
1912 /* Still no palette? Use the device's palette */
1913 /* Get the surface's palette */
1914 for (i = 0; i < 256; i++) {
1915 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1916 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1917 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1919 if(index_in_alpha) {
1921 } else if (colorkey &&
1922 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1923 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1924 /* We should maybe here put a more 'neutral' color than the standard bright purple
1925 one often used by application to prevent the nice purple borders when bi-linear
1933 TRACE("Using surface palette %p\n", pal);
1934 /* Get the surface's palette */
1935 for (i = 0; i < 256; i++) {
1936 table[i][0] = pal->palents[i].peRed;
1937 table[i][1] = pal->palents[i].peGreen;
1938 table[i][2] = pal->palents[i].peBlue;
1940 if(index_in_alpha) {
1943 else if (colorkey &&
1944 (i >= This->SrcBltCKey.dwColorSpaceLowValue) &&
1945 (i <= This->SrcBltCKey.dwColorSpaceHighValue)) {
1946 /* We should maybe here put a more 'neutral' color than the standard bright purple
1947 one often used by application to prevent the nice purple borders when bi-linear
1950 } else if(pal->Flags & WINEDDPCAPS_ALPHA) {
1951 table[i][3] = pal->palents[i].peFlags;
1959 const char *fragment_palette_conversion =
1962 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n" /* { 255/256, 0.5/255*255/256, 0, 0 } */
1963 "TEX index.x, fragment.texcoord[0], texture[0], 2D;\n" /* store the red-component of the current pixel */
1964 "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 */
1965 "TEX result.color, index, texture[1], 1D;\n" /* use the red-component as a index in the palette to get the final color */
1968 /* This function is used in case of 8bit paletted textures to upload the palette.
1969 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
1970 extensions like ATI_fragment_shaders is possible.
1972 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
1973 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1975 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1977 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
1979 /* Try to use the paletted texture extension */
1980 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
1982 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
1983 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
1987 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
1988 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
1989 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
1991 /* Create the fragment program if we don't have it */
1992 if(!device->paletteConversionShader)
1994 glEnable(GL_FRAGMENT_PROGRAM_ARB);
1995 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
1996 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
1997 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), (const GLbyte *)fragment_palette_conversion));
1998 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2001 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2002 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2004 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2005 glEnable(GL_TEXTURE_1D);
2006 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2008 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2009 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2010 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2011 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2013 /* Switch back to unit 0 in which the 2D texture will be stored. */
2014 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2016 /* Rebind the texture because it isn't bound anymore */
2017 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2021 static BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2022 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2024 if(This->palette || (This->resource.format != WINED3DFMT_P8 && This->resource.format != WINED3DFMT_A8P8)) {
2025 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2026 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2031 if(This->palette9) {
2032 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2036 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2038 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2042 static inline void clear_unused_channels(IWineD3DSurfaceImpl *This) {
2043 GLboolean oldwrite[4];
2045 /* Some formats have only some color channels, and the others are 1.0.
2046 * since our rendering renders to all channels, and those pixel formats
2047 * are emulated by using a full texture with the other channels set to 1.0
2048 * manually, clear the unused channels.
2050 * This could be done with hacking colorwriteenable to mask the colors,
2051 * but before drawing the buffer would have to be cleared too, so there's
2054 switch(This->resource.format) {
2055 case WINED3DFMT_R16F:
2056 case WINED3DFMT_R32F:
2057 TRACE("R16F or R32F format, clearing green, blue and alpha to 1.0\n");
2058 /* Do not activate a context, the correct drawable is active already
2059 * though just the read buffer is set, make sure to have the correct draw
2062 glDrawBuffer(This->resource.wineD3DDevice->offscreenBuffer);
2063 glDisable(GL_SCISSOR_TEST);
2064 glGetBooleanv(GL_COLOR_WRITEMASK, oldwrite);
2065 glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE);
2066 glClearColor(0.0, 1.0, 1.0, 1.0);
2067 glClear(GL_COLOR_BUFFER_BIT);
2068 glColorMask(oldwrite[0], oldwrite[1], oldwrite[2], oldwrite[3]);
2069 if(!This->resource.wineD3DDevice->render_offscreen) glDrawBuffer(GL_BACK);
2070 checkGLcall("Unused channel clear\n");
2077 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2078 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2080 if (!(This->Flags & SFLAG_INTEXTURE)) {
2081 TRACE("Reloading because surface is dirty\n");
2082 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2083 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2084 /* Reload: vice versa OR */
2085 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2086 /* Also reload: Color key is active AND the color key has changed */
2087 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2088 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2089 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2090 TRACE("Reloading because of color keying\n");
2091 /* To perform the color key conversion we need a sysmem copy of
2092 * the surface. Make sure we have it
2094 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2095 } else if(palette9_changed(This)) {
2096 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
2097 /* TODO: This is not necessarily needed with hw palettized texture support */
2098 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2100 TRACE("surface is already in texture\n");
2104 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2105 * These resources are not bound by device size or format restrictions. Because of this,
2106 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2107 * However, these resources can always be created, locked, and copied.
2109 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2111 FIXME("(%p) Operation not supported for scratch textures\n",This);
2112 return WINED3DERR_INVALIDCALL;
2115 This->srgb = srgb_mode;
2116 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* no partial locking for textures yet */);
2120 static unsigned int gen = 0;
2123 if ((gen % 10) == 0) {
2124 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2125 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2128 * debugging crash code
2137 if (!(This->Flags & SFLAG_DONOTFREE)) {
2138 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2139 This->resource.allocatedMemory = NULL;
2140 This->resource.heapMemory = NULL;
2141 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2147 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface) {
2148 /* TODO: check for locks */
2149 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2150 IWineD3DBaseTexture *baseTexture = NULL;
2151 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2153 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2154 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2155 TRACE("Passing to container\n");
2156 IWineD3DBaseTexture_BindTexture(baseTexture);
2157 IWineD3DBaseTexture_Release(baseTexture);
2159 TRACE("(%p) : Binding surface\n", This);
2161 if(!device->isInDraw) {
2162 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2165 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2173 HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename) {
2176 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2177 char *allocatedMemory;
2179 IWineD3DSwapChain *swapChain = NULL;
2181 GLuint tmpTexture = 0;
2184 Textures may not be stored in ->allocatedgMemory and a GlTexture
2185 so we should lock the surface before saving a snapshot, or at least check that
2187 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2188 by calling GetTexImage and in compressed form by calling
2189 GetCompressedTexImageARB. Queried compressed images can be saved and
2190 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2191 texture images do not need to be processed by the GL and should
2192 significantly improve texture loading performance relative to uncompressed
2195 /* Setup the width and height to be the internal texture width and height. */
2196 width = This->pow2Width;
2197 height = This->pow2Height;
2198 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2199 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2201 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2202 /* if were not a real texture then read the back buffer into a real texture */
2203 /* we don't want to interfere with the back buffer so read the data into a temporary
2204 * texture and then save the data out of the temporary texture
2208 TRACE("(%p) Reading render target into texture\n", This);
2209 glEnable(GL_TEXTURE_2D);
2211 glGenTextures(1, &tmpTexture);
2212 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2214 glTexImage2D(GL_TEXTURE_2D,
2221 GL_UNSIGNED_INT_8_8_8_8_REV,
2224 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2225 vcheckGLcall("glGetIntegerv");
2226 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2227 vcheckGLcall("glReadBuffer");
2228 glCopyTexImage2D(GL_TEXTURE_2D,
2237 checkGLcall("glCopyTexImage2D");
2238 glReadBuffer(prevRead);
2241 } else { /* bind the real texture, and make sure it up to date */
2242 IWineD3DSurface_PreLoad(iface);
2244 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2246 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2247 glGetTexImage(GL_TEXTURE_2D,
2248 This->glDescription.level,
2250 GL_UNSIGNED_INT_8_8_8_8_REV,
2252 checkGLcall("glTexImage2D");
2254 glBindTexture(GL_TEXTURE_2D, 0);
2255 glDeleteTextures(1, &tmpTexture);
2259 f = fopen(filename, "w+");
2261 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2262 return WINED3DERR_INVALIDCALL;
2264 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2265 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format));
2280 fwrite(&width,2,1,f);
2282 fwrite(&height,2,1,f);
2287 /* 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 */
2289 textureRow = allocatedMemory + (width * (height - 1) *4);
2291 textureRow = allocatedMemory;
2292 for (y = 0 ; y < height; y++) {
2293 for (i = 0; i < width; i++) {
2294 color = *((DWORD*)textureRow);
2295 fputc((color >> 16) & 0xFF, f); /* B */
2296 fputc((color >> 8) & 0xFF, f); /* G */
2297 fputc((color >> 0) & 0xFF, f); /* R */
2298 fputc((color >> 24) & 0xFF, f); /* A */
2301 /* take two rows of the pointer to the texture memory */
2303 (textureRow-= width << 3);
2306 TRACE("Closing file\n");
2310 IWineD3DSwapChain_Release(swapChain);
2312 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2317 * Slightly inefficient way to handle multiple dirty rects but it works :)
2319 extern HRESULT WINAPI IWineD3DSurfaceImpl_AddDirtyRect(IWineD3DSurface *iface, CONST RECT* pDirtyRect) {
2320 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2321 IWineD3DBaseTexture *baseTexture = NULL;
2323 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
2324 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
2326 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2327 if (NULL != pDirtyRect) {
2328 This->dirtyRect.left = min(This->dirtyRect.left, pDirtyRect->left);
2329 This->dirtyRect.top = min(This->dirtyRect.top, pDirtyRect->top);
2330 This->dirtyRect.right = max(This->dirtyRect.right, pDirtyRect->right);
2331 This->dirtyRect.bottom = max(This->dirtyRect.bottom, pDirtyRect->bottom);
2333 This->dirtyRect.left = 0;
2334 This->dirtyRect.top = 0;
2335 This->dirtyRect.right = This->currentDesc.Width;
2336 This->dirtyRect.bottom = This->currentDesc.Height;
2338 TRACE("(%p) : Dirty: yes, Rect:(%d,%d,%d,%d)\n", This, This->dirtyRect.left,
2339 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
2340 /* if the container is a basetexture then mark it dirty. */
2341 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2342 TRACE("Passing to container\n");
2343 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
2344 IWineD3DBaseTexture_Release(baseTexture);
2349 HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2350 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2352 const GlPixelFormatDesc *glDesc;
2353 getFormatDescEntry(format, &GLINFO_LOCATION, &glDesc);
2355 TRACE("(%p) : Calling base function first\n", This);
2356 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2358 /* Setup some glformat defaults */
2359 This->glDescription.glFormat = glDesc->glFormat;
2360 This->glDescription.glFormatInternal = glDesc->glInternal;
2361 This->glDescription.glType = glDesc->glType;
2363 This->Flags &= ~SFLAG_ALLOCATED;
2364 TRACE("(%p) : glFormat %d, glFotmatInternal %d, glType %d\n", This,
2365 This->glDescription.glFormat, This->glDescription.glFormatInternal, This->glDescription.glType);
2370 HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2371 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2373 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2374 WARN("Surface is locked or the HDC is in use\n");
2375 return WINED3DERR_INVALIDCALL;
2378 if(Mem && Mem != This->resource.allocatedMemory) {
2379 void *release = NULL;
2381 /* Do I have to copy the old surface content? */
2382 if(This->Flags & SFLAG_DIBSECTION) {
2383 /* Release the DC. No need to hold the critical section for the update
2384 * Thread because this thread runs only on front buffers, but this method
2385 * fails for render targets in the check above.
2387 SelectObject(This->hDC, This->dib.holdbitmap);
2388 DeleteDC(This->hDC);
2389 /* Release the DIB section */
2390 DeleteObject(This->dib.DIBsection);
2391 This->dib.bitmap_data = NULL;
2392 This->resource.allocatedMemory = NULL;
2394 This->Flags &= ~SFLAG_DIBSECTION;
2395 } else if(!(This->Flags & SFLAG_USERPTR)) {
2396 release = This->resource.heapMemory;
2397 This->resource.heapMemory = NULL;
2399 This->resource.allocatedMemory = Mem;
2400 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2402 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2403 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2405 /* For client textures opengl has to be notified */
2406 if(This->Flags & SFLAG_CLIENT) {
2407 This->Flags &= ~SFLAG_ALLOCATED;
2408 IWineD3DSurface_PreLoad(iface);
2409 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2412 /* Now free the old memory if any */
2413 HeapFree(GetProcessHeap(), 0, release);
2414 } else if(This->Flags & SFLAG_USERPTR) {
2415 /* Lockrect and GetDC will re-create the dib section and allocated memory */
2416 This->resource.allocatedMemory = NULL;
2417 /* HeapMemory should be NULL already */
2418 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2419 This->Flags &= ~SFLAG_USERPTR;
2421 if(This->Flags & SFLAG_CLIENT) {
2422 This->Flags &= ~SFLAG_ALLOCATED;
2423 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2424 IWineD3DSurface_PreLoad(iface);
2430 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2431 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2432 IWineD3DSwapChainImpl *swapchain = NULL;
2434 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2436 /* Flipping is only supported on RenderTargets */
2437 if( !(This->resource.usage & WINED3DUSAGE_RENDERTARGET) ) return WINEDDERR_NOTFLIPPABLE;
2440 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2441 * FIXME("(%p) Target override is not supported by now\n", This);
2442 * Additionally, it isn't really possible to support triple-buffering
2443 * properly on opengl at all
2447 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2449 ERR("Flipped surface is not on a swapchain\n");
2450 return WINEDDERR_NOTFLIPPABLE;
2453 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2454 * and only d3d8 and d3d9 apps specify the presentation interval
2456 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2457 /* Most common case first to avoid wasting time on all the other cases */
2458 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2459 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2460 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2461 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2462 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2463 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2464 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2466 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2469 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2470 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2471 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2475 /* Does a direct frame buffer -> texture copy. Stretching is done
2476 * with single pixel copy calls
2478 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2479 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2482 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2485 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2487 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2489 /* TODO: Do we need GL_TEXTURE_2D enabled fpr copyteximage? */
2490 glEnable(This->glDescription.target);
2491 checkGLcall("glEnable(This->glDescription.target)");
2493 /* Bind the target texture */
2494 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2495 checkGLcall("glBindTexture");
2497 glReadBuffer(myDevice->offscreenBuffer);
2499 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
2500 glReadBuffer(buffer);
2502 checkGLcall("glReadBuffer");
2504 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
2505 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
2507 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2508 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2510 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
2511 ERR("Texture filtering not supported in direct blit\n");
2513 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2514 ERR("Texture filtering not supported in direct blit\n");
2518 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
2519 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
2520 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
2522 glCopyTexSubImage2D(This->glDescription.target,
2523 This->glDescription.level,
2524 drect->x1, drect->y1, /* xoffset, yoffset */
2525 srect->x1, Src->currentDesc.Height - srect->y2,
2526 drect->x2 - drect->x1, drect->y2 - drect->y1);
2528 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
2529 /* I have to process this row by row to swap the image,
2530 * otherwise it would be upside down, so stretching in y direction
2531 * doesn't cost extra time
2533 * However, stretching in x direction can be avoided if not necessary
2535 for(row = drect->y1; row < drect->y2; row++) {
2536 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
2537 /* Well, that stuff works, but it's very slow.
2538 * find a better way instead
2542 for(col = drect->x1; col < drect->x2; col++) {
2543 glCopyTexSubImage2D(This->glDescription.target,
2544 This->glDescription.level,
2545 drect->x1 + col, row, /* xoffset, yoffset */
2546 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
2550 glCopyTexSubImage2D(This->glDescription.target,
2551 This->glDescription.level,
2552 drect->x1, row, /* xoffset, yoffset */
2553 srect->x1, yoffset - (int) (row * yrel),
2554 drect->x2-drect->x1, 1);
2558 vcheckGLcall("glCopyTexSubImage2D");
2560 /* Leave the opengl state valid for blitting */
2561 glDisable(This->glDescription.target);
2562 checkGLcall("glDisable(This->glDescription.target)");
2567 /* Uses the hardware to stretch and flip the image */
2568 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface, IWineD3DSwapChainImpl *swapchain, WINED3DRECT *srect, WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter) {
2569 GLuint src, backup = 0;
2570 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2571 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2572 float left, right, top, bottom; /* Texture coordinates */
2573 UINT fbwidth = Src->currentDesc.Width;
2574 UINT fbheight = Src->currentDesc.Height;
2575 GLenum drawBuffer = GL_BACK;
2576 GLenum texture_target;
2578 TRACE("Using hwstretch blit\n");
2579 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2580 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
2583 IWineD3DSurface_PreLoad((IWineD3DSurface *) This);
2585 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2586 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2588 if(GL_LIMITS(aux_buffers) >= 2) {
2589 /* Got more than one aux buffer? Use the 2nd aux buffer */
2590 drawBuffer = GL_AUX1;
2591 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && GL_LIMITS(aux_buffers) >= 1) {
2592 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2593 drawBuffer = GL_AUX0;
2596 if(!swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
2597 glGenTextures(1, &backup);
2598 checkGLcall("glGenTextures\n");
2599 glBindTexture(GL_TEXTURE_2D, backup);
2600 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2601 texture_target = GL_TEXTURE_2D;
2603 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2604 * we are reading from the back buffer, the backup can be used as source texture
2606 if(Src->glDescription.textureName == 0) {
2607 /* Get it a description */
2608 IWineD3DSurface_PreLoad(SrcSurface);
2610 texture_target = Src->glDescription.target;
2611 glBindTexture(texture_target, Src->glDescription.textureName);
2612 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
2613 glEnable(texture_target);
2614 checkGLcall("glEnable(texture_target)");
2616 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2617 Src->Flags &= ~SFLAG_INTEXTURE;
2620 glReadBuffer(GL_BACK);
2621 checkGLcall("glReadBuffer(GL_BACK)");
2623 /* TODO: Only back up the part that will be overwritten */
2624 glCopyTexSubImage2D(texture_target, 0,
2625 0, 0 /* read offsets */,
2630 checkGLcall("glCopyTexSubImage2D");
2632 /* No issue with overriding these - the sampler is dirty due to blit usage */
2633 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
2634 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
2635 checkGLcall("glTexParameteri");
2636 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2637 minMipLookup[Filter][WINED3DTEXF_NONE]);
2638 checkGLcall("glTexParameteri");
2640 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
2641 src = backup ? backup : Src->glDescription.textureName;
2643 glReadBuffer(GL_FRONT);
2644 checkGLcall("glReadBuffer(GL_FRONT)");
2646 glGenTextures(1, &src);
2647 checkGLcall("glGenTextures(1, &src)");
2648 glBindTexture(GL_TEXTURE_2D, src);
2649 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
2651 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
2652 * out for power of 2 sizes
2654 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
2655 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2656 checkGLcall("glTexImage2D");
2657 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
2658 0, 0 /* read offsets */,
2663 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2664 checkGLcall("glTexParameteri");
2665 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2666 checkGLcall("glTexParameteri");
2668 glReadBuffer(GL_BACK);
2669 checkGLcall("glReadBuffer(GL_BACK)");
2671 if(texture_target != GL_TEXTURE_2D) {
2672 glDisable(texture_target);
2673 glEnable(GL_TEXTURE_2D);
2674 texture_target = GL_TEXTURE_2D;
2677 checkGLcall("glEnd and previous");
2679 left = (float) srect->x1 / (float) Src->pow2Width;
2680 right = (float) srect->x2 / (float) Src->pow2Width;
2683 top = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2684 bottom = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2686 top = (float) (Src->currentDesc.Height - srect->y2) / (float) Src->pow2Height;
2687 bottom = (float) (Src->currentDesc.Height - srect->y1) / (float) Src->pow2Height;
2690 /* draw the source texture stretched and upside down. The correct surface is bound already */
2691 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
2692 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
2694 glDrawBuffer(drawBuffer);
2695 glReadBuffer(drawBuffer);
2699 glTexCoord2f(left, bottom);
2700 glVertex2i(0, fbheight);
2703 glTexCoord2f(left, top);
2704 glVertex2i(0, fbheight - drect->y2 - drect->y1);
2707 glTexCoord2f(right, top);
2708 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
2711 glTexCoord2f(right, bottom);
2712 glVertex2i(drect->x2 - drect->x1, fbheight);
2714 checkGLcall("glEnd and previous");
2716 if(texture_target != This->glDescription.target) {
2717 glDisable(texture_target);
2718 glEnable(This->glDescription.target);
2719 texture_target = This->glDescription.target;
2722 /* Now read the stretched and upside down image into the destination texture */
2723 glBindTexture(texture_target, This->glDescription.textureName);
2724 checkGLcall("glBindTexture");
2725 glCopyTexSubImage2D(texture_target,
2727 drect->x1, drect->y1, /* xoffset, yoffset */
2728 0, 0, /* We blitted the image to the origin */
2729 drect->x2 - drect->x1, drect->y2 - drect->y1);
2730 checkGLcall("glCopyTexSubImage2D");
2732 if(drawBuffer == GL_BACK) {
2733 /* Write the back buffer backup back */
2735 if(texture_target != GL_TEXTURE_2D) {
2736 glDisable(texture_target);
2737 glEnable(GL_TEXTURE_2D);
2738 texture_target = GL_TEXTURE_2D;
2740 glBindTexture(GL_TEXTURE_2D, backup);
2741 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
2743 if(texture_target != Src->glDescription.target) {
2744 glDisable(texture_target);
2745 glEnable(Src->glDescription.target);
2746 texture_target = Src->glDescription.target;
2748 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
2749 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
2754 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
2758 glTexCoord2f(0.0, 0.0);
2759 glVertex2i(0, fbheight);
2762 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
2763 glVertex2i(fbwidth, Src->currentDesc.Height);
2766 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
2767 glVertex2i(fbwidth, 0);
2770 /* Restore the old draw buffer */
2771 glDrawBuffer(GL_BACK);
2773 glDisable(texture_target);
2774 checkGLcall("glDisable(texture_target)");
2777 if(src != Src->glDescription.textureName && src != backup) {
2778 glDeleteTextures(1, &src);
2779 checkGLcall("glDeleteTextures(1, &src)");
2782 glDeleteTextures(1, &backup);
2783 checkGLcall("glDeleteTextures(1, &backup)");
2789 /* Not called from the VTable */
2790 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
2792 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
2793 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
2794 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2796 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
2798 /* Get the swapchain. One of the surfaces has to be a primary surface */
2799 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2800 WARN("Destination is in sysmem, rejecting gl blt\n");
2801 return WINED3DERR_INVALIDCALL;
2803 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
2804 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
2806 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
2807 WARN("Src is in sysmem, rejecting gl blt\n");
2808 return WINED3DERR_INVALIDCALL;
2810 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
2811 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
2814 /* Early sort out of cases where no render target is used */
2815 if(!dstSwapchain && !srcSwapchain &&
2816 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2817 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
2818 return WINED3DERR_INVALIDCALL;
2821 /* No destination color keying supported */
2822 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
2823 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2824 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2825 return WINED3DERR_INVALIDCALL;
2829 rect.x1 = DestRect->left;
2830 rect.y1 = DestRect->top;
2831 rect.x2 = DestRect->right;
2832 rect.y2 = DestRect->bottom;
2836 rect.x2 = This->currentDesc.Width;
2837 rect.y2 = This->currentDesc.Height;
2840 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
2841 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
2842 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
2843 /* Half-life does a Blt from the back buffer to the front buffer,
2844 * Full surface size, no flags... Use present instead
2846 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
2849 /* Check rects - IWineD3DDevice_Present doesn't handle them */
2853 TRACE("Looking if a Present can be done...\n");
2854 /* Source Rectangle must be full surface */
2856 if(SrcRect->left != 0 || SrcRect->top != 0 ||
2857 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
2858 TRACE("No, Source rectangle doesn't match\n");
2864 mySrcRect.right = Src->currentDesc.Width;
2865 mySrcRect.bottom = Src->currentDesc.Height;
2867 /* No stretching may occur */
2868 if(mySrcRect.right != rect.x2 - rect.x1 ||
2869 mySrcRect.bottom != rect.y2 - rect.y1) {
2870 TRACE("No, stretching is done\n");
2874 /* Destination must be full surface or match the clipping rectangle */
2875 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
2879 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
2884 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
2887 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
2888 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
2890 TRACE("No, dest rectangle doesn't match(clipper)\n");
2891 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
2892 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
2898 if(rect.x1 != 0 || rect.y1 != 0 ||
2899 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
2900 TRACE("No, dest rectangle doesn't match(surface size)\n");
2907 /* These flags are unimportant for the flag check, remove them */
2908 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
2909 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
2911 /* The idea behind this is that a glReadPixels and a glDrawPixels call
2912 * take very long, while a flip is fast.
2913 * This applies to Half-Life, which does such Blts every time it finished
2914 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
2915 * menu. This is also used by all apps when they do windowed rendering
2917 * The problem is that flipping is not really the same as copying. After a
2918 * Blt the front buffer is a copy of the back buffer, and the back buffer is
2919 * untouched. Therefore it's necessary to override the swap effect
2920 * and to set it back after the flip.
2922 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
2926 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
2927 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2929 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
2930 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
2932 dstSwapchain->presentParms.SwapEffect = orig_swap;
2939 TRACE("Unsupported blit between buffers on the same swapchain\n");
2940 return WINED3DERR_INVALIDCALL;
2941 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
2942 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
2943 return WINED3DERR_INVALIDCALL;
2944 } else if(dstSwapchain && srcSwapchain) {
2945 FIXME("Implement hardware blit between two different swapchains\n");
2946 return WINED3DERR_INVALIDCALL;
2947 } else if(dstSwapchain) {
2948 if(SrcSurface == myDevice->render_targets[0]) {
2949 TRACE("Blit from active render target to a swapchain\n");
2950 /* Handled with regular texture -> swapchain blit */
2952 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
2953 FIXME("Implement blit from a swapchain to the active render target\n");
2954 return WINED3DERR_INVALIDCALL;
2957 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
2958 /* Blit from render target to texture */
2960 BOOL upsideDown, stretchx;
2962 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
2963 TRACE("Color keying not supported by frame buffer to texture blit\n");
2964 return WINED3DERR_INVALIDCALL;
2965 /* Destination color key is checked above */
2968 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2969 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2972 if(SrcRect->top < SrcRect->bottom) {
2973 srect.y1 = SrcRect->top;
2974 srect.y2 = SrcRect->bottom;
2977 srect.y1 = SrcRect->bottom;
2978 srect.y2 = SrcRect->top;
2981 srect.x1 = SrcRect->left;
2982 srect.x2 = SrcRect->right;
2986 srect.x2 = Src->currentDesc.Width;
2987 srect.y2 = Src->currentDesc.Height;
2990 if(rect.x1 > rect.x2) {
2994 upsideDown = !upsideDown;
2997 TRACE("Reading from an offscreen target\n");
2998 upsideDown = !upsideDown;
3001 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3007 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3008 * flip the image nor scale it.
3010 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3011 * -> If the app wants a image width an unscaled width, copy it line per line
3012 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3013 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3014 * back buffer. This is slower than reading line per line, thus not used for flipping
3015 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3018 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3019 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3022 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)) {
3023 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3024 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3025 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3026 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3027 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3028 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3030 TRACE("Using hardware stretching to flip / stretch the texture\n");
3031 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3034 if(!(This->Flags & SFLAG_DONOTFREE)) {
3035 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3036 This->resource.allocatedMemory = NULL;
3037 This->resource.heapMemory = NULL;
3039 This->Flags &= ~SFLAG_INSYSMEM;
3041 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3042 * path is never entered
3044 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3048 /* Blit from offscreen surface to render target */
3049 float glTexCoord[4];
3050 DWORD oldCKeyFlags = Src->CKeyFlags;
3051 WINEDDCOLORKEY oldBltCKey = This->SrcBltCKey;
3052 RECT SourceRectangle;
3054 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3057 SourceRectangle.left = SrcRect->left;
3058 SourceRectangle.right = SrcRect->right;
3059 SourceRectangle.top = SrcRect->top;
3060 SourceRectangle.bottom = SrcRect->bottom;
3062 SourceRectangle.left = 0;
3063 SourceRectangle.right = Src->currentDesc.Width;
3064 SourceRectangle.top = 0;
3065 SourceRectangle.bottom = Src->currentDesc.Height;
3067 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT) &&
3068 (Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) == 0) {
3069 TRACE("Using stretch_rect_fbo\n");
3070 /* The source is always a texture, but never the currently active render target, and the texture
3071 * contents are never upside down
3073 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3074 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3078 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3079 /* Fall back to software */
3080 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3081 SourceRectangle.left, SourceRectangle.top,
3082 SourceRectangle.right, SourceRectangle.bottom);
3083 return WINED3DERR_INVALIDCALL;
3086 /* Color keying: Check if we have to do a color keyed blt,
3087 * and if not check if a color key is activated.
3089 * Just modify the color keying parameters in the surface and restore them afterwards
3090 * The surface keeps track of the color key last used to load the opengl surface.
3091 * PreLoad will catch the change to the flags and color key and reload if necessary.
3093 if(Flags & WINEDDBLT_KEYSRC) {
3094 /* Use color key from surface */
3095 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3096 /* Use color key from DDBltFx */
3097 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3098 This->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3100 /* Do not use color key */
3101 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3104 /* Now load the surface */
3105 IWineD3DSurface_PreLoad((IWineD3DSurface *) Src);
3108 /* Activate the destination context, set it up for blitting */
3109 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3112 glEnable(Src->glDescription.target);
3113 checkGLcall("glEnable(Src->glDescription.target)");
3116 TRACE("Drawing to offscreen buffer\n");
3117 glDrawBuffer(myDevice->offscreenBuffer);
3118 checkGLcall("glDrawBuffer");
3120 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This, (IWineD3DSwapChain *)dstSwapchain);
3121 TRACE("Drawing to %#x buffer\n", buffer);
3122 glDrawBuffer(buffer);
3123 checkGLcall("glDrawBuffer");
3126 /* Bind the texture */
3127 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3128 checkGLcall("glBindTexture");
3130 /* Filtering for StretchRect */
3131 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3132 stateLookup[WINELOOKUP_MAGFILTER][Filter - minLookup[WINELOOKUP_MAGFILTER]]);
3133 checkGLcall("glTexParameteri");
3134 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3135 minMipLookup[Filter][WINED3DTEXF_NONE]);
3136 checkGLcall("glTexParameteri");
3137 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3138 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3139 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3140 checkGLcall("glTexEnvi");
3142 /* This is for color keying */
3143 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3144 glEnable(GL_ALPHA_TEST);
3145 checkGLcall("glEnable GL_ALPHA_TEST");
3146 glAlphaFunc(GL_NOTEQUAL, 0.0);
3147 checkGLcall("glAlphaFunc\n");
3149 glDisable(GL_ALPHA_TEST);
3150 checkGLcall("glDisable GL_ALPHA_TEST");
3153 /* Draw a textured quad
3157 glColor3d(1.0f, 1.0f, 1.0f);
3158 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3163 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3164 glVertex3f(rect.x1, rect.y2, 0.0);
3166 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3171 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3176 checkGLcall("glEnd");
3178 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3179 glDisable(GL_ALPHA_TEST);
3180 checkGLcall("glDisable(GL_ALPHA_TEST)");
3183 /* Flush in case the drawable is used by multiple GL contexts */
3184 if(dstSwapchain && (dstSwapchain->num_contexts >= 2))
3187 glBindTexture(Src->glDescription.target, 0);
3188 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3189 /* Leave the opengl state valid for blitting */
3190 glDisable(Src->glDescription.target);
3191 checkGLcall("glDisable(Src->glDescription.target)");
3193 /* The draw buffer should only need to be restored if we were drawing to the front buffer, and there is a back buffer.
3194 * otherwise the context manager should choose between GL_BACK / offscreenDrawBuffer
3196 if(dstSwapchain && This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer && dstSwapchain->backBuffer) {
3197 glDrawBuffer(GL_BACK);
3198 checkGLcall("glDrawBuffer");
3200 /* Restore the color key parameters */
3201 Src->CKeyFlags = oldCKeyFlags;
3202 This->SrcBltCKey = oldBltCKey;
3206 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3207 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3210 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3211 /* TODO: This should be moved to ModifyLocation() */
3212 if(!(dstSwapchain || wined3d_settings.offscreen_rendering_mode != ORM_FBO)) {
3213 This->Flags |= SFLAG_INTEXTURE;
3218 /* Source-Less Blit to render target */
3219 if (Flags & WINEDDBLT_COLORFILL) {
3220 /* This is easy to handle for the D3D Device... */
3223 TRACE("Colorfill\n");
3225 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3226 must be true if we are here */
3227 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3228 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3229 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3230 TRACE("Surface is higher back buffer, falling back to software\n");
3231 return WINED3DERR_INVALIDCALL;
3234 /* The color as given in the Blt function is in the format of the frame-buffer...
3235 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3237 if (This->resource.format == WINED3DFMT_P8) {
3238 if (This->palette) {
3239 color = ((0xFF000000) |
3240 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3241 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3242 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3247 else if (This->resource.format == WINED3DFMT_R5G6B5) {
3248 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3251 color = ((0xFF000000) |
3252 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3253 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3254 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3257 else if ((This->resource.format == WINED3DFMT_R8G8B8) ||
3258 (This->resource.format == WINED3DFMT_X8R8G8B8) ) {
3259 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3261 else if (This->resource.format == WINED3DFMT_A8R8G8B8) {
3262 color = DDBltFx->u5.dwFillColor;
3265 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3266 return WINED3DERR_INVALIDCALL;
3269 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3270 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3271 1, /* Number of rectangles */
3272 &rect, WINED3DCLEAR_TARGET, color,
3279 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3280 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3281 return WINED3DERR_INVALIDCALL;
3284 static HRESULT WINAPI IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx)
3286 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3289 if (Flags & WINEDDBLT_DEPTHFILL) {
3290 switch(This->resource.format) {
3291 case WINED3DFMT_D16:
3292 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3294 case WINED3DFMT_D15S1:
3295 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3297 case WINED3DFMT_D24S8:
3298 case WINED3DFMT_D24X8:
3299 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3301 case WINED3DFMT_D32:
3302 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3306 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format));
3309 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3310 DestRect == NULL ? 0 : 1,
3311 (WINED3DRECT *) DestRect,
3312 WINED3DCLEAR_ZBUFFER,
3318 FIXME("(%p): Unsupp depthstencil blit\n", This);
3319 return WINED3DERR_INVALIDCALL;
3322 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, RECT *DestRect, IWineD3DSurface *SrcSurface, RECT *SrcRect, DWORD Flags, WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3323 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3324 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3325 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3326 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3327 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3329 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3330 * except depth blits, which seem to work
3332 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3333 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3334 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3335 return WINED3DERR_INVALIDCALL;
3336 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3337 TRACE("Z Blit override handled the blit\n");
3342 /* Special cases for RenderTargets */
3343 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3344 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3345 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3348 /* For the rest call the X11 surface implementation.
3349 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3350 * other Blts are rather rare
3352 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3355 HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty, IWineD3DSurface *Source, RECT *rsrc, DWORD trans) {
3356 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3357 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3358 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3359 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3361 if(myDevice->inScene &&
3362 (iface == myDevice->stencilBufferTarget ||
3363 (Source && Source == myDevice->stencilBufferTarget))) {
3364 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3365 return WINED3DERR_INVALIDCALL;
3368 /* Special cases for RenderTargets */
3369 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3370 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3372 RECT SrcRect, DstRect;
3376 SrcRect.left = rsrc->left;
3377 SrcRect.top= rsrc->top;
3378 SrcRect.bottom = rsrc->bottom;
3379 SrcRect.right = rsrc->right;
3383 SrcRect.right = srcImpl->currentDesc.Width;
3384 SrcRect.bottom = srcImpl->currentDesc.Height;
3387 DstRect.left = dstx;
3389 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3390 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3392 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3393 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3394 Flags |= WINEDDBLT_KEYSRC;
3395 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3396 Flags |= WINEDDBLT_KEYDEST;
3397 if(trans & WINEDDBLTFAST_WAIT)
3398 Flags |= WINEDDBLT_WAIT;
3399 if(trans & WINEDDBLTFAST_DONOTWAIT)
3400 Flags |= WINEDDBLT_DONOTWAIT;
3402 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3406 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3409 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3410 /** Check against the maximum texture sizes supported by the video card **/
3411 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3412 unsigned int pow2Width, pow2Height;
3413 const GlPixelFormatDesc *glDesc;
3415 getFormatDescEntry(This->resource.format, &GLINFO_LOCATION, &glDesc);
3416 /* Setup some glformat defaults */
3417 This->glDescription.glFormat = glDesc->glFormat;
3418 This->glDescription.glFormatInternal = glDesc->glInternal;
3419 This->glDescription.glType = glDesc->glType;
3421 This->glDescription.textureName = 0;
3422 This->glDescription.target = GL_TEXTURE_2D;
3424 /* Non-power2 support */
3425 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO)) {
3426 pow2Width = This->currentDesc.Width;
3427 pow2Height = This->currentDesc.Height;
3429 /* Find the nearest pow2 match */
3430 pow2Width = pow2Height = 1;
3431 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3432 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3434 This->pow2Width = pow2Width;
3435 This->pow2Height = pow2Height;
3437 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3438 WINED3DFORMAT Format = This->resource.format;
3439 /** TODO: add support for non power two compressed textures **/
3440 if (Format == WINED3DFMT_DXT1 || Format == WINED3DFMT_DXT2 || Format == WINED3DFMT_DXT3
3441 || Format == WINED3DFMT_DXT4 || Format == WINED3DFMT_DXT5) {
3442 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3443 This, This->currentDesc.Width, This->currentDesc.Height);
3444 return WINED3DERR_NOTAVAILABLE;
3448 if(pow2Width != This->currentDesc.Width ||
3449 pow2Height != This->currentDesc.Height) {
3450 This->Flags |= SFLAG_NONPOW2;
3453 TRACE("%p\n", This);
3454 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
3455 /* one of three options
3456 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)
3457 2: Set the texture to the maximum size (bad idea)
3458 3: WARN and return WINED3DERR_NOTAVAILABLE;
3459 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.
3461 WARN("(%p) Creating an oversized surface\n", This);
3462 This->Flags |= SFLAG_OVERSIZE;
3464 /* This will be initialized on the first blt */
3465 This->glRect.left = 0;
3466 This->glRect.top = 0;
3467 This->glRect.right = 0;
3468 This->glRect.bottom = 0;
3470 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one */
3471 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE)) {
3472 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
3473 This->pow2Width = This->currentDesc.Width;
3474 This->pow2Height = This->currentDesc.Height;
3475 This->Flags &= ~SFLAG_NONPOW2;
3478 /* No oversize, gl rect is the full texture size */
3479 This->Flags &= ~SFLAG_OVERSIZE;
3480 This->glRect.left = 0;
3481 This->glRect.top = 0;
3482 This->glRect.right = This->pow2Width;
3483 This->glRect.bottom = This->pow2Height;
3486 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3487 switch(wined3d_settings.offscreen_rendering_mode) {
3488 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3489 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3490 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3494 This->Flags |= SFLAG_INSYSMEM;
3499 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
3500 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3501 IWineD3DBaseTexture *texture;
3503 TRACE("(%p)->(%s, %s)\n", iface,
3504 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3505 persistent ? "TRUE" : "FALSE");
3507 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3508 IWineD3DSwapChain *swapchain = NULL;
3510 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3511 TRACE("Surface %p is an onscreen surface\n", iface);
3513 IWineD3DSwapChain_Release(swapchain);
3515 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
3516 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3521 if((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) {
3522 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3523 TRACE("Passing to container\n");
3524 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3525 IWineD3DBaseTexture_Release(texture);
3528 This->Flags &= ~SFLAG_LOCATIONS;
3529 This->Flags |= flag;
3531 if((This->Flags & SFLAG_INTEXTURE) && (flag & SFLAG_INTEXTURE)) {
3532 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
3533 TRACE("Passing to container\n");
3534 IWineD3DBaseTexture_SetDirty(texture, TRUE);
3535 IWineD3DBaseTexture_Release(texture);
3538 This->Flags &= ~flag;
3546 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
3547 struct coords coords[4];
3549 IWineD3DSwapChain *swapchain = NULL;
3550 IWineD3DBaseTexture *texture = NULL;
3552 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3559 rect.right = This->currentDesc.Width;
3560 rect.bottom = This->currentDesc.Height;
3563 ActivateContext(device, device->render_targets[0], CTXUSAGE_BLIT);
3566 if(This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
3567 glEnable(GL_TEXTURE_RECTANGLE_ARB);
3568 checkGLcall("glEnable(GL_TEXTURE_RECTANGLE_ARB)");
3569 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName);
3570 checkGLcall("GL_TEXTURE_RECTANGLE_ARB, This->glDescription.textureName)");
3571 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3572 checkGLcall("glTexParameteri");
3573 glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3574 checkGLcall("glTexParameteri");
3576 coords[0].x = rect.left;
3579 coords[1].x = rect.left;
3582 coords[2].x = rect.right;
3585 coords[3].x = rect.right;
3588 coords[0].y = rect.top;
3589 coords[1].y = rect.bottom;
3590 coords[2].y = rect.bottom;
3591 coords[3].y = rect.top;
3592 } else if(This->glDescription.target == GL_TEXTURE_2D) {
3593 glEnable(GL_TEXTURE_2D);
3594 checkGLcall("glEnable(GL_TEXTURE_2D)");
3595 glBindTexture(GL_TEXTURE_2D, This->glDescription.textureName);
3596 checkGLcall("GL_TEXTURE_2D, This->glDescription.textureName)");
3597 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3598 checkGLcall("glTexParameteri");
3599 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3600 checkGLcall("glTexParameteri");
3602 coords[0].x = (float)rect.left / This->pow2Width;
3605 coords[1].x = (float)rect.left / This->pow2Width;
3608 coords[2].x = (float)rect.right / This->pow2Width;
3611 coords[3].x = (float)rect.right / This->pow2Width;
3614 coords[0].y = (float)rect.top / This->pow2Height;
3615 coords[1].y = (float)rect.bottom / This->pow2Height;
3616 coords[2].y = (float)rect.bottom / This->pow2Height;
3617 coords[3].y = (float)rect.top / This->pow2Height;
3619 /* Must be a cube map */
3620 glEnable(GL_TEXTURE_CUBE_MAP_ARB);
3621 checkGLcall("glEnable(GL_TEXTURE_CUBE_MAP_ARB)");
3622 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName);
3623 checkGLcall("GL_TEXTURE_CUBE_MAP_ARB, This->glDescription.textureName)");
3624 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3625 checkGLcall("glTexParameteri");
3626 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3627 checkGLcall("glTexParameteri");
3629 switch(This->glDescription.target) {
3630 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
3631 coords[0].x = 1; coords[0].y = -1; coords[0].z = 1;
3632 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3633 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3634 coords[3].x = 1; coords[3].y = -1; coords[3].z = -1;
3637 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
3638 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3639 coords[1].x = -1; coords[1].y = 1; coords[1].z = 1;
3640 coords[2].x = -1; coords[2].y = 1; coords[2].z = -1;
3641 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3644 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
3645 coords[0].x = -1; coords[0].y = 1; coords[0].z = 1;
3646 coords[1].x = 1; coords[1].y = 1; coords[1].z = 1;
3647 coords[2].x = 1; coords[2].y = 1; coords[2].z = -1;
3648 coords[3].x = -1; coords[3].y = 1; coords[3].z = -1;
3651 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
3652 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3653 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3654 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3655 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3658 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
3659 coords[0].x = -1; coords[0].y = -1; coords[0].z = 1;
3660 coords[1].x = 1; coords[1].y = -1; coords[1].z = 1;
3661 coords[2].x = 1; coords[2].y = -1; coords[2].z = 1;
3662 coords[3].x = -1; coords[3].y = -1; coords[3].z = 1;
3665 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
3666 coords[0].x = -1; coords[0].y = -1; coords[0].z = -1;
3667 coords[1].x = 1; coords[1].y = -1; coords[1].z = -1;
3668 coords[2].x = 1; coords[2].y = -1; coords[2].z = -1;
3669 coords[3].x = -1; coords[3].y = -1; coords[3].z = -1;
3672 ERR("Unexpected texture target\n");
3679 glTexCoord3fv(&coords[0].x);
3680 glVertex2i(rect.left, device->render_offscreen ? rect.bottom : rect.top);
3682 glTexCoord3fv(&coords[1].x);
3683 glVertex2i(rect.left, device->render_offscreen ? rect.top : rect.bottom);
3685 glTexCoord3fv(&coords[2].x);
3686 glVertex2i(rect.right, device->render_offscreen ? rect.top : rect.bottom);
3688 glTexCoord3fv(&coords[3].x);
3689 glVertex2i(rect.right, device->render_offscreen ? rect.bottom : rect.top);
3691 checkGLcall("glEnd");
3693 if(This->glDescription.target != GL_TEXTURE_2D) {
3694 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3695 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3697 glDisable(GL_TEXTURE_2D);
3698 checkGLcall("glDisable(GL_TEXTURE_2D)");
3701 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain);
3702 if(hr == WINED3D_OK && swapchain) {
3703 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
3704 if(((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
3707 IWineD3DSwapChain_Release(swapchain);
3709 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
3710 * reset properly next draw
3712 hr = IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture);
3713 if(hr == WINED3D_OK && texture) {
3714 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
3715 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
3716 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
3717 IWineD3DBaseTexture_Release(texture);
3723 /*****************************************************************************
3724 * IWineD3DSurface::LoadLocation
3726 * Copies the current surface data from wherever it is to the requested
3727 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
3728 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
3729 * multiple locations, the gl texture is preferred over the drawable, which is
3730 * preferred over system memory. The PBO counts as system memory. If rect is
3731 * not NULL, only the specified rectangle is copied (only supported for
3732 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
3733 * location is marked up to date after the copy.
3736 * flag: Surface location flag to be updated
3737 * rect: rectangle to be copied
3740 * WINED3D_OK on success
3741 * WINED3DERR_DEVICELOST on an internal error
3743 *****************************************************************************/
3744 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
3745 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3746 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
3747 IWineD3DSwapChain *swapchain = NULL;
3748 GLenum format, internal, type;
3749 CONVERT_TYPES convert;
3751 int width, pitch, outpitch;
3754 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
3755 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
3756 TRACE("Surface %p is an onscreen surface\n", iface);
3758 IWineD3DSwapChain_Release(swapchain);
3760 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
3761 * Prefer SFLAG_INTEXTURE. */
3762 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
3766 TRACE("(%p)->(%s, %p)\n", iface,
3767 flag == SFLAG_INSYSMEM ? "SFLAG_INSYSMEM" : flag == SFLAG_INDRAWABLE ? "SFLAG_INDRAWABLE" : "SFLAG_INTEXTURE",
3770 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
3773 if(This->Flags & flag) {
3774 TRACE("Location already up to date\n");
3778 if(!(This->Flags & SFLAG_LOCATIONS)) {
3779 ERR("Surface does not have any up to date location\n");
3780 This->Flags |= SFLAG_LOST;
3781 return WINED3DERR_DEVICELOST;
3784 if(flag == SFLAG_INSYSMEM) {
3785 surface_prepare_system_memory(This);
3787 /* Download the surface to system memory */
3788 if(This->Flags & SFLAG_INTEXTURE) {
3789 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
3790 surface_bind_and_dirtify(This);
3792 surface_download_data(This);
3794 read_from_framebuffer(This, rect,
3795 This->resource.allocatedMemory,
3796 IWineD3DSurface_GetPitch(iface));
3798 } else if(flag == SFLAG_INDRAWABLE) {
3799 if(This->Flags & SFLAG_INTEXTURE) {
3800 surface_blt_to_drawable(This, rect);
3802 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
3804 /* The width is in 'length' not in bytes */
3805 width = This->currentDesc.Width;
3806 pitch = IWineD3DSurface_GetPitch(iface);
3808 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
3809 int height = This->currentDesc.Height;
3811 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
3812 outpitch = width * bpp;
3813 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
3815 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
3817 ERR("Out of memory %d, %d!\n", outpitch, height);
3818 return WINED3DERR_OUTOFVIDEOMEMORY;
3820 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
3822 This->Flags |= SFLAG_CONVERTED;
3824 This->Flags &= ~SFLAG_CONVERTED;
3825 mem = This->resource.allocatedMemory;
3828 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
3830 /* Don't delete PBO memory */
3831 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
3832 HeapFree(GetProcessHeap(), 0, mem);
3834 } else /* if(flag == SFLAG_INTEXTURE) */ {
3835 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, This->srgb);
3837 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
3838 surface_bind_and_dirtify(This);
3840 if (This->Flags & SFLAG_INDRAWABLE) {
3844 glGetIntegerv(GL_READ_BUFFER, &prevRead);
3845 vcheckGLcall("glGetIntegerv");
3846 glReadBuffer(This->resource.wineD3DDevice->offscreenBuffer);
3847 vcheckGLcall("glReadBuffer");
3849 if(!(This->Flags & SFLAG_ALLOCATED)) {
3850 surface_allocate_surface(This, internal, This->pow2Width,
3851 This->pow2Height, format, type);
3854 clear_unused_channels(This);
3856 glCopyTexSubImage2D(This->glDescription.target,
3857 This->glDescription.level,
3859 This->currentDesc.Width,
3860 This->currentDesc.Height);
3861 checkGLcall("glCopyTexSubImage2D");
3863 glReadBuffer(prevRead);
3864 vcheckGLcall("glReadBuffer");
3868 TRACE("Updated target %d\n", This->glDescription.target);
3870 /* The only place where LoadTexture() might get called when isInDraw=1
3871 * is ActivateContext where lastActiveRenderTarget is preloaded.
3873 if(iface == device->lastActiveRenderTarget && device->isInDraw)
3874 ERR("Reading back render target but SFLAG_INDRAWABLE not set\n");
3876 /* Otherwise: System memory copy must be most up to date */
3878 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
3879 This->Flags |= SFLAG_GLCKEY;
3880 This->glCKey = This->SrcBltCKey;
3882 else This->Flags &= ~SFLAG_GLCKEY;
3884 /* The width is in 'length' not in bytes */
3885 width = This->currentDesc.Width;
3886 pitch = IWineD3DSurface_GetPitch(iface);
3888 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
3889 int height = This->currentDesc.Height;
3891 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
3892 outpitch = width * bpp;
3893 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
3895 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
3897 ERR("Out of memory %d, %d!\n", outpitch, height);
3898 return WINED3DERR_OUTOFVIDEOMEMORY;
3900 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
3902 This->Flags |= SFLAG_CONVERTED;
3903 } else if( (This->resource.format == WINED3DFMT_P8) && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) ) {
3904 d3dfmt_p8_upload_palette(iface, convert);
3905 This->Flags &= ~SFLAG_CONVERTED;
3906 mem = This->resource.allocatedMemory;
3908 This->Flags &= ~SFLAG_CONVERTED;
3909 mem = This->resource.allocatedMemory;
3912 /* Make sure the correct pitch is used */
3913 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
3915 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
3916 TRACE("non power of two support\n");
3917 if(!(This->Flags & SFLAG_ALLOCATED)) {
3918 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
3920 if (mem || (This->Flags & SFLAG_PBO)) {
3921 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
3924 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
3925 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
3927 if(!(This->Flags & SFLAG_ALLOCATED)) {
3928 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
3930 if (mem || (This->Flags & SFLAG_PBO)) {
3931 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
3935 /* Restore the default pitch */
3936 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
3938 /* Don't delete PBO memory */
3939 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
3940 HeapFree(GetProcessHeap(), 0, mem);
3945 This->Flags |= flag;
3948 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !swapchain
3949 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
3950 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
3951 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
3957 HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container) {
3958 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3959 IWineD3DSwapChain *swapchain = NULL;
3961 /* Update the drawable size method */
3963 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
3966 This->get_drawable_size = get_drawable_size_swapchain;
3967 IWineD3DSwapChain_Release(swapchain);
3968 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3969 switch(wined3d_settings.offscreen_rendering_mode) {
3970 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3971 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
3972 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
3976 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
3979 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
3982 IWineD3DBaseSurfaceImpl_QueryInterface,
3983 IWineD3DBaseSurfaceImpl_AddRef,
3984 IWineD3DSurfaceImpl_Release,
3985 /* IWineD3DResource */
3986 IWineD3DBaseSurfaceImpl_GetParent,
3987 IWineD3DBaseSurfaceImpl_GetDevice,
3988 IWineD3DBaseSurfaceImpl_SetPrivateData,
3989 IWineD3DBaseSurfaceImpl_GetPrivateData,
3990 IWineD3DBaseSurfaceImpl_FreePrivateData,
3991 IWineD3DBaseSurfaceImpl_SetPriority,
3992 IWineD3DBaseSurfaceImpl_GetPriority,
3993 IWineD3DSurfaceImpl_PreLoad,
3994 IWineD3DSurfaceImpl_UnLoad,
3995 IWineD3DBaseSurfaceImpl_GetType,
3996 /* IWineD3DSurface */
3997 IWineD3DBaseSurfaceImpl_GetContainer,
3998 IWineD3DBaseSurfaceImpl_GetDesc,
3999 IWineD3DSurfaceImpl_LockRect,
4000 IWineD3DSurfaceImpl_UnlockRect,
4001 IWineD3DSurfaceImpl_GetDC,
4002 IWineD3DSurfaceImpl_ReleaseDC,
4003 IWineD3DSurfaceImpl_Flip,
4004 IWineD3DSurfaceImpl_Blt,
4005 IWineD3DBaseSurfaceImpl_GetBltStatus,
4006 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4007 IWineD3DBaseSurfaceImpl_IsLost,
4008 IWineD3DBaseSurfaceImpl_Restore,
4009 IWineD3DSurfaceImpl_BltFast,
4010 IWineD3DBaseSurfaceImpl_GetPalette,
4011 IWineD3DBaseSurfaceImpl_SetPalette,
4012 IWineD3DBaseSurfaceImpl_RealizePalette,
4013 IWineD3DBaseSurfaceImpl_SetColorKey,
4014 IWineD3DBaseSurfaceImpl_GetPitch,
4015 IWineD3DSurfaceImpl_SetMem,
4016 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4017 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4018 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4019 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4020 IWineD3DBaseSurfaceImpl_SetClipper,
4021 IWineD3DBaseSurfaceImpl_GetClipper,
4023 IWineD3DSurfaceImpl_AddDirtyRect,
4024 IWineD3DSurfaceImpl_LoadTexture,
4025 IWineD3DSurfaceImpl_BindTexture,
4026 IWineD3DSurfaceImpl_SaveSnapshot,
4027 IWineD3DSurfaceImpl_SetContainer,
4028 IWineD3DSurfaceImpl_SetGlTextureDesc,
4029 IWineD3DSurfaceImpl_GetGlDesc,
4030 IWineD3DSurfaceImpl_GetData,
4031 IWineD3DSurfaceImpl_SetFormat,
4032 IWineD3DSurfaceImpl_PrivateSetup,
4033 IWineD3DSurfaceImpl_ModifyLocation,
4034 IWineD3DSurfaceImpl_LoadLocation