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-2008 Stefan Dösinger for CodeWeavers
11 * Copyright 2007-2008 Henri Verbeet
12 * Copyright 2006-2008 Roderick Colenbrander
13 * Copyright 2009 Henri Verbeet for CodeWeavers
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wine/port.h"
32 #include "wined3d_private.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
35 WINE_DECLARE_DEBUG_CHANNEL(d3d);
37 #define GLINFO_LOCATION (*gl_info)
39 static void surface_cleanup(IWineD3DSurfaceImpl *This)
41 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
42 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
43 renderbuffer_entry_t *entry, *entry2;
45 TRACE("(%p) : Cleaning up.\n", This);
47 /* Need a context to destroy the texture. Use the currently active render
48 * target, but only if the primary render target exists. Otherwise
49 * lastActiveRenderTarget is garbage. When destroying the primary render
50 * target, Uninit3D() will activate a context before doing anything. */
51 if (device->render_targets && device->render_targets[0])
53 ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
58 if (This->texture_name)
60 /* Release the OpenGL texture. */
61 TRACE("Deleting texture %u.\n", This->texture_name);
62 glDeleteTextures(1, &This->texture_name);
65 if (This->Flags & SFLAG_PBO)
68 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
71 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry)
73 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
74 HeapFree(GetProcessHeap(), 0, entry);
79 if (This->Flags & SFLAG_DIBSECTION)
82 SelectObject(This->hDC, This->dib.holdbitmap);
84 /* Release the DIB section. */
85 DeleteObject(This->dib.DIBsection);
86 This->dib.bitmap_data = NULL;
87 This->resource.allocatedMemory = NULL;
90 if (This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem((IWineD3DSurface *)This, NULL);
91 if (This->overlay_dest) list_remove(&This->overlay_entry);
93 HeapFree(GetProcessHeap(), 0, This->palette9);
95 resource_cleanup((IWineD3DResource *)This);
98 UINT surface_calculate_size(const struct GlPixelFormatDesc *format_desc, UINT alignment, UINT width, UINT height)
102 if (format_desc->format == WINED3DFMT_UNKNOWN)
106 else if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
108 UINT row_block_count = (width + format_desc->block_width - 1) / format_desc->block_width;
109 UINT row_count = (height + format_desc->block_height - 1) / format_desc->block_height;
110 size = row_count * row_block_count * format_desc->block_byte_count;
114 /* The pitch is a multiple of 4 bytes. */
115 size = height * (((width * format_desc->byte_count) + alignment - 1) & ~(alignment - 1));
118 if (format_desc->heightscale != 0.0f) size *= format_desc->heightscale;
123 HRESULT surface_init(IWineD3DSurfaceImpl *surface, WINED3DSURFTYPE surface_type, UINT alignment,
124 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
125 UINT multisample_quality, IWineD3DDeviceImpl *device, DWORD usage, WINED3DFORMAT format,
126 WINED3DPOOL pool, IUnknown *parent, const struct wined3d_parent_ops *parent_ops)
128 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
129 const struct GlPixelFormatDesc *format_desc = getFormatDescEntry(format, &GLINFO_LOCATION);
130 void (*cleanup)(IWineD3DSurfaceImpl *This);
131 unsigned int resource_size;
134 if (multisample_quality > 0)
136 FIXME("multisample_quality set to %u, substituting 0\n", multisample_quality);
137 multisample_quality = 0;
140 /* FIXME: Check that the format is supported by the device. */
142 resource_size = surface_calculate_size(format_desc, alignment, width, height);
144 /* Look at the implementation and set the correct Vtable. */
145 switch (surface_type)
148 surface->lpVtbl = &IWineD3DSurface_Vtbl;
149 cleanup = surface_cleanup;
153 surface->lpVtbl = &IWineGDISurface_Vtbl;
154 cleanup = surface_gdi_cleanup;
158 ERR("Requested unknown surface implementation %#x.\n", surface_type);
159 return WINED3DERR_INVALIDCALL;
162 hr = resource_init((IWineD3DResource *)surface, WINED3DRTYPE_SURFACE,
163 device, resource_size, usage, format_desc, pool, parent);
166 WARN("Failed to initialize resource, returning %#x.\n", hr);
170 surface->parent_ops = parent_ops;
172 /* "Standalone" surface. */
173 IWineD3DSurface_SetContainer((IWineD3DSurface *)surface, NULL);
175 surface->currentDesc.Width = width;
176 surface->currentDesc.Height = height;
177 surface->currentDesc.MultiSampleType = multisample_type;
178 surface->currentDesc.MultiSampleQuality = multisample_quality;
179 surface->texture_level = level;
180 list_init(&surface->overlays);
183 surface->Flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
184 if (discard) surface->Flags |= SFLAG_DISCARD;
185 if (lockable || format == WINED3DFMT_D16_LOCKABLE) surface->Flags |= SFLAG_LOCKABLE;
187 /* Quick lockable sanity check.
188 * TODO: remove this after surfaces, usage and lockability have been debugged properly
189 * this function is too deep to need to care about things like this.
190 * Levels need to be checked too, since they all affect what can be done. */
193 case WINED3DPOOL_SCRATCH:
196 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
197 "which are mutually exclusive, setting lockable to TRUE.\n");
202 case WINED3DPOOL_SYSTEMMEM:
204 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
207 case WINED3DPOOL_MANAGED:
208 if (usage & WINED3DUSAGE_DYNAMIC)
209 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
212 case WINED3DPOOL_DEFAULT:
213 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
214 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
218 FIXME("Unknown pool %#x.\n", pool);
222 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
224 FIXME("Trying to create a render target that isn't in the default pool.\n");
227 /* Mark the texture as dirty so that it gets loaded first time around. */
228 surface_add_dirty_rect((IWineD3DSurface *)surface, NULL);
229 list_init(&surface->renderbuffers);
231 TRACE("surface %p, memory %p, size %u\n", surface, surface->resource.allocatedMemory, surface->resource.size);
233 /* Call the private setup routine */
234 hr = IWineD3DSurface_PrivateSetup((IWineD3DSurface *)surface);
237 ERR("Private setup failed, returning %#x\n", hr);
245 static void surface_force_reload(IWineD3DSurface *iface)
247 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
249 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
252 void surface_set_texture_name(IWineD3DSurface *iface, GLuint new_name, BOOL srgb)
254 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
260 name = &This->texture_name_srgb;
261 flag = SFLAG_INSRGBTEX;
265 name = &This->texture_name;
266 flag = SFLAG_INTEXTURE;
269 TRACE("(%p) : setting texture name %u\n", This, new_name);
271 if (!*name && new_name)
273 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
274 * surface has no texture name yet. See if we can get rid of this. */
275 if (This->Flags & flag)
276 ERR("Surface has SFLAG_INTEXTURE set, but no texture name\n");
277 IWineD3DSurface_ModifyLocation(iface, flag, FALSE);
281 surface_force_reload(iface);
284 void surface_set_texture_target(IWineD3DSurface *iface, GLenum target)
286 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
288 TRACE("(%p) : setting target %#x\n", This, target);
290 if (This->texture_target != target)
292 if (target == GL_TEXTURE_RECTANGLE_ARB)
294 This->Flags &= ~SFLAG_NORMCOORD;
296 else if (This->texture_target == GL_TEXTURE_RECTANGLE_ARB)
298 This->Flags |= SFLAG_NORMCOORD;
301 This->texture_target = target;
302 surface_force_reload(iface);
305 /* Context activation is done by the caller. */
306 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This, BOOL srgb) {
307 DWORD active_sampler;
309 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
310 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
311 * gl states. The current texture unit should always be a valid one.
313 * To be more specific, this is tricky because we can implicitly be called
314 * from sampler() in state.c. This means we can't touch anything other than
315 * whatever happens to be the currently active texture, or we would risk
316 * marking already applied sampler states dirty again.
318 * TODO: Track the current active texture per GL context instead of using glGet
320 GLint active_texture;
322 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
324 active_sampler = This->resource.wineD3DDevice->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
326 if (active_sampler != WINED3D_UNMAPPED_STAGE)
328 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(active_sampler));
330 IWineD3DSurface_BindTexture((IWineD3DSurface *)This, srgb);
333 /* This function checks if the primary render target uses the 8bit paletted format. */
334 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
336 if (device->render_targets && device->render_targets[0]) {
337 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
338 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
339 && (render_target->resource.format_desc->format == WINED3DFMT_P8))
345 #undef GLINFO_LOCATION
347 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
349 /* This call just downloads data, the caller is responsible for binding the
350 * correct texture. */
351 /* Context activation is done by the caller. */
352 static void surface_download_data(IWineD3DSurfaceImpl *This) {
353 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
355 /* Only support read back of converted P8 surfaces */
356 if (This->Flags & SFLAG_CONVERTED && format_desc->format != WINED3DFMT_P8)
358 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(format_desc->format));
364 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
366 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
367 This, This->texture_level, format_desc->glFormat, format_desc->glType,
368 This->resource.allocatedMemory);
370 if (This->Flags & SFLAG_PBO)
372 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
373 checkGLcall("glBindBufferARB");
374 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target, This->texture_level, NULL));
375 checkGLcall("glGetCompressedTexImageARB");
376 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
377 checkGLcall("glBindBufferARB");
381 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target,
382 This->texture_level, This->resource.allocatedMemory));
383 checkGLcall("glGetCompressedTexImageARB");
389 GLenum format = format_desc->glFormat;
390 GLenum type = format_desc->glType;
394 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
395 if (format_desc->format == WINED3DFMT_P8 && primary_render_target_is_p8(This->resource.wineD3DDevice))
398 type = GL_UNSIGNED_BYTE;
401 if (This->Flags & SFLAG_NONPOW2) {
402 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
403 src_pitch = format_desc->byte_count * This->pow2Width;
404 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
405 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
406 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
408 mem = This->resource.allocatedMemory;
411 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
412 This, This->texture_level, format, type, mem);
414 if(This->Flags & SFLAG_PBO) {
415 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
416 checkGLcall("glBindBufferARB");
418 glGetTexImage(This->texture_target, This->texture_level, format, type, NULL);
419 checkGLcall("glGetTexImage");
421 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
422 checkGLcall("glBindBufferARB");
424 glGetTexImage(This->texture_target, This->texture_level, format, type, mem);
425 checkGLcall("glGetTexImage");
429 if (This->Flags & SFLAG_NONPOW2) {
430 const BYTE *src_data;
434 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
435 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
436 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
438 * We're doing this...
440 * instead of boxing the texture :
441 * |<-texture width ->| -->pow2width| /\
442 * |111111111111111111| | |
443 * |222 Texture 222222| boxed empty | texture height
444 * |3333 Data 33333333| | |
445 * |444444444444444444| | \/
446 * ----------------------------------- |
447 * | boxed empty | boxed empty | pow2height
449 * -----------------------------------
452 * we're repacking the data to the expected texture width
454 * |<-texture width ->| -->pow2width| /\
455 * |111111111111111111222222222222222| |
456 * |222333333333333333333444444444444| texture height
460 * | empty | pow2height
462 * -----------------------------------
466 * |<-texture width ->| /\
467 * |111111111111111111|
468 * |222222222222222222|texture height
469 * |333333333333333333|
470 * |444444444444444444| \/
471 * --------------------
473 * this also means that any references to allocatedMemory should work with the data as if were a
474 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
476 * internally the texture is still stored in a boxed format so any references to textureName will
477 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
479 * Performance should not be an issue, because applications normally do not lock the surfaces when
480 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
481 * and doesn't have to be re-read.
484 dst_data = This->resource.allocatedMemory;
485 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
486 for (y = 1 ; y < This->currentDesc.Height; y++) {
487 /* skip the first row */
488 src_data += src_pitch;
489 dst_data += dst_pitch;
490 memcpy(dst_data, src_data, dst_pitch);
493 HeapFree(GetProcessHeap(), 0, mem);
497 /* Surface has now been downloaded */
498 This->Flags |= SFLAG_INSYSMEM;
501 /* This call just uploads data, the caller is responsible for binding the
502 * correct texture. */
503 /* Context activation is done by the caller. */
504 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
505 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
507 TRACE("This %p, internal %#x, width %d, height %d, format %#x, type %#x, data %p.\n",
508 This, internal, width, height, format, type, data);
509 TRACE("target %#x, level %u, resource size %u.\n",
510 This->texture_target, This->texture_level, This->resource.size);
512 if (format_desc->heightscale != 1.0f && format_desc->heightscale != 0.0f) height *= format_desc->heightscale;
516 if (This->Flags & SFLAG_PBO)
518 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
519 checkGLcall("glBindBufferARB");
521 TRACE("(%p) pbo: %#x, data: %p.\n", This, This->pbo, data);
525 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
527 TRACE("Calling glCompressedTexSubImage2DARB.\n");
529 GL_EXTCALL(glCompressedTexSubImage2DARB(This->texture_target, This->texture_level,
530 0, 0, width, height, internal, This->resource.size, data));
531 checkGLcall("glCompressedTexSubImage2DARB");
535 TRACE("Calling glTexSubImage2D.\n");
537 glTexSubImage2D(This->texture_target, This->texture_level,
538 0, 0, width, height, format, type, data);
539 checkGLcall("glTexSubImage2D");
542 if (This->Flags & SFLAG_PBO)
544 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
545 checkGLcall("glBindBufferARB");
551 /* This call just allocates the texture, the caller is responsible for binding
552 * the correct texture. */
553 /* Context activation is done by the caller. */
554 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
555 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
556 BOOL enable_client_storage = FALSE;
557 const BYTE *mem = NULL;
559 if (format_desc->heightscale != 1.0f && format_desc->heightscale != 0.0f) height *= format_desc->heightscale;
561 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",
562 This, This->texture_target, This->texture_level, debug_d3dformat(format_desc->format),
563 internal, width, height, format, type);
567 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
568 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
569 /* In some cases we want to disable client storage.
570 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
571 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
572 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
573 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
574 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
576 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
577 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
578 This->Flags &= ~SFLAG_CLIENT;
579 enable_client_storage = TRUE;
581 This->Flags |= SFLAG_CLIENT;
583 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
584 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
586 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
590 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED && mem)
592 GL_EXTCALL(glCompressedTexImage2DARB(This->texture_target, This->texture_level,
593 internal, width, height, 0, This->resource.size, mem));
597 glTexImage2D(This->texture_target, This->texture_level,
598 internal, width, height, 0, format, type, mem);
599 checkGLcall("glTexImage2D");
602 if(enable_client_storage) {
603 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
604 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
609 /* In D3D the depth stencil dimensions have to be greater than or equal to the
610 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
611 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
612 /* GL locking is done by the caller */
613 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
614 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
615 renderbuffer_entry_t *entry;
616 GLuint renderbuffer = 0;
617 unsigned int src_width, src_height;
619 src_width = This->pow2Width;
620 src_height = This->pow2Height;
622 /* A depth stencil smaller than the render target is not valid */
623 if (width > src_width || height > src_height) return;
625 /* Remove any renderbuffer set if the sizes match */
626 if (width == src_width && height == src_height) {
627 This->current_renderbuffer = NULL;
631 /* Look if we've already got a renderbuffer of the correct dimensions */
632 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
633 if (entry->width == width && entry->height == height) {
634 renderbuffer = entry->id;
635 This->current_renderbuffer = entry;
641 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
642 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
643 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
644 This->resource.format_desc->glInternal, width, height));
646 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
647 entry->width = width;
648 entry->height = height;
649 entry->id = renderbuffer;
650 list_add_head(&This->renderbuffers, &entry->entry);
652 This->current_renderbuffer = entry;
655 checkGLcall("set_compatible_renderbuffer");
658 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
659 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
660 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
662 TRACE("(%p) : swapchain %p\n", This, swapchain);
664 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
665 TRACE("Returning GL_BACK\n");
667 } else if (swapchain_impl->frontBuffer == iface) {
668 TRACE("Returning GL_FRONT\n");
672 FIXME("Higher back buffer, returning GL_BACK\n");
676 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
677 void surface_add_dirty_rect(IWineD3DSurface *iface, const RECT *dirty_rect)
679 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
680 IWineD3DBaseTexture *baseTexture = NULL;
682 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
683 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
685 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
688 This->dirtyRect.left = min(This->dirtyRect.left, dirty_rect->left);
689 This->dirtyRect.top = min(This->dirtyRect.top, dirty_rect->top);
690 This->dirtyRect.right = max(This->dirtyRect.right, dirty_rect->right);
691 This->dirtyRect.bottom = max(This->dirtyRect.bottom, dirty_rect->bottom);
695 This->dirtyRect.left = 0;
696 This->dirtyRect.top = 0;
697 This->dirtyRect.right = This->currentDesc.Width;
698 This->dirtyRect.bottom = This->currentDesc.Height;
701 TRACE("(%p) : Dirty: yes, Rect:(%d, %d, %d, %d)\n", This, This->dirtyRect.left,
702 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
704 /* if the container is a basetexture then mark it dirty. */
705 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture)))
707 TRACE("Passing to container\n");
708 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
709 IWineD3DBaseTexture_Release(baseTexture);
713 static inline BOOL surface_can_stretch_rect(IWineD3DSurfaceImpl *src, IWineD3DSurfaceImpl *dst)
715 return ((src->resource.format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE)
716 || (src->resource.usage & WINED3DUSAGE_RENDERTARGET))
717 && ((dst->resource.format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE)
718 || (dst->resource.usage & WINED3DUSAGE_RENDERTARGET))
719 && (src->resource.format_desc->format == dst->resource.format_desc->format
720 || (is_identity_fixup(src->resource.format_desc->color_fixup)
721 && is_identity_fixup(dst->resource.format_desc->color_fixup)));
724 static ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface)
726 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
727 ULONG ref = InterlockedDecrement(&This->resource.ref);
728 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
732 surface_cleanup(This);
733 This->parent_ops->wined3d_object_destroyed(This->resource.parent);
735 TRACE("(%p) Released.\n", This);
736 HeapFree(GetProcessHeap(), 0, This);
742 /* ****************************************************
743 IWineD3DSurface IWineD3DResource parts follow
744 **************************************************** */
746 void surface_internal_preload(IWineD3DSurface *iface, enum WINED3DSRGB srgb)
748 /* TODO: check for locks */
749 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
750 IWineD3DBaseTexture *baseTexture = NULL;
751 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
753 TRACE("(%p)Checking to see if the container is a base texture\n", This);
754 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
755 IWineD3DBaseTextureImpl *tex_impl = (IWineD3DBaseTextureImpl *) baseTexture;
756 TRACE("Passing to container\n");
757 tex_impl->baseTexture.internal_preload(baseTexture, srgb);
758 IWineD3DBaseTexture_Release(baseTexture);
760 TRACE("(%p) : About to load surface\n", This);
762 if(!device->isInDraw) {
763 ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
766 if (This->resource.format_desc->format == WINED3DFMT_P8
767 || This->resource.format_desc->format == WINED3DFMT_A8P8)
769 if(palette9_changed(This)) {
770 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
771 /* TODO: This is not necessarily needed with hw palettized texture support */
772 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
773 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
774 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
778 IWineD3DSurface_LoadTexture(iface, srgb == SRGB_SRGB ? TRUE : FALSE);
780 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
781 /* Tell opengl to try and keep this texture in video ram (well mostly) */
785 glPrioritizeTextures(1, &This->texture_name, &tmp);
792 static void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
793 surface_internal_preload(iface, SRGB_ANY);
796 /* Context activation is done by the caller. */
797 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
798 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
799 This->resource.allocatedMemory =
800 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
803 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
804 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
805 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
806 checkGLcall("glGetBufferSubDataARB");
807 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
808 checkGLcall("glDeleteBuffersARB");
812 This->Flags &= ~SFLAG_PBO;
815 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
816 IWineD3DBaseTexture *texture = NULL;
817 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
818 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
819 renderbuffer_entry_t *entry, *entry2;
820 TRACE("(%p)\n", iface);
822 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
823 /* Default pool resources are supposed to be destroyed before Reset is called.
824 * Implicit resources stay however. So this means we have an implicit render target
825 * or depth stencil. The content may be destroyed, but we still have to tear down
826 * opengl resources, so we cannot leave early.
828 * Put the most up to date surface location into the drawable. D3D-wise this content
829 * is undefined, so it would be nowhere, but that would make the location management
830 * more complicated. The drawable is a sane location, because if we mark sysmem or
831 * texture up to date, drawPrim will copy the uninitialized texture or sysmem to the
832 * uninitialized drawable. That's pointless and we'd have to allocate the texture /
835 if (This->resource.usage & WINED3DUSAGE_DEPTHSTENCIL) {
836 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
838 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, TRUE);
841 /* Load the surface into system memory */
842 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
843 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
845 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
846 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSRGBTEX, FALSE);
847 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
849 ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
851 /* Destroy PBOs, but load them into real sysmem before */
852 if(This->Flags & SFLAG_PBO) {
853 surface_remove_pbo(This);
856 /* Destroy fbo render buffers. This is needed for implicit render targets, for
857 * all application-created targets the application has to release the surface
858 * before calling _Reset
860 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
862 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
864 list_remove(&entry->entry);
865 HeapFree(GetProcessHeap(), 0, entry);
867 list_init(&This->renderbuffers);
868 This->current_renderbuffer = NULL;
870 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
873 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
876 glDeleteTextures(1, &This->texture_name);
877 This->texture_name = 0;
878 glDeleteTextures(1, &This->texture_name_srgb);
879 This->texture_name_srgb = 0;
882 IWineD3DBaseTexture_Release(texture);
887 /* ******************************************************
888 IWineD3DSurface IWineD3DSurface parts follow
889 ****************************************************** */
891 /* Read the framebuffer back into the surface */
892 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
893 IWineD3DSwapChainImpl *swapchain;
894 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
898 BYTE *row, *top, *bottom;
902 BOOL srcIsUpsideDown;
907 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
908 static BOOL warned = FALSE;
910 ERR("The application tries to lock the render target, but render target locking is disabled\n");
916 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
917 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
918 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
919 * context->last_was_blit set on the unlock.
921 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
924 /* Select the correct read buffer, and give some debug output.
925 * There is no need to keep track of the current read buffer or reset it, every part of the code
926 * that reads sets the read buffer as desired.
928 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
930 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
931 TRACE("Locking %#x buffer\n", buffer);
932 glReadBuffer(buffer);
933 checkGLcall("glReadBuffer");
935 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
936 srcIsUpsideDown = FALSE;
938 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
939 * Read from the back buffer
941 TRACE("Locking offscreen render target\n");
942 glReadBuffer(myDevice->offscreenBuffer);
943 srcIsUpsideDown = TRUE;
946 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
950 local_rect.right = This->currentDesc.Width;
951 local_rect.bottom = This->currentDesc.Height;
955 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
957 switch(This->resource.format_desc->format)
961 if(primary_render_target_is_p8(myDevice)) {
962 /* In case of P8 render targets the index is stored in the alpha component */
964 type = GL_UNSIGNED_BYTE;
966 bpp = This->resource.format_desc->byte_count;
968 /* GL can't return palettized data, so read ARGB pixels into a
969 * separate block of memory and convert them into palettized format
970 * in software. Slow, but if the app means to use palettized render
971 * targets and locks it...
973 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
974 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
975 * for the color channels when palettizing the colors.
978 type = GL_UNSIGNED_BYTE;
980 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
982 ERR("Out of memory\n");
986 bpp = This->resource.format_desc->byte_count * 3;
993 fmt = This->resource.format_desc->glFormat;
994 type = This->resource.format_desc->glType;
995 bpp = This->resource.format_desc->byte_count;
998 if(This->Flags & SFLAG_PBO) {
999 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
1000 checkGLcall("glBindBufferARB");
1002 ERR("mem not null for pbo -- unexpected\n");
1007 /* Save old pixel store pack state */
1008 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
1009 checkGLcall("glGetIntegerv");
1010 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
1011 checkGLcall("glGetIntegerv");
1012 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
1013 checkGLcall("glGetIntegerv");
1015 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1016 glPixelStorei(GL_PACK_ROW_LENGTH, This->currentDesc.Width);
1017 checkGLcall("glPixelStorei");
1018 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
1019 checkGLcall("glPixelStorei");
1020 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
1021 checkGLcall("glPixelStorei");
1023 glReadPixels(local_rect.left, (!srcIsUpsideDown) ? (This->currentDesc.Height - local_rect.bottom) : local_rect.top ,
1024 local_rect.right - local_rect.left,
1025 local_rect.bottom - local_rect.top,
1027 checkGLcall("glReadPixels");
1029 /* Reset previous pixel store pack state */
1030 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
1031 checkGLcall("glPixelStorei");
1032 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
1033 checkGLcall("glPixelStorei");
1034 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
1035 checkGLcall("glPixelStorei");
1037 if(This->Flags & SFLAG_PBO) {
1038 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1039 checkGLcall("glBindBufferARB");
1041 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
1042 * to get a pointer to it and perform the flipping in software. This is a lot
1043 * faster than calling glReadPixels for each line. In case we want more speed
1044 * we should rerender it flipped in a FBO and read the data back from the FBO. */
1045 if(!srcIsUpsideDown) {
1046 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1047 checkGLcall("glBindBufferARB");
1049 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1050 checkGLcall("glMapBufferARB");
1054 /* TODO: Merge this with the palettization loop below for P8 targets */
1055 if(!srcIsUpsideDown) {
1057 /* glReadPixels returns the image upside down, and there is no way to prevent this.
1058 Flip the lines in software */
1059 len = (local_rect.right - local_rect.left) * bpp;
1060 off = local_rect.left * bpp;
1062 row = HeapAlloc(GetProcessHeap(), 0, len);
1064 ERR("Out of memory\n");
1065 if (This->resource.format_desc->format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
1070 top = mem + pitch * local_rect.top;
1071 bottom = mem + pitch * (local_rect.bottom - 1);
1072 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
1073 memcpy(row, top + off, len);
1074 memcpy(top + off, bottom + off, len);
1075 memcpy(bottom + off, row, len);
1079 HeapFree(GetProcessHeap(), 0, row);
1081 /* Unmap the temp PBO buffer */
1082 if(This->Flags & SFLAG_PBO) {
1083 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1084 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1090 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
1091 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
1092 * the same color but we have no choice.
1093 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
1095 if ((This->resource.format_desc->format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice))
1097 const PALETTEENTRY *pal = NULL;
1098 DWORD width = pitch / 3;
1102 pal = This->palette->palents;
1104 ERR("Palette is missing, cannot perform inverse palette lookup\n");
1105 HeapFree(GetProcessHeap(), 0, mem);
1109 for(y = local_rect.top; y < local_rect.bottom; y++) {
1110 for(x = local_rect.left; x < local_rect.right; x++) {
1111 /* start lines pixels */
1112 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
1113 const BYTE *green = blue + 1;
1114 const BYTE *red = green + 1;
1116 for(c = 0; c < 256; c++) {
1117 if(*red == pal[c].peRed &&
1118 *green == pal[c].peGreen &&
1119 *blue == pal[c].peBlue)
1121 *((BYTE *) dest + y * width + x) = c;
1127 HeapFree(GetProcessHeap(), 0, mem);
1131 /* Read the framebuffer contents into a texture */
1132 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This, BOOL srgb)
1134 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1135 IWineD3DSwapChainImpl *swapchain;
1137 GLenum format, internal, type;
1138 CONVERT_TYPES convert;
1140 BOOL alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
1142 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
1144 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
1145 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
1146 * states in the stateblock, and no driver was found yet that had bugs in that regard.
1148 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
1149 surface_bind_and_dirtify(This, srgb);
1152 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1155 /* Select the correct read buffer, and give some debug output.
1156 * There is no need to keep track of the current read buffer or reset it, every part of the code
1157 * that reads sets the read buffer as desired.
1159 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
1161 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1162 TRACE("Locking %#x buffer\n", buffer);
1165 glReadBuffer(buffer);
1166 checkGLcall("glReadBuffer");
1169 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1171 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1172 * Read from the back buffer
1174 TRACE("Locking offscreen render target\n");
1176 glReadBuffer(device->offscreenBuffer);
1177 checkGLcall("glReadBuffer");
1181 if(!(This->Flags & alloc_flag)) {
1182 surface_allocate_surface(This, internal, This->pow2Width,
1183 This->pow2Height, format, type);
1184 This->Flags |= alloc_flag;
1188 /* If !SrcIsUpsideDown we should flip the surface.
1189 * This can be done using glCopyTexSubImage2D but this
1190 * is VERY slow, so don't do that. We should prevent
1191 * this code from getting called in such cases or perhaps
1192 * we can use FBOs */
1194 glCopyTexSubImage2D(This->texture_target, This->texture_level,
1195 0, 0, 0, 0, This->currentDesc.Width, This->currentDesc.Height);
1196 checkGLcall("glCopyTexSubImage2D");
1198 glReadBuffer(prevRead);
1199 checkGLcall("glReadBuffer");
1202 TRACE("Updated target %d\n", This->texture_target);
1205 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
1206 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
1207 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
1210 if(!(This->Flags & SFLAG_DYNLOCK)) {
1212 /* MAXLOCKCOUNT is defined in wined3d_private.h */
1213 if(This->lockCount > MAXLOCKCOUNT) {
1214 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
1215 This->Flags |= SFLAG_DYNLOCK;
1219 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
1220 * Also don't create a PBO for systemmem surfaces.
1222 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
1224 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1226 ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
1229 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
1230 error = glGetError();
1231 if(This->pbo == 0 || error != GL_NO_ERROR) {
1232 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
1235 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
1237 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1238 checkGLcall("glBindBufferARB");
1240 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1241 checkGLcall("glBufferDataARB");
1243 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1244 checkGLcall("glBindBufferARB");
1246 /* We don't need the system memory anymore and we can't even use it for PBOs */
1247 if(!(This->Flags & SFLAG_CLIENT)) {
1248 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1249 This->resource.heapMemory = NULL;
1251 This->resource.allocatedMemory = NULL;
1252 This->Flags |= SFLAG_PBO;
1254 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
1255 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1258 if(!This->resource.heapMemory) {
1259 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1261 This->resource.allocatedMemory =
1262 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1263 if(This->Flags & SFLAG_INSYSMEM) {
1264 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1269 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1270 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1271 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1272 const RECT *pass_rect = pRect;
1274 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1276 /* This is also done in the base class, but we have to verify this before loading any data from
1277 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1278 * may interfere, and all other bad things may happen
1280 if (This->Flags & SFLAG_LOCKED) {
1281 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1282 return WINED3DERR_INVALIDCALL;
1284 This->Flags |= SFLAG_LOCKED;
1286 if (!(This->Flags & SFLAG_LOCKABLE))
1288 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1291 if (Flags & WINED3DLOCK_DISCARD) {
1292 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1293 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1294 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1295 This->Flags |= SFLAG_INSYSMEM;
1299 if (This->Flags & SFLAG_INSYSMEM) {
1300 TRACE("Local copy is up to date, not downloading data\n");
1301 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1305 /* IWineD3DSurface_LoadLocation() does not check if the rectangle specifies
1306 * the full surface. Most callers don't need that, so do it here. */
1307 if (pRect && pRect->top == 0 && pRect->left == 0
1308 && pRect->right == This->currentDesc.Width
1309 && pRect->bottom == This->currentDesc.Height)
1314 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
1315 && ((This->Flags & SFLAG_SWAPCHAIN) || iface == myDevice->render_targets[0])))
1317 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1321 if(This->Flags & SFLAG_PBO) {
1322 ActivateContext(myDevice, NULL, CTXUSAGE_RESOURCELOAD);
1324 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1325 checkGLcall("glBindBufferARB");
1327 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1328 if(This->resource.allocatedMemory) {
1329 ERR("The surface already has PBO memory allocated!\n");
1332 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1333 checkGLcall("glMapBufferARB");
1335 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1336 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1337 checkGLcall("glBindBufferARB");
1342 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1345 IWineD3DBaseTexture *pBaseTexture;
1348 * as seen in msdn docs
1350 surface_add_dirty_rect(iface, pRect);
1352 /** Dirtify Container if needed */
1353 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1354 TRACE("Making container dirty\n");
1355 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1356 IWineD3DBaseTexture_Release(pBaseTexture);
1358 TRACE("Surface is standalone, no need to dirty the container\n");
1362 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1365 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1367 GLint prev_rasterpos[4];
1368 GLint skipBytes = 0;
1369 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1370 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1371 IWineD3DSwapChainImpl *swapchain;
1373 /* Activate the correct context for the render target */
1374 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1377 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
1378 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1379 TRACE("Unlocking %#x buffer\n", buffer);
1380 glDrawBuffer(buffer);
1381 checkGLcall("glDrawBuffer");
1383 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1385 /* Primary offscreen render target */
1386 TRACE("Offscreen render target\n");
1387 glDrawBuffer(myDevice->offscreenBuffer);
1388 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1391 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1392 checkGLcall("glGetIntegerv");
1393 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1394 checkGLcall("glGetIntegerv");
1395 glPixelZoom(1.0f, -1.0f);
1396 checkGLcall("glPixelZoom");
1398 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1399 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1400 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1402 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1403 checkGLcall("glRasterPos3i");
1405 /* Some drivers(radeon dri, others?) don't like exceptions during
1406 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1407 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1408 * catch to put the dib section in InSync mode, which leads to a crash
1409 * and a blocked x server on my radeon card.
1411 * The following lines read the dib section so it is put in InSync mode
1412 * before glDrawPixels is called and the crash is prevented. There won't
1413 * be any interfering gdi accesses, because UnlockRect is called from
1414 * ReleaseDC, and the app won't use the dc any more afterwards.
1416 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1418 read = This->resource.allocatedMemory[0];
1421 if(This->Flags & SFLAG_PBO) {
1422 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1423 checkGLcall("glBindBufferARB");
1426 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1427 if(This->Flags & SFLAG_LOCKED) {
1428 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1429 (This->lockedRect.bottom - This->lockedRect.top)-1,
1431 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1432 checkGLcall("glDrawPixels");
1434 glDrawPixels(This->currentDesc.Width,
1435 This->currentDesc.Height,
1437 checkGLcall("glDrawPixels");
1440 if(This->Flags & SFLAG_PBO) {
1441 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1442 checkGLcall("glBindBufferARB");
1445 glPixelZoom(1.0f, 1.0f);
1446 checkGLcall("glPixelZoom");
1448 glRasterPos3iv(&prev_rasterpos[0]);
1449 checkGLcall("glRasterPos3iv");
1451 /* Reset to previous pack row length */
1452 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1453 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH)");
1456 glDrawBuffer(myDevice->offscreenBuffer);
1457 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1458 } else if(swapchain->backBuffer) {
1459 glDrawBuffer(GL_BACK);
1460 checkGLcall("glDrawBuffer(GL_BACK)");
1462 glDrawBuffer(GL_FRONT);
1463 checkGLcall("glDrawBuffer(GL_FRONT)");
1470 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1471 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1472 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1475 if (!(This->Flags & SFLAG_LOCKED)) {
1476 WARN("trying to Unlock an unlocked surf@%p\n", This);
1477 return WINEDDERR_NOTLOCKED;
1480 if (This->Flags & SFLAG_PBO) {
1481 TRACE("Freeing PBO memory\n");
1482 ActivateContext(myDevice, NULL, CTXUSAGE_RESOURCELOAD);
1484 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1485 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1486 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1487 checkGLcall("glUnmapBufferARB");
1489 This->resource.allocatedMemory = NULL;
1492 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1494 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1495 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1499 if ((This->Flags & SFLAG_SWAPCHAIN) || (myDevice->render_targets && iface == myDevice->render_targets[0]))
1501 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1502 static BOOL warned = FALSE;
1504 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1510 if(This->dirtyRect.left == 0 &&
1511 This->dirtyRect.top == 0 &&
1512 This->dirtyRect.right == This->currentDesc.Width &&
1513 This->dirtyRect.bottom == This->currentDesc.Height) {
1516 /* TODO: Proper partial rectangle tracking */
1517 fullsurface = FALSE;
1518 This->Flags |= SFLAG_INSYSMEM;
1521 switch(wined3d_settings.rendertargetlock_mode) {
1523 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1527 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1532 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1533 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1534 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1535 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1536 * not fully up to date because only a subrectangle was read in LockRect.
1538 This->Flags &= ~SFLAG_INSYSMEM;
1539 This->Flags |= SFLAG_INDRAWABLE;
1542 This->dirtyRect.left = This->currentDesc.Width;
1543 This->dirtyRect.top = This->currentDesc.Height;
1544 This->dirtyRect.right = 0;
1545 This->dirtyRect.bottom = 0;
1546 } else if(iface == myDevice->stencilBufferTarget) {
1547 FIXME("Depth Stencil buffer locking is not implemented\n");
1549 /* The rest should be a normal texture */
1550 IWineD3DBaseTextureImpl *impl;
1551 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1552 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1553 * states need resetting
1555 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1556 if(impl->baseTexture.bindCount) {
1557 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1559 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1564 This->Flags &= ~SFLAG_LOCKED;
1565 memset(&This->lockedRect, 0, sizeof(RECT));
1567 /* Overlays have to be redrawn manually after changes with the GL implementation */
1568 if(This->overlay_dest) {
1569 IWineD3DSurface_DrawOverlay(iface);
1574 static HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC)
1576 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1577 WINED3DLOCKED_RECT lock;
1581 TRACE("(%p)->(%p)\n",This,pHDC);
1583 if(This->Flags & SFLAG_USERPTR) {
1584 ERR("Not supported on surfaces with an application-provided surfaces\n");
1585 return WINEDDERR_NODC;
1588 /* Give more detailed info for ddraw */
1589 if (This->Flags & SFLAG_DCINUSE)
1590 return WINEDDERR_DCALREADYCREATED;
1592 /* Can't GetDC if the surface is locked */
1593 if (This->Flags & SFLAG_LOCKED)
1594 return WINED3DERR_INVALIDCALL;
1596 memset(&lock, 0, sizeof(lock)); /* To be sure */
1598 /* Create a DIB section if there isn't a hdc yet */
1600 hr = IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1601 if(FAILED(hr)) return WINED3DERR_INVALIDCALL;
1602 if(This->Flags & SFLAG_CLIENT) {
1603 surface_internal_preload(iface, SRGB_RGB);
1606 /* Use the dib section from now on if we are not using a PBO */
1607 if(!(This->Flags & SFLAG_PBO))
1608 This->resource.allocatedMemory = This->dib.bitmap_data;
1611 /* Lock the surface */
1612 hr = IWineD3DSurface_LockRect(iface,
1617 if(This->Flags & SFLAG_PBO) {
1618 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1619 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1623 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1624 /* keep the dib section */
1628 if (This->resource.format_desc->format == WINED3DFMT_P8
1629 || This->resource.format_desc->format == WINED3DFMT_A8P8)
1631 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1632 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1634 const PALETTEENTRY *pal = NULL;
1637 pal = This->palette->palents;
1639 IWineD3DSurfaceImpl *dds_primary;
1640 IWineD3DSwapChainImpl *swapchain;
1641 swapchain = (IWineD3DSwapChainImpl *)This->resource.wineD3DDevice->swapchains[0];
1642 dds_primary = (IWineD3DSurfaceImpl *)swapchain->frontBuffer;
1643 if (dds_primary && dds_primary->palette)
1644 pal = dds_primary->palette->palents;
1648 for (n=0; n<256; n++) {
1649 col[n].rgbRed = pal[n].peRed;
1650 col[n].rgbGreen = pal[n].peGreen;
1651 col[n].rgbBlue = pal[n].peBlue;
1652 col[n].rgbReserved = 0;
1654 SetDIBColorTable(This->hDC, 0, 256, col);
1659 TRACE("returning %p\n",*pHDC);
1660 This->Flags |= SFLAG_DCINUSE;
1665 static HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC)
1667 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1669 TRACE("(%p)->(%p)\n",This,hDC);
1671 if (!(This->Flags & SFLAG_DCINUSE))
1672 return WINEDDERR_NODC;
1674 if (This->hDC !=hDC) {
1675 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1676 return WINEDDERR_NODC;
1679 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1680 /* Copy the contents of the DIB over to the PBO */
1681 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1684 /* we locked first, so unlock now */
1685 IWineD3DSurface_UnlockRect(iface);
1687 This->Flags &= ~SFLAG_DCINUSE;
1692 /* ******************************************************
1693 IWineD3DSurface Internal (No mapping to directx api) parts follow
1694 ****************************************************** */
1696 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) {
1697 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1698 const struct GlPixelFormatDesc *glDesc = This->resource.format_desc;
1699 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1701 /* Default values: From the surface */
1702 *format = glDesc->glFormat;
1703 *type = glDesc->glType;
1704 *convert = NO_CONVERSION;
1705 *target_bpp = glDesc->byte_count;
1708 *internal = glDesc->glGammaInternal;
1710 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET
1711 && !(This->Flags & SFLAG_SWAPCHAIN))
1713 *internal = glDesc->rtInternal;
1715 *internal = glDesc->glInternal;
1718 /* Ok, now look if we have to do any conversion */
1719 switch(This->resource.format_desc->format)
1726 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1727 * of the two is available make sure texturing is requested as neither of the two works in
1728 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1729 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1730 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1731 * conflicts with this.
1733 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) ||
1734 (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) &&
1735 device->render_targets &&
1736 This == (IWineD3DSurfaceImpl*)device->render_targets[0])) ||
1737 colorkey_active || !use_texturing ) {
1739 *internal = GL_RGBA;
1740 *type = GL_UNSIGNED_BYTE;
1742 if(colorkey_active) {
1743 *convert = CONVERT_PALETTED_CK;
1745 *convert = CONVERT_PALETTED;
1748 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1750 *type = GL_UNSIGNED_BYTE;
1756 case WINED3DFMT_R3G3B2:
1757 /* **********************
1758 GL_UNSIGNED_BYTE_3_3_2
1759 ********************** */
1760 if (colorkey_active) {
1761 /* This texture format will never be used.. So do not care about color keying
1762 up until the point in time it will be needed :-) */
1763 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1767 case WINED3DFMT_R5G6B5:
1768 if (colorkey_active) {
1769 *convert = CONVERT_CK_565;
1771 *internal = GL_RGB5_A1;
1772 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1776 case WINED3DFMT_X1R5G5B5:
1777 if (colorkey_active) {
1778 *convert = CONVERT_CK_5551;
1780 *internal = GL_RGB5_A1;
1781 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1785 case WINED3DFMT_R8G8B8:
1786 if (colorkey_active) {
1787 *convert = CONVERT_CK_RGB24;
1789 *internal = GL_RGBA8;
1790 *type = GL_UNSIGNED_INT_8_8_8_8;
1795 case WINED3DFMT_X8R8G8B8:
1796 if (colorkey_active) {
1797 *convert = CONVERT_RGB32_888;
1799 *internal = GL_RGBA8;
1800 *type = GL_UNSIGNED_INT_8_8_8_8;
1804 case WINED3DFMT_R8G8_SNORM:
1805 if (GL_SUPPORT(NV_TEXTURE_SHADER)) break;
1806 *convert = CONVERT_V8U8;
1808 *type = GL_UNSIGNED_BYTE;
1812 case WINED3DFMT_L6V5U5:
1813 *convert = CONVERT_L6V5U5;
1814 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1816 /* Use format and types from table */
1818 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1821 *type = GL_UNSIGNED_SHORT_5_6_5;
1825 case WINED3DFMT_X8L8V8U8:
1826 *convert = CONVERT_X8L8V8U8;
1828 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1829 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1830 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1831 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1832 * the needed type and format parameter, so the internal format contains a
1833 * 4th component, which is returned as alpha
1837 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1841 case WINED3DFMT_R8G8B8A8_SNORM:
1842 if (GL_SUPPORT(NV_TEXTURE_SHADER)) break;
1843 *convert = CONVERT_Q8W8V8U8;
1845 *type = GL_UNSIGNED_BYTE;
1849 case WINED3DFMT_R16G16_SNORM:
1850 if (GL_SUPPORT(NV_TEXTURE_SHADER)) break;
1851 *convert = CONVERT_V16U16;
1853 *type = GL_UNSIGNED_SHORT;
1857 case WINED3DFMT_A4L4:
1858 /* A4L4 exists as an internal gl format, but for some reason there is not
1859 * format+type combination to load it. Thus convert it to A8L8, then load it
1860 * with A4L4 internal, but A8L8 format+type
1862 *convert = CONVERT_A4L4;
1863 *format = GL_LUMINANCE_ALPHA;
1864 *type = GL_UNSIGNED_BYTE;
1868 case WINED3DFMT_R16G16_UNORM:
1869 *convert = CONVERT_G16R16;
1871 *type = GL_UNSIGNED_SHORT;
1875 case WINED3DFMT_R16G16_FLOAT:
1876 *convert = CONVERT_R16G16F;
1878 *type = GL_HALF_FLOAT_ARB;
1882 case WINED3DFMT_R32G32_FLOAT:
1883 *convert = CONVERT_R32G32F;
1889 case WINED3DFMT_D15S1:
1890 if (GL_SUPPORT(EXT_PACKED_DEPTH_STENCIL))
1892 *convert = CONVERT_D15S1;
1897 case WINED3DFMT_D24X4S4:
1898 if (GL_SUPPORT(EXT_PACKED_DEPTH_STENCIL))
1900 *convert = CONVERT_D24X4S4;
1904 case WINED3DFMT_D24FS8:
1905 if (GL_SUPPORT(ARB_DEPTH_BUFFER_FLOAT))
1907 *convert = CONVERT_D24FS8;
1919 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey)
1921 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1922 IWineD3DPaletteImpl *pal = This->palette;
1923 BOOL index_in_alpha = FALSE;
1926 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
1927 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
1928 * is slow. Further RGB->P8 conversion is not possible because palettes can have
1929 * duplicate entries. Store the color key in the unused alpha component to speed the
1930 * download up and to make conversion unneeded. */
1931 index_in_alpha = primary_render_target_is_p8(device);
1935 UINT dxVersion = ((IWineD3DImpl *)device->wineD3D)->dxVersion;
1937 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
1940 ERR("This code should never get entered for DirectDraw!, expect problems\n");
1943 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
1944 * there's no palette at this time. */
1945 for (i = 0; i < 256; i++) table[i][3] = i;
1950 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
1951 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
1952 * capability flag is present (wine does advertise this capability) */
1953 for (i = 0; i < 256; ++i)
1955 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1956 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1957 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1958 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
1964 TRACE("Using surface palette %p\n", pal);
1965 /* Get the surface's palette */
1966 for (i = 0; i < 256; ++i)
1968 table[i][0] = pal->palents[i].peRed;
1969 table[i][1] = pal->palents[i].peGreen;
1970 table[i][2] = pal->palents[i].peBlue;
1972 /* When index_in_alpha is set the palette index is stored in the
1973 * alpha component. In case of a readback we can then read
1974 * GL_ALPHA. Color keying is handled in BltOverride using a
1975 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
1976 * color key itself is passed to glAlphaFunc in other cases the
1977 * alpha component of pixels that should be masked away is set to 0. */
1982 else if (colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue)
1983 && (i <= This->SrcBltCKey.dwColorSpaceHighValue))
1987 else if(pal->Flags & WINEDDPCAPS_ALPHA)
1989 table[i][3] = pal->palents[i].peFlags;
1999 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
2000 UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This)
2004 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
2009 memcpy(dst, src, pitch * height);
2012 case CONVERT_PALETTED:
2013 case CONVERT_PALETTED_CK:
2015 IWineD3DPaletteImpl* pal = This->palette;
2020 /* TODO: If we are a sublevel, try to get the palette from level 0 */
2023 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2025 for (y = 0; y < height; y++)
2027 source = src + pitch * y;
2028 dest = dst + outpitch * y;
2029 /* This is an 1 bpp format, using the width here is fine */
2030 for (x = 0; x < width; x++) {
2031 BYTE color = *source++;
2032 *dest++ = table[color][0];
2033 *dest++ = table[color][1];
2034 *dest++ = table[color][2];
2035 *dest++ = table[color][3];
2041 case CONVERT_CK_565:
2043 /* Converting the 565 format in 5551 packed to emulate color-keying.
2045 Note : in all these conversion, it would be best to average the averaging
2046 pixels to get the color of the pixel that will be color-keyed to
2047 prevent 'color bleeding'. This will be done later on if ever it is
2050 Note2: Nvidia documents say that their driver does not support alpha + color keying
2051 on the same surface and disables color keying in such a case
2057 TRACE("Color keyed 565\n");
2059 for (y = 0; y < height; y++) {
2060 Source = (const WORD *)(src + y * pitch);
2061 Dest = (WORD *) (dst + y * outpitch);
2062 for (x = 0; x < width; x++ ) {
2063 WORD color = *Source++;
2064 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
2065 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2066 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2075 case CONVERT_CK_5551:
2077 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
2081 TRACE("Color keyed 5551\n");
2082 for (y = 0; y < height; y++) {
2083 Source = (const WORD *)(src + y * pitch);
2084 Dest = (WORD *) (dst + y * outpitch);
2085 for (x = 0; x < width; x++ ) {
2086 WORD color = *Source++;
2088 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2089 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2093 *Dest &= ~(1 << 15);
2101 case CONVERT_CK_RGB24:
2103 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
2105 for (y = 0; y < height; y++)
2107 source = src + pitch * y;
2108 dest = dst + outpitch * y;
2109 for (x = 0; x < width; x++) {
2110 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
2111 DWORD dstcolor = color << 8;
2112 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2113 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2116 *(DWORD*)dest = dstcolor;
2124 case CONVERT_RGB32_888:
2126 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
2128 for (y = 0; y < height; y++)
2130 source = src + pitch * y;
2131 dest = dst + outpitch * y;
2132 for (x = 0; x < width; x++) {
2133 DWORD color = 0xffffff & *(const DWORD*)source;
2134 DWORD dstcolor = color << 8;
2135 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2136 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2139 *(DWORD*)dest = dstcolor;
2150 const short *Source;
2151 unsigned char *Dest;
2152 for(y = 0; y < height; y++) {
2153 Source = (const short *)(src + y * pitch);
2154 Dest = dst + y * outpitch;
2155 for (x = 0; x < width; x++ ) {
2156 long color = (*Source++);
2157 /* B */ Dest[0] = 0xff;
2158 /* G */ Dest[1] = (color >> 8) + 128; /* V */
2159 /* R */ Dest[2] = (color) + 128; /* U */
2166 case CONVERT_V16U16:
2169 const DWORD *Source;
2170 unsigned short *Dest;
2171 for(y = 0; y < height; y++) {
2172 Source = (const DWORD *)(src + y * pitch);
2173 Dest = (unsigned short *) (dst + y * outpitch);
2174 for (x = 0; x < width; x++ ) {
2175 DWORD color = (*Source++);
2176 /* B */ Dest[0] = 0xffff;
2177 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
2178 /* R */ Dest[2] = (color ) + 32768; /* U */
2185 case CONVERT_Q8W8V8U8:
2188 const DWORD *Source;
2189 unsigned char *Dest;
2190 for(y = 0; y < height; y++) {
2191 Source = (const DWORD *)(src + y * pitch);
2192 Dest = dst + y * outpitch;
2193 for (x = 0; x < width; x++ ) {
2194 long color = (*Source++);
2195 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
2196 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2197 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2198 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
2205 case CONVERT_L6V5U5:
2209 unsigned char *Dest;
2211 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2212 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
2213 * fixed function and shaders without further conversion once the surface is
2216 for(y = 0; y < height; y++) {
2217 Source = (const WORD *)(src + y * pitch);
2218 Dest = dst + y * outpitch;
2219 for (x = 0; x < width; x++ ) {
2220 short color = (*Source++);
2221 unsigned char l = ((color >> 10) & 0xfc);
2222 char v = ((color >> 5) & 0x3e);
2223 char u = ((color ) & 0x1f);
2225 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
2226 * and doubles the positive range. Thus shift left only once, gl does the 2nd
2227 * shift. GL reads a signed value and converts it into an unsigned value.
2229 /* M */ Dest[2] = l << 1;
2231 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
2232 * from 5 bit values to 8 bit values.
2234 /* V */ Dest[1] = v << 3;
2235 /* U */ Dest[0] = u << 3;
2240 for(y = 0; y < height; y++) {
2241 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
2242 Source = (const WORD *)(src + y * pitch);
2243 for (x = 0; x < width; x++ ) {
2244 short color = (*Source++);
2245 unsigned char l = ((color >> 10) & 0xfc);
2246 short v = ((color >> 5) & 0x3e);
2247 short u = ((color ) & 0x1f);
2248 short v_conv = v + 16;
2249 short u_conv = u + 16;
2251 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
2259 case CONVERT_X8L8V8U8:
2262 const DWORD *Source;
2263 unsigned char *Dest;
2265 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2266 /* This implementation works with the fixed function pipeline and shaders
2267 * without further modification after converting the surface.
2269 for(y = 0; y < height; y++) {
2270 Source = (const DWORD *)(src + y * pitch);
2271 Dest = dst + y * outpitch;
2272 for (x = 0; x < width; x++ ) {
2273 long color = (*Source++);
2274 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
2275 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
2276 /* U */ Dest[0] = (color & 0xff); /* U */
2277 /* I */ Dest[3] = 255; /* X */
2282 /* Doesn't work correctly with the fixed function pipeline, but can work in
2283 * shaders if the shader is adjusted. (There's no use for this format in gl's
2284 * standard fixed function pipeline anyway).
2286 for(y = 0; y < height; y++) {
2287 Source = (const DWORD *)(src + y * pitch);
2288 Dest = dst + y * outpitch;
2289 for (x = 0; x < width; x++ ) {
2290 long color = (*Source++);
2291 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
2292 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2293 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2304 const unsigned char *Source;
2305 unsigned char *Dest;
2306 for(y = 0; y < height; y++) {
2307 Source = src + y * pitch;
2308 Dest = dst + y * outpitch;
2309 for (x = 0; x < width; x++ ) {
2310 unsigned char color = (*Source++);
2311 /* A */ Dest[1] = (color & 0xf0) << 0;
2312 /* L */ Dest[0] = (color & 0x0f) << 4;
2319 case CONVERT_G16R16:
2320 case CONVERT_R16G16F:
2326 for(y = 0; y < height; y++) {
2327 Source = (const WORD *)(src + y * pitch);
2328 Dest = (WORD *) (dst + y * outpitch);
2329 for (x = 0; x < width; x++ ) {
2330 WORD green = (*Source++);
2331 WORD red = (*Source++);
2334 /* Strictly speaking not correct for R16G16F, but it doesn't matter because the
2335 * shader overwrites it anyway
2344 case CONVERT_R32G32F:
2347 const float *Source;
2349 for(y = 0; y < height; y++) {
2350 Source = (const float *)(src + y * pitch);
2351 Dest = (float *) (dst + y * outpitch);
2352 for (x = 0; x < width; x++ ) {
2353 float green = (*Source++);
2354 float red = (*Source++);
2368 for (y = 0; y < height; ++y)
2370 const WORD *source = (const WORD *)(src + y * pitch);
2371 DWORD *dest = (DWORD *)(dst + y * outpitch);
2373 for (x = 0; x < width; ++x)
2375 /* The depth data is normalized, so needs to be scaled,
2376 * the stencil data isn't. Scale depth data by
2377 * (2^24-1)/(2^15-1) ~~ (2^9 + 2^-6). */
2378 WORD d15 = source[x] >> 1;
2379 DWORD d24 = (d15 << 9) + (d15 >> 6);
2380 dest[x] = (d24 << 8) | (source[x] & 0x1);
2386 case CONVERT_D24X4S4:
2390 for (y = 0; y < height; ++y)
2392 const DWORD *source = (const DWORD *)(src + y * pitch);
2393 DWORD *dest = (DWORD *)(dst + y * outpitch);
2395 for (x = 0; x < width; ++x)
2397 /* Just need to clear out the X4 part. */
2398 dest[x] = source[x] & ~0xf0;
2404 case CONVERT_D24FS8:
2408 for (y = 0; y < height; ++y)
2410 const DWORD *source = (const DWORD *)(src + y * pitch);
2411 float *dest_f = (float *)(dst + y * outpitch);
2412 DWORD *dest_s = (DWORD *)(dst + y * outpitch);
2414 for (x = 0; x < width; ++x)
2416 dest_f[x * 2] = float_24_to_32((source[x] & 0xffffff00) >> 8);
2417 dest_s[x * 2 + 1] = source[x] & 0xff;
2424 ERR("Unsupported conversion type %#x.\n", convert);
2429 /* This function is used in case of 8bit paletted textures to upload the palette.
2430 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2431 extensions like ATI_fragment_shaders is possible.
2433 /* Context activation is done by the caller. */
2434 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2435 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2437 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2439 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2441 /* Try to use the paletted texture extension */
2442 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2444 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2446 GL_EXTCALL(glColorTableEXT(This->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
2451 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2452 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2453 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2457 /* Create the fragment program if we don't have it */
2458 if(!device->paletteConversionShader)
2460 const char *fragment_palette_conversion =
2463 /* { 255/256, 0.5/255*255/256, 0, 0 } */
2464 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n"
2465 /* The alpha-component contains the palette index */
2466 "TEX index, fragment.texcoord[0], texture[0], 2D;\n"
2467 /* Scale the index by 255/256 and add a bias of '0.5' in order to sample in the middle */
2468 "MAD index.a, index.a, constants.x, constants.y;\n"
2469 /* Use the alpha-component as an index in the palette to get the final color */
2470 "TEX result.color, index.a, texture[1], 1D;\n"
2473 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2474 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2475 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2476 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), fragment_palette_conversion));
2477 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2480 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2481 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2483 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2484 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2486 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2487 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2488 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2489 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2491 /* Switch back to unit 0 in which the 2D texture will be stored. */
2492 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2494 /* Rebind the texture because it isn't bound anymore */
2495 glBindTexture(This->texture_target, This->texture_name);
2501 BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2502 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2504 if (This->palette || (This->resource.format_desc->format != WINED3DFMT_P8
2505 && This->resource.format_desc->format != WINED3DFMT_A8P8))
2507 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2508 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2513 if(This->palette9) {
2514 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2518 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2520 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2524 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2525 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2526 DWORD flag = srgb_mode ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2528 if (!(This->Flags & flag)) {
2529 TRACE("Reloading because surface is dirty\n");
2530 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2531 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2532 /* Reload: vice versa OR */
2533 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2534 /* Also reload: Color key is active AND the color key has changed */
2535 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2536 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2537 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2538 TRACE("Reloading because of color keying\n");
2539 /* To perform the color key conversion we need a sysmem copy of
2540 * the surface. Make sure we have it
2543 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2544 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2545 /* TODO: This is not necessarily needed with hw palettized texture support */
2546 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2548 TRACE("surface is already in texture\n");
2552 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2553 * These resources are not bound by device size or format restrictions. Because of this,
2554 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2555 * However, these resources can always be created, locked, and copied.
2557 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2559 FIXME("(%p) Operation not supported for scratch textures\n",This);
2560 return WINED3DERR_INVALIDCALL;
2563 IWineD3DSurface_LoadLocation(iface, flag, NULL /* no partial locking for textures yet */);
2567 static unsigned int gen = 0;
2570 if ((gen % 10) == 0) {
2571 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm",
2572 This, This->texture_target, This->texture_level, gen);
2573 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2576 * debugging crash code
2585 if (!(This->Flags & SFLAG_DONOTFREE)) {
2586 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2587 This->resource.allocatedMemory = NULL;
2588 This->resource.heapMemory = NULL;
2589 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2595 /* Context activation is done by the caller. */
2596 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface, BOOL srgb) {
2597 /* TODO: check for locks */
2598 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2599 IWineD3DBaseTexture *baseTexture = NULL;
2600 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2602 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2603 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2604 TRACE("Passing to container\n");
2605 IWineD3DBaseTexture_BindTexture(baseTexture, srgb);
2606 IWineD3DBaseTexture_Release(baseTexture);
2609 TRACE("(%p) : Binding surface\n", This);
2611 name = srgb ? &This->texture_name_srgb : &This->texture_name;
2612 if(!device->isInDraw) {
2613 ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
2618 if (!This->texture_level)
2621 glGenTextures(1, name);
2622 checkGLcall("glGenTextures");
2623 TRACE("Surface %p given name %d\n", This, *name);
2625 glBindTexture(This->texture_target, *name);
2626 checkGLcall("glBindTexture");
2627 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2628 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2629 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2630 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2631 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2632 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2633 glTexParameteri(This->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2634 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2635 glTexParameteri(This->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2636 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2638 /* This is where we should be reducing the amount of GLMemoryUsed */
2640 /* Mipmap surfaces should have a base texture container */
2641 ERR("Mipmap surface has a glTexture bound to it!\n");
2644 glBindTexture(This->texture_target, *name);
2645 checkGLcall("glBindTexture");
2654 static HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename)
2657 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2658 char *allocatedMemory;
2659 const char *textureRow;
2660 IWineD3DSwapChain *swapChain = NULL;
2661 int width, height, i, y;
2662 GLuint tmpTexture = 0;
2665 Textures may not be stored in ->allocatedgMemory and a GlTexture
2666 so we should lock the surface before saving a snapshot, or at least check that
2668 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2669 by calling GetTexImage and in compressed form by calling
2670 GetCompressedTexImageARB. Queried compressed images can be saved and
2671 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2672 texture images do not need to be processed by the GL and should
2673 significantly improve texture loading performance relative to uncompressed
2676 /* Setup the width and height to be the internal texture width and height. */
2677 width = This->pow2Width;
2678 height = This->pow2Height;
2679 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2680 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2682 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2683 /* if were not a real texture then read the back buffer into a real texture */
2684 /* we don't want to interfere with the back buffer so read the data into a temporary
2685 * texture and then save the data out of the temporary texture
2689 TRACE("(%p) Reading render target into texture\n", This);
2691 glGenTextures(1, &tmpTexture);
2692 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2694 glTexImage2D(GL_TEXTURE_2D,
2701 GL_UNSIGNED_INT_8_8_8_8_REV,
2704 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2705 checkGLcall("glGetIntegerv");
2706 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2707 checkGLcall("glReadBuffer");
2708 glCopyTexImage2D(GL_TEXTURE_2D,
2717 checkGLcall("glCopyTexImage2D");
2718 glReadBuffer(prevRead);
2721 } else { /* bind the real texture, and make sure it up to date */
2722 surface_internal_preload(iface, SRGB_RGB);
2723 surface_bind_and_dirtify(This, FALSE);
2725 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2727 FIXME("Saving texture level %d width %d height %d\n", This->texture_level, width, height);
2728 glGetTexImage(GL_TEXTURE_2D, This->texture_level, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, allocatedMemory);
2729 checkGLcall("glGetTexImage");
2731 glBindTexture(GL_TEXTURE_2D, 0);
2732 glDeleteTextures(1, &tmpTexture);
2736 f = fopen(filename, "w+");
2738 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2739 return WINED3DERR_INVALIDCALL;
2741 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2742 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format_desc->format));
2757 fwrite(&width,2,1,f);
2759 fwrite(&height,2,1,f);
2764 /* 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 */
2766 textureRow = allocatedMemory + (width * (height - 1) *4);
2768 textureRow = allocatedMemory;
2769 for (y = 0 ; y < height; y++) {
2770 for (i = 0; i < width; i++) {
2771 color = *((const DWORD*)textureRow);
2772 fputc((color >> 16) & 0xFF, f); /* B */
2773 fputc((color >> 8) & 0xFF, f); /* G */
2774 fputc((color >> 0) & 0xFF, f); /* R */
2775 fputc((color >> 24) & 0xFF, f); /* A */
2778 /* take two rows of the pointer to the texture memory */
2780 (textureRow-= width << 3);
2783 TRACE("Closing file\n");
2787 IWineD3DSwapChain_Release(swapChain);
2789 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2793 static HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2794 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2797 TRACE("(%p) : Calling base function first\n", This);
2798 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2800 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2801 TRACE("(%p) : glFormat %d, glFormatInternal %d, glType %d\n", This, This->resource.format_desc->glFormat,
2802 This->resource.format_desc->glInternal, This->resource.format_desc->glType);
2807 static HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2808 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2810 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2811 WARN("Surface is locked or the HDC is in use\n");
2812 return WINED3DERR_INVALIDCALL;
2815 if(Mem && Mem != This->resource.allocatedMemory) {
2816 void *release = NULL;
2818 /* Do I have to copy the old surface content? */
2819 if(This->Flags & SFLAG_DIBSECTION) {
2820 /* Release the DC. No need to hold the critical section for the update
2821 * Thread because this thread runs only on front buffers, but this method
2822 * fails for render targets in the check above.
2824 SelectObject(This->hDC, This->dib.holdbitmap);
2825 DeleteDC(This->hDC);
2826 /* Release the DIB section */
2827 DeleteObject(This->dib.DIBsection);
2828 This->dib.bitmap_data = NULL;
2829 This->resource.allocatedMemory = NULL;
2831 This->Flags &= ~SFLAG_DIBSECTION;
2832 } else if(!(This->Flags & SFLAG_USERPTR)) {
2833 release = This->resource.heapMemory;
2834 This->resource.heapMemory = NULL;
2836 This->resource.allocatedMemory = Mem;
2837 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2839 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2840 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2842 /* For client textures opengl has to be notified */
2843 if(This->Flags & SFLAG_CLIENT) {
2844 DWORD oldFlags = This->Flags;
2845 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2846 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2847 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2848 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2851 /* Now free the old memory if any */
2852 HeapFree(GetProcessHeap(), 0, release);
2853 } else if(This->Flags & SFLAG_USERPTR) {
2854 /* LockRect and GetDC will re-create the dib section and allocated memory */
2855 This->resource.allocatedMemory = NULL;
2856 /* HeapMemory should be NULL already */
2857 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2858 This->Flags &= ~SFLAG_USERPTR;
2860 if(This->Flags & SFLAG_CLIENT) {
2861 DWORD oldFlags = This->Flags;
2862 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2863 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2864 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2865 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2871 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
2873 /* Flip the surface contents */
2878 front->hDC = back->hDC;
2882 /* Flip the DIBsection */
2885 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
2886 tmp = front->dib.DIBsection;
2887 front->dib.DIBsection = back->dib.DIBsection;
2888 back->dib.DIBsection = tmp;
2890 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
2891 else front->Flags &= ~SFLAG_DIBSECTION;
2892 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
2893 else back->Flags &= ~SFLAG_DIBSECTION;
2896 /* Flip the surface data */
2900 tmp = front->dib.bitmap_data;
2901 front->dib.bitmap_data = back->dib.bitmap_data;
2902 back->dib.bitmap_data = tmp;
2904 tmp = front->resource.allocatedMemory;
2905 front->resource.allocatedMemory = back->resource.allocatedMemory;
2906 back->resource.allocatedMemory = tmp;
2908 tmp = front->resource.heapMemory;
2909 front->resource.heapMemory = back->resource.heapMemory;
2910 back->resource.heapMemory = tmp;
2915 GLuint tmp_pbo = front->pbo;
2916 front->pbo = back->pbo;
2917 back->pbo = tmp_pbo;
2920 /* client_memory should not be different, but just in case */
2923 tmp = front->dib.client_memory;
2924 front->dib.client_memory = back->dib.client_memory;
2925 back->dib.client_memory = tmp;
2928 /* Flip the opengl texture */
2932 tmp = back->texture_name;
2933 back->texture_name = front->texture_name;
2934 front->texture_name = tmp;
2936 tmp = back->texture_name_srgb;
2937 back->texture_name_srgb = front->texture_name_srgb;
2938 front->texture_name_srgb = tmp;
2942 DWORD tmp_flags = back->Flags;
2943 back->Flags = front->Flags;
2944 front->Flags = tmp_flags;
2948 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2949 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2950 IWineD3DSwapChainImpl *swapchain = NULL;
2952 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2954 /* Flipping is only supported on RenderTargets and overlays*/
2955 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
2956 WARN("Tried to flip a non-render target, non-overlay surface\n");
2957 return WINEDDERR_NOTFLIPPABLE;
2960 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
2961 flip_surface(This, (IWineD3DSurfaceImpl *) override);
2963 /* Update the overlay if it is visible */
2964 if(This->overlay_dest) {
2965 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
2972 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2973 * FIXME("(%p) Target override is not supported by now\n", This);
2974 * Additionally, it isn't really possible to support triple-buffering
2975 * properly on opengl at all
2979 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2981 ERR("Flipped surface is not on a swapchain\n");
2982 return WINEDDERR_NOTFLIPPABLE;
2985 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2986 * and only d3d8 and d3d9 apps specify the presentation interval
2988 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2989 /* Most common case first to avoid wasting time on all the other cases */
2990 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2991 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2992 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2993 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2994 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2995 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2996 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2998 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
3001 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
3002 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
3003 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
3007 /* Does a direct frame buffer -> texture copy. Stretching is done
3008 * with single pixel copy calls
3010 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3011 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
3012 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
3014 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3017 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3020 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
3021 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3024 /* Bind the target texture */
3025 glBindTexture(This->texture_target, This->texture_name);
3026 checkGLcall("glBindTexture");
3028 TRACE("Reading from an offscreen target\n");
3029 upsidedown = !upsidedown;
3030 glReadBuffer(myDevice->offscreenBuffer);
3032 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
3033 glReadBuffer(buffer);
3035 checkGLcall("glReadBuffer");
3037 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
3038 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
3040 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3042 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3044 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
3045 ERR("Texture filtering not supported in direct blit\n");
3048 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
3049 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3051 ERR("Texture filtering not supported in direct blit\n");
3055 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3056 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3058 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
3060 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3061 drect->x1 /*xoffset */, drect->y1 /* y offset */,
3062 srect->x1, Src->currentDesc.Height - srect->y2,
3063 drect->x2 - drect->x1, drect->y2 - drect->y1);
3065 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
3066 /* I have to process this row by row to swap the image,
3067 * otherwise it would be upside down, so stretching in y direction
3068 * doesn't cost extra time
3070 * However, stretching in x direction can be avoided if not necessary
3072 for(row = drect->y1; row < drect->y2; row++) {
3073 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3075 /* Well, that stuff works, but it's very slow.
3076 * find a better way instead
3080 for(col = drect->x1; col < drect->x2; col++) {
3081 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3082 drect->x1 + col /* x offset */, row /* y offset */,
3083 srect->x1 + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3086 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3087 drect->x1 /* x offset */, row /* y offset */,
3088 srect->x1, yoffset - (int) (row * yrel), drect->x2-drect->x1, 1);
3092 checkGLcall("glCopyTexSubImage2D");
3097 /* Uses the hardware to stretch and flip the image */
3098 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3099 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
3100 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
3102 GLuint src, backup = 0;
3103 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3104 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3105 float left, right, top, bottom; /* Texture coordinates */
3106 UINT fbwidth = Src->currentDesc.Width;
3107 UINT fbheight = Src->currentDesc.Height;
3108 struct wined3d_context *context;
3109 GLenum drawBuffer = GL_BACK;
3110 GLenum texture_target;
3111 BOOL noBackBufferBackup;
3113 TRACE("Using hwstretch blit\n");
3114 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3115 context = ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
3116 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3118 noBackBufferBackup = !swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3119 if (!noBackBufferBackup && !Src->texture_name)
3121 /* Get it a description */
3122 surface_internal_preload(SrcSurface, SRGB_RGB);
3126 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3127 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3129 if (context->aux_buffers >= 2)
3131 /* Got more than one aux buffer? Use the 2nd aux buffer */
3132 drawBuffer = GL_AUX1;
3134 else if ((swapchain || myDevice->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3136 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3137 drawBuffer = GL_AUX0;
3140 if(noBackBufferBackup) {
3141 glGenTextures(1, &backup);
3142 checkGLcall("glGenTextures");
3143 glBindTexture(GL_TEXTURE_2D, backup);
3144 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3145 texture_target = GL_TEXTURE_2D;
3147 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3148 * we are reading from the back buffer, the backup can be used as source texture
3150 texture_target = Src->texture_target;
3151 glBindTexture(texture_target, Src->texture_name);
3152 checkGLcall("glBindTexture(texture_target, Src->texture_name)");
3153 glEnable(texture_target);
3154 checkGLcall("glEnable(texture_target)");
3156 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3157 Src->Flags &= ~SFLAG_INTEXTURE;
3161 glReadBuffer(surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain));
3163 TRACE("Reading from an offscreen target\n");
3164 upsidedown = !upsidedown;
3165 glReadBuffer(myDevice->offscreenBuffer);
3168 /* TODO: Only back up the part that will be overwritten */
3169 glCopyTexSubImage2D(texture_target, 0,
3170 0, 0 /* read offsets */,
3175 checkGLcall("glCopyTexSubImage2D");
3177 /* No issue with overriding these - the sampler is dirty due to blit usage */
3178 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3179 wined3d_gl_mag_filter(magLookup, Filter));
3180 checkGLcall("glTexParameteri");
3181 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3182 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
3183 checkGLcall("glTexParameteri");
3185 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
3186 src = backup ? backup : Src->texture_name;
3188 glReadBuffer(GL_FRONT);
3189 checkGLcall("glReadBuffer(GL_FRONT)");
3191 glGenTextures(1, &src);
3192 checkGLcall("glGenTextures(1, &src)");
3193 glBindTexture(GL_TEXTURE_2D, src);
3194 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
3196 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
3197 * out for power of 2 sizes
3199 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
3200 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3201 checkGLcall("glTexImage2D");
3202 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
3203 0, 0 /* read offsets */,
3208 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3209 checkGLcall("glTexParameteri");
3210 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3211 checkGLcall("glTexParameteri");
3213 glReadBuffer(GL_BACK);
3214 checkGLcall("glReadBuffer(GL_BACK)");
3216 if(texture_target != GL_TEXTURE_2D) {
3217 glDisable(texture_target);
3218 glEnable(GL_TEXTURE_2D);
3219 texture_target = GL_TEXTURE_2D;
3222 checkGLcall("glEnd and previous");
3228 top = Src->currentDesc.Height - srect->y1;
3229 bottom = Src->currentDesc.Height - srect->y2;
3231 top = Src->currentDesc.Height - srect->y2;
3232 bottom = Src->currentDesc.Height - srect->y1;
3235 if(Src->Flags & SFLAG_NORMCOORD) {
3236 left /= Src->pow2Width;
3237 right /= Src->pow2Width;
3238 top /= Src->pow2Height;
3239 bottom /= Src->pow2Height;
3242 /* draw the source texture stretched and upside down. The correct surface is bound already */
3243 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3244 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3246 glDrawBuffer(drawBuffer);
3247 glReadBuffer(drawBuffer);
3251 glTexCoord2f(left, bottom);
3252 glVertex2i(0, fbheight);
3255 glTexCoord2f(left, top);
3256 glVertex2i(0, fbheight - drect->y2 - drect->y1);
3259 glTexCoord2f(right, top);
3260 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
3263 glTexCoord2f(right, bottom);
3264 glVertex2i(drect->x2 - drect->x1, fbheight);
3266 checkGLcall("glEnd and previous");
3268 if (texture_target != This->texture_target)
3270 glDisable(texture_target);
3271 glEnable(This->texture_target);
3272 texture_target = This->texture_target;
3275 /* Now read the stretched and upside down image into the destination texture */
3276 glBindTexture(texture_target, This->texture_name);
3277 checkGLcall("glBindTexture");
3278 glCopyTexSubImage2D(texture_target,
3280 drect->x1, drect->y1, /* xoffset, yoffset */
3281 0, 0, /* We blitted the image to the origin */
3282 drect->x2 - drect->x1, drect->y2 - drect->y1);
3283 checkGLcall("glCopyTexSubImage2D");
3285 if(drawBuffer == GL_BACK) {
3286 /* Write the back buffer backup back */
3288 if(texture_target != GL_TEXTURE_2D) {
3289 glDisable(texture_target);
3290 glEnable(GL_TEXTURE_2D);
3291 texture_target = GL_TEXTURE_2D;
3293 glBindTexture(GL_TEXTURE_2D, backup);
3294 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3296 if (texture_target != Src->texture_target)
3298 glDisable(texture_target);
3299 glEnable(Src->texture_target);
3300 texture_target = Src->texture_target;
3302 glBindTexture(Src->texture_target, Src->texture_name);
3303 checkGLcall("glBindTexture(Src->texture_target, Src->texture_name)");
3308 glTexCoord2f(0.0f, (float)fbheight / (float)Src->pow2Height);
3312 glTexCoord2f(0.0f, 0.0f);
3313 glVertex2i(0, fbheight);
3316 glTexCoord2f((float)fbwidth / (float)Src->pow2Width, 0.0f);
3317 glVertex2i(fbwidth, Src->currentDesc.Height);
3320 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
3321 glVertex2i(fbwidth, 0);
3324 /* Restore the old draw buffer */
3325 glDrawBuffer(GL_BACK);
3327 glDisable(texture_target);
3328 checkGLcall("glDisable(texture_target)");
3331 if (src != Src->texture_name && src != backup)
3333 glDeleteTextures(1, &src);
3334 checkGLcall("glDeleteTextures(1, &src)");
3337 glDeleteTextures(1, &backup);
3338 checkGLcall("glDeleteTextures(1, &backup)");
3344 /* Not called from the VTable */
3345 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3346 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx,
3347 WINED3DTEXTUREFILTERTYPE Filter)
3350 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3351 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3352 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3354 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3356 /* Get the swapchain. One of the surfaces has to be a primary surface */
3357 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3358 WARN("Destination is in sysmem, rejecting gl blt\n");
3359 return WINED3DERR_INVALIDCALL;
3361 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3362 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3364 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3365 WARN("Src is in sysmem, rejecting gl blt\n");
3366 return WINED3DERR_INVALIDCALL;
3368 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3369 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3372 /* Early sort out of cases where no render target is used */
3373 if(!dstSwapchain && !srcSwapchain &&
3374 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3375 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3376 return WINED3DERR_INVALIDCALL;
3379 /* No destination color keying supported */
3380 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3381 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3382 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3383 return WINED3DERR_INVALIDCALL;
3387 rect.x1 = DestRect->left;
3388 rect.y1 = DestRect->top;
3389 rect.x2 = DestRect->right;
3390 rect.y2 = DestRect->bottom;
3394 rect.x2 = This->currentDesc.Width;
3395 rect.y2 = This->currentDesc.Height;
3398 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3399 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3400 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3401 /* Half-life does a Blt from the back buffer to the front buffer,
3402 * Full surface size, no flags... Use present instead
3404 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3407 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3411 TRACE("Looking if a Present can be done...\n");
3412 /* Source Rectangle must be full surface */
3414 if(SrcRect->left != 0 || SrcRect->top != 0 ||
3415 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
3416 TRACE("No, Source rectangle doesn't match\n");
3422 mySrcRect.right = Src->currentDesc.Width;
3423 mySrcRect.bottom = Src->currentDesc.Height;
3425 /* No stretching may occur */
3426 if(mySrcRect.right != rect.x2 - rect.x1 ||
3427 mySrcRect.bottom != rect.y2 - rect.y1) {
3428 TRACE("No, stretching is done\n");
3432 /* Destination must be full surface or match the clipping rectangle */
3433 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3437 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3442 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3445 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3446 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3448 TRACE("No, dest rectangle doesn't match(clipper)\n");
3449 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3450 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3456 if(rect.x1 != 0 || rect.y1 != 0 ||
3457 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3458 TRACE("No, dest rectangle doesn't match(surface size)\n");
3465 /* These flags are unimportant for the flag check, remove them */
3466 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3467 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3469 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3470 * take very long, while a flip is fast.
3471 * This applies to Half-Life, which does such Blts every time it finished
3472 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3473 * menu. This is also used by all apps when they do windowed rendering
3475 * The problem is that flipping is not really the same as copying. After a
3476 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3477 * untouched. Therefore it's necessary to override the swap effect
3478 * and to set it back after the flip.
3480 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3484 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3485 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3487 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3488 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3490 dstSwapchain->presentParms.SwapEffect = orig_swap;
3497 TRACE("Unsupported blit between buffers on the same swapchain\n");
3498 return WINED3DERR_INVALIDCALL;
3499 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3500 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3501 return WINED3DERR_INVALIDCALL;
3502 } else if(dstSwapchain && srcSwapchain) {
3503 FIXME("Implement hardware blit between two different swapchains\n");
3504 return WINED3DERR_INVALIDCALL;
3505 } else if(dstSwapchain) {
3506 if(SrcSurface == myDevice->render_targets[0]) {
3507 TRACE("Blit from active render target to a swapchain\n");
3508 /* Handled with regular texture -> swapchain blit */
3510 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3511 FIXME("Implement blit from a swapchain to the active render target\n");
3512 return WINED3DERR_INVALIDCALL;
3515 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3516 /* Blit from render target to texture */
3518 BOOL upsideDown, stretchx;
3519 BOOL paletteOverride = FALSE;
3521 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3522 TRACE("Color keying not supported by frame buffer to texture blit\n");
3523 return WINED3DERR_INVALIDCALL;
3524 /* Destination color key is checked above */
3527 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3528 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3531 if(SrcRect->top < SrcRect->bottom) {
3532 srect.y1 = SrcRect->top;
3533 srect.y2 = SrcRect->bottom;
3536 srect.y1 = SrcRect->bottom;
3537 srect.y2 = SrcRect->top;
3540 srect.x1 = SrcRect->left;
3541 srect.x2 = SrcRect->right;
3545 srect.x2 = Src->currentDesc.Width;
3546 srect.y2 = Src->currentDesc.Height;
3549 if(rect.x1 > rect.x2) {
3553 upsideDown = !upsideDown;
3556 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3562 /* When blitting from a render target a texture, the texture isn't required to have a palette.
3563 * In this case grab the palette from the render target. */
3564 if ((This->resource.format_desc->format == WINED3DFMT_P8) && (This->palette == NULL))
3566 paletteOverride = TRUE;
3567 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3568 This->palette = Src->palette;
3571 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3572 * flip the image nor scale it.
3574 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3575 * -> If the app wants a image width an unscaled width, copy it line per line
3576 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3577 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3578 * back buffer. This is slower than reading line per line, thus not used for flipping
3579 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3582 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3583 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3586 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)
3587 && surface_can_stretch_rect(Src, This))
3589 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3590 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3591 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3592 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3593 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3594 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3596 TRACE("Using hardware stretching to flip / stretch the texture\n");
3597 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3600 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3602 This->palette = NULL;
3604 if(!(This->Flags & SFLAG_DONOTFREE)) {
3605 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3606 This->resource.allocatedMemory = NULL;
3607 This->resource.heapMemory = NULL;
3609 This->Flags &= ~SFLAG_INSYSMEM;
3611 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3612 * path is never entered
3614 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3618 /* Blit from offscreen surface to render target */
3619 float glTexCoord[4];
3620 DWORD oldCKeyFlags = Src->CKeyFlags;
3621 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3622 RECT SourceRectangle;
3623 BOOL paletteOverride = FALSE;
3625 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3628 SourceRectangle.left = SrcRect->left;
3629 SourceRectangle.right = SrcRect->right;
3630 SourceRectangle.top = SrcRect->top;
3631 SourceRectangle.bottom = SrcRect->bottom;
3633 SourceRectangle.left = 0;
3634 SourceRectangle.right = Src->currentDesc.Width;
3635 SourceRectangle.top = 0;
3636 SourceRectangle.bottom = Src->currentDesc.Height;
3639 /* When blitting from an offscreen surface to a rendertarget, the source
3640 * surface is not required to have a palette. Our rendering / conversion
3641 * code further down the road retrieves the palette from the surface, so
3642 * it must have a palette set. */
3643 if ((Src->resource.format_desc->format == WINED3DFMT_P8) && (Src->palette == NULL))
3645 paletteOverride = TRUE;
3646 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3647 Src->palette = This->palette;
3650 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)
3651 && !(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3652 && surface_can_stretch_rect(Src, This))
3654 TRACE("Using stretch_rect_fbo\n");
3655 /* The source is always a texture, but never the currently active render target, and the texture
3656 * contents are never upside down
3658 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3659 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3661 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3663 Src->palette = NULL;
3667 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3668 /* Fall back to software */
3669 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3670 SourceRectangle.left, SourceRectangle.top,
3671 SourceRectangle.right, SourceRectangle.bottom);
3672 return WINED3DERR_INVALIDCALL;
3675 /* Color keying: Check if we have to do a color keyed blt,
3676 * and if not check if a color key is activated.
3678 * Just modify the color keying parameters in the surface and restore them afterwards
3679 * The surface keeps track of the color key last used to load the opengl surface.
3680 * PreLoad will catch the change to the flags and color key and reload if necessary.
3682 if(Flags & WINEDDBLT_KEYSRC) {
3683 /* Use color key from surface */
3684 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3685 /* Use color key from DDBltFx */
3686 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3687 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3689 /* Do not use color key */
3690 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3693 /* Now load the surface */
3694 surface_internal_preload((IWineD3DSurface *) Src, SRGB_RGB);
3696 /* Activate the destination context, set it up for blitting */
3697 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3699 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3700 * while OpenGL coordinates are window relative.
3701 * Also beware of the origin difference(top left vs bottom left).
3702 * Also beware that the front buffer's surface size is screen width x screen height,
3703 * whereas the real gl drawable size is the size of the window.
3705 if (dstSwapchain && (IWineD3DSurface *)This == dstSwapchain->frontBuffer) {
3707 POINT offset = {0,0};
3709 ClientToScreen(dstSwapchain->win_handle, &offset);
3710 GetClientRect(dstSwapchain->win_handle, &windowsize);
3711 h = windowsize.bottom - windowsize.top;
3712 rect.x1 -= offset.x; rect.x2 -=offset.x;
3713 rect.y1 -= offset.y; rect.y2 -=offset.y;
3714 rect.y1 += This->currentDesc.Height - h; rect.y2 += This->currentDesc.Height - h;
3717 if (!is_identity_fixup(This->resource.format_desc->color_fixup))
3719 FIXME("Destination format %s has a fixup, this is not supported.\n",
3720 debug_d3dformat(This->resource.format_desc->format));
3721 dump_color_fixup_desc(This->resource.format_desc->color_fixup);
3724 if (!myDevice->blitter->color_fixup_supported(Src->resource.format_desc->color_fixup))
3726 FIXME("Source format %s has an unsupported fixup:\n",
3727 debug_d3dformat(Src->resource.format_desc->format));
3728 dump_color_fixup_desc(Src->resource.format_desc->color_fixup);
3731 myDevice->blitter->set_shader((IWineD3DDevice *) myDevice, Src->resource.format_desc,
3732 Src->texture_target, Src->pow2Width, Src->pow2Height);
3736 /* Bind the texture */
3737 glBindTexture(Src->texture_target, Src->texture_name);
3738 checkGLcall("glBindTexture");
3740 /* Filtering for StretchRect */
3741 glTexParameteri(Src->texture_target, GL_TEXTURE_MAG_FILTER,
3742 wined3d_gl_mag_filter(magLookup, Filter));
3743 checkGLcall("glTexParameteri");
3744 glTexParameteri(Src->texture_target, GL_TEXTURE_MIN_FILTER,
3745 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
3746 checkGLcall("glTexParameteri");
3747 glTexParameteri(Src->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3748 glTexParameteri(Src->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3749 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3750 checkGLcall("glTexEnvi");
3752 /* This is for color keying */
3753 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3754 glEnable(GL_ALPHA_TEST);
3755 checkGLcall("glEnable(GL_ALPHA_TEST)");
3757 /* When the primary render target uses P8, the alpha component contains the palette index.
3758 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3759 * should be masked away have alpha set to 0. */
3760 if(primary_render_target_is_p8(myDevice))
3761 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
3763 glAlphaFunc(GL_NOTEQUAL, 0.0f);
3764 checkGLcall("glAlphaFunc");
3766 glDisable(GL_ALPHA_TEST);
3767 checkGLcall("glDisable(GL_ALPHA_TEST)");
3770 /* Draw a textured quad
3774 glColor3f(1.0f, 1.0f, 1.0f);
3775 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3776 glVertex3f(rect.x1, rect.y1, 0.0f);
3778 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3779 glVertex3f(rect.x1, rect.y2, 0.0f);
3781 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3782 glVertex3f(rect.x2, rect.y2, 0.0f);
3784 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3785 glVertex3f(rect.x2, rect.y1, 0.0f);
3788 checkGLcall("glEnd");
3790 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3791 glDisable(GL_ALPHA_TEST);
3792 checkGLcall("glDisable(GL_ALPHA_TEST)");
3795 glBindTexture(Src->texture_target, 0);
3796 checkGLcall("glBindTexture(Src->texture_target, 0)");
3798 /* Restore the color key parameters */
3799 Src->CKeyFlags = oldCKeyFlags;
3800 Src->SrcBltCKey = oldBltCKey;
3802 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3804 Src->palette = NULL;
3808 /* Leave the opengl state valid for blitting */
3809 myDevice->blitter->unset_shader((IWineD3DDevice *) myDevice);
3811 /* Flush in case the drawable is used by multiple GL contexts */
3812 if(dstSwapchain && (This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer || dstSwapchain->num_contexts >= 2))
3815 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3816 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3819 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3823 /* Source-Less Blit to render target */
3824 if (Flags & WINEDDBLT_COLORFILL) {
3825 /* This is easy to handle for the D3D Device... */
3828 TRACE("Colorfill\n");
3830 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3831 must be true if we are here */
3832 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3833 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3834 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3835 TRACE("Surface is higher back buffer, falling back to software\n");
3836 return WINED3DERR_INVALIDCALL;
3839 /* The color as given in the Blt function is in the format of the frame-buffer...
3840 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3842 if (This->resource.format_desc->format == WINED3DFMT_P8)
3846 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3847 else alpha = 0xFF000000;
3849 if (This->palette) {
3851 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3852 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3853 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3858 else if (This->resource.format_desc->format == WINED3DFMT_R5G6B5)
3860 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3863 color = ((0xFF000000) |
3864 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3865 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3866 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3869 else if ((This->resource.format_desc->format == WINED3DFMT_R8G8B8)
3870 || (This->resource.format_desc->format == WINED3DFMT_X8R8G8B8))
3872 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3874 else if (This->resource.format_desc->format == WINED3DFMT_A8R8G8B8)
3876 color = DDBltFx->u5.dwFillColor;
3879 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3880 return WINED3DERR_INVALIDCALL;
3883 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3884 IWineD3DDeviceImpl_ClearSurface(myDevice, This, 1 /* Number of rectangles */,
3885 &rect, WINED3DCLEAR_TARGET, color, 0.0f /* Z */, 0 /* Stencil */);
3890 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3891 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3892 return WINED3DERR_INVALIDCALL;
3895 static HRESULT IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3896 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx)
3898 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3901 if (Flags & WINEDDBLT_DEPTHFILL) {
3902 switch(This->resource.format_desc->format)
3904 case WINED3DFMT_D16_UNORM:
3905 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3907 case WINED3DFMT_D15S1:
3908 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3910 case WINED3DFMT_D24S8:
3911 case WINED3DFMT_D24X8:
3912 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3914 case WINED3DFMT_D32:
3915 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3919 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format_desc->format));
3922 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3923 DestRect == NULL ? 0 : 1,
3924 (const WINED3DRECT *)DestRect,
3925 WINED3DCLEAR_ZBUFFER,
3931 FIXME("(%p): Unsupp depthstencil blit\n", This);
3932 return WINED3DERR_INVALIDCALL;
3935 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
3936 const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3937 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3938 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3939 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3940 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3941 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3943 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3945 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3946 return WINEDDERR_SURFACEBUSY;
3949 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3950 * except depth blits, which seem to work
3952 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3953 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3954 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3955 return WINED3DERR_INVALIDCALL;
3956 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3957 TRACE("Z Blit override handled the blit\n");
3962 /* Special cases for RenderTargets */
3963 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3964 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3965 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3968 /* For the rest call the X11 surface implementation.
3969 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3970 * other Blts are rather rare
3972 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3975 static HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
3976 IWineD3DSurface *Source, const RECT *rsrc, DWORD trans)
3978 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3979 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3980 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3981 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3983 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
3985 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3986 return WINEDDERR_SURFACEBUSY;
3989 if(myDevice->inScene &&
3990 (iface == myDevice->stencilBufferTarget ||
3991 (Source && Source == myDevice->stencilBufferTarget))) {
3992 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3993 return WINED3DERR_INVALIDCALL;
3996 /* Special cases for RenderTargets */
3997 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3998 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
4000 RECT SrcRect, DstRect;
4004 SrcRect.left = rsrc->left;
4005 SrcRect.top= rsrc->top;
4006 SrcRect.bottom = rsrc->bottom;
4007 SrcRect.right = rsrc->right;
4011 SrcRect.right = srcImpl->currentDesc.Width;
4012 SrcRect.bottom = srcImpl->currentDesc.Height;
4015 DstRect.left = dstx;
4017 DstRect.right = dstx + SrcRect.right - SrcRect.left;
4018 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
4020 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
4021 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
4022 Flags |= WINEDDBLT_KEYSRC;
4023 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
4024 Flags |= WINEDDBLT_KEYDEST;
4025 if(trans & WINEDDBLTFAST_WAIT)
4026 Flags |= WINEDDBLT_WAIT;
4027 if(trans & WINEDDBLTFAST_DONOTWAIT)
4028 Flags |= WINEDDBLT_DONOTWAIT;
4030 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
4034 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
4037 static HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface)
4039 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4041 IWineD3DPaletteImpl *pal = This->palette;
4043 TRACE("(%p)\n", This);
4045 if (!pal) return WINED3D_OK;
4047 if (This->resource.format_desc->format == WINED3DFMT_P8
4048 || This->resource.format_desc->format == WINED3DFMT_A8P8)
4051 GLenum format, internal, type;
4052 CONVERT_TYPES convert;
4054 /* Check if we are using a RTL mode which uses texturing for uploads */
4055 BOOL use_texture = (wined3d_settings.rendertargetlock_mode == RTL_READTEX);
4057 /* Check if we have hardware palette conversion if we have convert is set to NO_CONVERSION */
4058 d3dfmt_get_conv(This, TRUE, use_texture, &format, &internal, &type, &convert, &bpp, FALSE);
4060 if((This->resource.usage & WINED3DUSAGE_RENDERTARGET) && (convert == NO_CONVERSION))
4062 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4064 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
4065 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
4067 /* We want to force a palette refresh, so mark the drawable as not being up to date */
4068 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
4070 /* Re-upload the palette */
4071 ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
4072 d3dfmt_p8_upload_palette(iface, convert);
4074 if(!(This->Flags & SFLAG_INSYSMEM)) {
4075 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
4076 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
4078 TRACE("Dirtifying surface\n");
4079 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
4083 if(This->Flags & SFLAG_DIBSECTION) {
4084 TRACE("(%p): Updating the hdc's palette\n", This);
4085 for (n=0; n<256; n++) {
4086 col[n].rgbRed = pal->palents[n].peRed;
4087 col[n].rgbGreen = pal->palents[n].peGreen;
4088 col[n].rgbBlue = pal->palents[n].peBlue;
4089 col[n].rgbReserved = 0;
4091 SetDIBColorTable(This->hDC, 0, 256, col);
4094 /* Propagate the changes to the drawable when we have a palette. */
4095 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
4096 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
4101 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
4102 /** Check against the maximum texture sizes supported by the video card **/
4103 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4104 unsigned int pow2Width, pow2Height;
4106 This->texture_name = 0;
4107 This->texture_target = GL_TEXTURE_2D;
4109 /* Non-power2 support */
4110 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO) || GL_SUPPORT(WINE_NORMALIZED_TEXRECT)) {
4111 pow2Width = This->currentDesc.Width;
4112 pow2Height = This->currentDesc.Height;
4114 /* Find the nearest pow2 match */
4115 pow2Width = pow2Height = 1;
4116 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
4117 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
4119 This->pow2Width = pow2Width;
4120 This->pow2Height = pow2Height;
4122 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
4123 /** TODO: add support for non power two compressed textures **/
4124 if (This->resource.format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
4126 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
4127 This, This->currentDesc.Width, This->currentDesc.Height);
4128 return WINED3DERR_NOTAVAILABLE;
4132 if(pow2Width != This->currentDesc.Width ||
4133 pow2Height != This->currentDesc.Height) {
4134 This->Flags |= SFLAG_NONPOW2;
4137 TRACE("%p\n", This);
4138 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
4139 /* one of three options
4140 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)
4141 2: Set the texture to the maximum size (bad idea)
4142 3: WARN and return WINED3DERR_NOTAVAILABLE;
4143 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.
4145 WARN("(%p) Creating an oversized surface: %ux%u (texture is %ux%u)\n",
4146 This, This->pow2Width, This->pow2Height, This->currentDesc.Width, This->currentDesc.Height);
4147 This->Flags |= SFLAG_OVERSIZE;
4149 /* This will be initialized on the first blt */
4150 This->glRect.left = 0;
4151 This->glRect.top = 0;
4152 This->glRect.right = 0;
4153 This->glRect.bottom = 0;
4155 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
4156 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
4157 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
4158 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
4160 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE)
4161 && !((This->resource.format_desc->format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE)
4162 && (wined3d_settings.rendertargetlock_mode == RTL_READTEX)))
4164 This->texture_target = GL_TEXTURE_RECTANGLE_ARB;
4165 This->pow2Width = This->currentDesc.Width;
4166 This->pow2Height = This->currentDesc.Height;
4167 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
4170 /* No oversize, gl rect is the full texture size */
4171 This->Flags &= ~SFLAG_OVERSIZE;
4172 This->glRect.left = 0;
4173 This->glRect.top = 0;
4174 This->glRect.right = This->pow2Width;
4175 This->glRect.bottom = This->pow2Height;
4178 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4179 switch(wined3d_settings.offscreen_rendering_mode) {
4180 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4181 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4182 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4186 This->Flags |= SFLAG_INSYSMEM;
4191 struct depth_blt_info
4195 enum tex_types tex_type;
4196 GLfloat coords[4][3];
4199 static void surface_get_depth_blt_info(GLenum target, GLsizei w, GLsizei h, struct depth_blt_info *info)
4201 GLfloat (*coords)[3] = info->coords;
4206 FIXME("Unsupported texture target %#x\n", target);
4207 /* Fall back to GL_TEXTURE_2D */
4209 info->binding = GL_TEXTURE_BINDING_2D;
4210 info->bind_target = GL_TEXTURE_2D;
4211 info->tex_type = tex_2d;
4212 coords[0][0] = 0.0f; coords[0][1] = 1.0f; coords[0][2] = 0.0f;
4213 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 0.0f;
4214 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4215 coords[3][0] = 1.0f; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4218 case GL_TEXTURE_RECTANGLE_ARB:
4219 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
4220 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
4221 info->tex_type = tex_rect;
4222 coords[0][0] = 0.0f; coords[0][1] = h; coords[0][2] = 0.0f;
4223 coords[1][0] = w; coords[1][1] = h; coords[1][2] = 0.0f;
4224 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4225 coords[3][0] = w; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4228 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4229 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4230 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4231 info->tex_type = tex_cube;
4232 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4233 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4234 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4235 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4237 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4238 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4239 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4240 info->tex_type = tex_cube;
4241 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4242 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4243 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4244 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4246 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4247 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4248 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4249 info->tex_type = tex_cube;
4250 coords[0][0] = -1.0f; coords[0][1] = 1.0f; coords[0][2] = 1.0f;
4251 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 1.0f;
4252 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4253 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4255 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4256 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4257 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4258 info->tex_type = tex_cube;
4259 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4260 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4261 coords[2][0] = -1.0f; coords[2][1] = -1.0f; coords[2][2] = 1.0f;
4262 coords[3][0] = 1.0f; coords[3][1] = -1.0f; coords[3][2] = 1.0f;
4264 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4265 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4266 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4267 info->tex_type = tex_cube;
4268 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4269 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4270 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4271 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4273 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4274 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4275 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4276 info->tex_type = tex_cube;
4277 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4278 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4279 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4280 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4284 /* GL locking is done by the caller */
4285 static void surface_depth_blt(IWineD3DSurfaceImpl *This, GLuint texture, GLsizei w, GLsizei h, GLenum target)
4287 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4288 struct depth_blt_info info;
4289 GLint old_binding = 0;
4291 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4293 glDisable(GL_CULL_FACE);
4294 glDisable(GL_BLEND);
4295 glDisable(GL_ALPHA_TEST);
4296 glDisable(GL_SCISSOR_TEST);
4297 glDisable(GL_STENCIL_TEST);
4298 glEnable(GL_DEPTH_TEST);
4299 glDepthFunc(GL_ALWAYS);
4300 glDepthMask(GL_TRUE);
4301 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4302 glViewport(0, 0, w, h);
4304 surface_get_depth_blt_info(target, w, h, &info);
4305 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4306 glGetIntegerv(info.binding, &old_binding);
4307 glBindTexture(info.bind_target, texture);
4309 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4311 glBegin(GL_TRIANGLE_STRIP);
4312 glTexCoord3fv(info.coords[0]);
4313 glVertex2f(-1.0f, -1.0f);
4314 glTexCoord3fv(info.coords[1]);
4315 glVertex2f(1.0f, -1.0f);
4316 glTexCoord3fv(info.coords[2]);
4317 glVertex2f(-1.0f, 1.0f);
4318 glTexCoord3fv(info.coords[3]);
4319 glVertex2f(1.0f, 1.0f);
4322 glBindTexture(info.bind_target, old_binding);
4326 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4329 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
4330 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4332 TRACE("(%p) New location %#x\n", This, location);
4334 if (location & ~SFLAG_DS_LOCATIONS) {
4335 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
4338 This->Flags &= ~SFLAG_DS_LOCATIONS;
4339 This->Flags |= location;
4342 /* Context activation is done by the caller. */
4343 void surface_load_ds_location(IWineD3DSurface *iface, struct wined3d_context *context, DWORD location)
4345 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4346 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4348 TRACE("(%p) New location %#x\n", This, location);
4350 /* TODO: Make this work for modes other than FBO */
4351 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4353 if (This->Flags & location) {
4354 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
4358 if (This->current_renderbuffer) {
4359 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
4363 if (location == SFLAG_DS_OFFSCREEN) {
4364 if (This->Flags & SFLAG_DS_ONSCREEN) {
4365 GLint old_binding = 0;
4368 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
4372 if (!device->depth_blt_texture) {
4373 glGenTextures(1, &device->depth_blt_texture);
4376 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4377 * directly on the FBO texture. That's because we need to flip. */
4378 context_bind_fbo(context, GL_FRAMEBUFFER_EXT, NULL);
4379 if (This->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4381 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4382 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4384 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4385 bind_target = GL_TEXTURE_2D;
4387 glBindTexture(bind_target, device->depth_blt_texture);
4388 glCopyTexImage2D(bind_target, This->texture_level, This->resource.format_desc->glInternal,
4389 0, 0, This->currentDesc.Width, This->currentDesc.Height, 0);
4390 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4391 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4392 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4393 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4394 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4395 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4396 glBindTexture(bind_target, old_binding);
4398 /* Setup the destination */
4399 if (!device->depth_blt_rb) {
4400 GL_EXTCALL(glGenRenderbuffersEXT(1, &device->depth_blt_rb));
4401 checkGLcall("glGenRenderbuffersEXT");
4403 if (device->depth_blt_rb_w != This->currentDesc.Width
4404 || device->depth_blt_rb_h != This->currentDesc.Height) {
4405 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4406 checkGLcall("glBindRenderbufferEXT");
4407 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, This->currentDesc.Width, This->currentDesc.Height));
4408 checkGLcall("glRenderbufferStorageEXT");
4409 device->depth_blt_rb_w = This->currentDesc.Width;
4410 device->depth_blt_rb_h = This->currentDesc.Height;
4413 context_bind_fbo(context, GL_FRAMEBUFFER_EXT, &context->dst_fbo);
4414 GL_EXTCALL(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4415 checkGLcall("glFramebufferRenderbufferEXT");
4416 context_attach_depth_stencil_fbo(context, GL_FRAMEBUFFER_EXT, iface, FALSE);
4418 /* Do the actual blit */
4419 surface_depth_blt(This, device->depth_blt_texture, This->currentDesc.Width, This->currentDesc.Height, bind_target);
4420 checkGLcall("depth_blt");
4422 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER_EXT, &context->current_fbo->id);
4423 else context_bind_fbo(context, GL_FRAMEBUFFER_EXT, NULL);
4427 FIXME("No up to date depth stencil location\n");
4429 } else if (location == SFLAG_DS_ONSCREEN) {
4430 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4431 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4435 context_bind_fbo(context, GL_FRAMEBUFFER_EXT, NULL);
4436 surface_depth_blt(This, This->texture_name, This->currentDesc.Width,
4437 This->currentDesc.Height, This->texture_target);
4438 checkGLcall("depth_blt");
4440 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER_EXT, &context->current_fbo->id);
4444 FIXME("No up to date depth stencil location\n");
4447 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4450 This->Flags |= location;
4453 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4454 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4455 IWineD3DBaseTexture *texture;
4456 IWineD3DSurfaceImpl *overlay;
4458 TRACE("(%p)->(%s, %s)\n", iface, debug_surflocation(flag),
4459 persistent ? "TRUE" : "FALSE");
4461 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4462 if (This->Flags & SFLAG_SWAPCHAIN)
4464 TRACE("Surface %p is an onscreen surface\n", iface);
4466 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4467 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4472 if(((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) ||
4473 ((This->Flags & SFLAG_INSRGBTEX) && !(flag & SFLAG_INSRGBTEX))) {
4474 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4475 TRACE("Passing to container\n");
4476 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4477 IWineD3DBaseTexture_Release(texture);
4480 This->Flags &= ~SFLAG_LOCATIONS;
4481 This->Flags |= flag;
4483 /* Redraw emulated overlays, if any */
4484 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4485 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4486 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4490 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))) {
4491 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4492 TRACE("Passing to container\n");
4493 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4494 IWineD3DBaseTexture_Release(texture);
4497 This->Flags &= ~flag;
4500 if(!(This->Flags & SFLAG_LOCATIONS)) {
4501 ERR("%p: Surface does not have any up to date location\n", This);
4517 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
4519 f->l = ((r->left * 2.0f) / w) - 1.0f;
4520 f->t = ((r->top * 2.0f) / h) - 1.0f;
4521 f->r = ((r->right * 2.0f) / w) - 1.0f;
4522 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
4525 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in)
4527 const struct wined3d_context *context;
4528 struct coords coords[4];
4530 IWineD3DSwapChain *swapchain;
4531 IWineD3DBaseTexture *texture;
4532 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4534 struct float_rect f;
4541 rect.right = This->currentDesc.Width;
4542 rect.bottom = This->currentDesc.Height;
4545 switch (This->texture_target)
4548 bind_target = GL_TEXTURE_2D;
4550 coords[0].x = (float)rect.left / This->pow2Width;
4551 coords[0].y = (float)rect.top / This->pow2Height;
4554 coords[1].x = (float)rect.left / This->pow2Width;
4555 coords[1].y = (float)rect.bottom / This->pow2Height;
4558 coords[2].x = (float)rect.right / This->pow2Width;
4559 coords[2].y = (float)rect.bottom / This->pow2Height;
4562 coords[3].x = (float)rect.right / This->pow2Width;
4563 coords[3].y = (float)rect.top / This->pow2Height;
4567 case GL_TEXTURE_RECTANGLE_ARB:
4568 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4569 coords[0].x = rect.left; coords[0].y = rect.top; coords[0].z = 0;
4570 coords[1].x = rect.left; coords[1].y = rect.bottom; coords[1].z = 0;
4571 coords[2].x = rect.right; coords[2].y = rect.bottom; coords[2].z = 0;
4572 coords[3].x = rect.right; coords[3].y = rect.top; coords[3].z = 0;
4575 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4576 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4577 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4578 coords[0].x = 1; coords[0].y = -f.t; coords[0].z = -f.l;
4579 coords[1].x = 1; coords[1].y = -f.b; coords[1].z = -f.l;
4580 coords[2].x = 1; coords[2].y = -f.b; coords[2].z = -f.r;
4581 coords[3].x = 1; coords[3].y = -f.t; coords[3].z = -f.r;
4584 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4585 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4586 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4587 coords[0].x = -1; coords[0].y = -f.t; coords[0].z = f.l;
4588 coords[1].x = -1; coords[1].y = -f.b; coords[1].z = f.l;
4589 coords[2].x = -1; coords[2].y = -f.b; coords[2].z = f.r;
4590 coords[3].x = -1; coords[3].y = -f.t; coords[3].z = f.r;
4593 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4594 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4595 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4596 coords[0].x = f.l; coords[0].y = 1; coords[0].z = f.t;
4597 coords[1].x = f.l; coords[1].y = 1; coords[1].z = f.b;
4598 coords[2].x = f.r; coords[2].y = 1; coords[2].z = f.b;
4599 coords[3].x = f.r; coords[3].y = 1; coords[3].z = f.t;
4602 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4603 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4604 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4605 coords[0].x = f.l; coords[0].y = -1; coords[0].z = -f.t;
4606 coords[1].x = f.l; coords[1].y = -1; coords[1].z = -f.b;
4607 coords[2].x = f.r; coords[2].y = -1; coords[2].z = -f.b;
4608 coords[3].x = f.r; coords[3].y = -1; coords[3].z = -f.t;
4611 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4612 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4613 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4614 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1;
4615 coords[1].x = f.l; coords[1].y = -f.b; coords[1].z = 1;
4616 coords[2].x = f.r; coords[2].y = -f.b; coords[2].z = 1;
4617 coords[3].x = f.r; coords[3].y = -f.t; coords[3].z = 1;
4620 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4621 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4622 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4623 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1;
4624 coords[1].x = -f.l; coords[1].y = -f.b; coords[1].z = -1;
4625 coords[2].x = -f.r; coords[2].y = -f.b; coords[2].z = -1;
4626 coords[3].x = -f.r; coords[3].y = -f.t; coords[3].z = -1;
4630 ERR("Unexpected texture target %#x\n", This->texture_target);
4634 context = ActivateContext(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4638 glEnable(bind_target);
4639 checkGLcall("glEnable(bind_target)");
4640 glBindTexture(bind_target, This->texture_name);
4641 checkGLcall("glBindTexture(bind_target, This->texture_name)");
4642 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4643 checkGLcall("glTexParameteri");
4644 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4645 checkGLcall("glTexParameteri");
4647 if (context->render_offscreen)
4649 LONG tmp = rect.top;
4650 rect.top = rect.bottom;
4655 glTexCoord3fv(&coords[0].x);
4656 glVertex2i(rect.left, rect.top);
4658 glTexCoord3fv(&coords[1].x);
4659 glVertex2i(rect.left, rect.bottom);
4661 glTexCoord3fv(&coords[2].x);
4662 glVertex2i(rect.right, rect.bottom);
4664 glTexCoord3fv(&coords[3].x);
4665 glVertex2i(rect.right, rect.top);
4667 checkGLcall("glEnd");
4669 glDisable(bind_target);
4670 checkGLcall("glDisable(bind_target)");
4674 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain)))
4676 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
4677 if(((IWineD3DSwapChainImpl*)swapchain)->frontBuffer == (IWineD3DSurface*)This ||
4678 ((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
4681 IWineD3DSwapChain_Release(swapchain);
4683 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
4684 * reset properly next draw
4686 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture)))
4688 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
4689 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
4690 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
4691 IWineD3DBaseTexture_Release(texture);
4696 /*****************************************************************************
4697 * IWineD3DSurface::LoadLocation
4699 * Copies the current surface data from wherever it is to the requested
4700 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4701 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4702 * multiple locations, the gl texture is preferred over the drawable, which is
4703 * preferred over system memory. The PBO counts as system memory. If rect is
4704 * not NULL, only the specified rectangle is copied (only supported for
4705 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4706 * location is marked up to date after the copy.
4709 * flag: Surface location flag to be updated
4710 * rect: rectangle to be copied
4713 * WINED3D_OK on success
4714 * WINED3DERR_DEVICELOST on an internal error
4716 *****************************************************************************/
4717 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4718 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4719 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4720 GLenum format, internal, type;
4721 CONVERT_TYPES convert;
4723 int width, pitch, outpitch;
4725 BOOL drawable_read_ok = TRUE;
4727 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4728 if (This->Flags & SFLAG_SWAPCHAIN)
4730 TRACE("Surface %p is an onscreen surface\n", iface);
4732 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4733 * Prefer SFLAG_INTEXTURE. */
4734 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4735 drawable_read_ok = FALSE;
4739 TRACE("(%p)->(%s, %p)\n", iface, debug_surflocation(flag), rect);
4741 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4744 if(This->Flags & flag) {
4745 TRACE("Location already up to date\n");
4749 if(!(This->Flags & SFLAG_LOCATIONS)) {
4750 ERR("%p: Surface does not have any up to date location\n", This);
4751 This->Flags |= SFLAG_LOST;
4752 return WINED3DERR_DEVICELOST;
4755 if(flag == SFLAG_INSYSMEM) {
4756 surface_prepare_system_memory(This);
4758 /* Download the surface to system memory */
4759 if(This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) {
4760 if (!device->isInDraw) ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
4761 surface_bind_and_dirtify(This, !(This->Flags & SFLAG_INTEXTURE));
4763 surface_download_data(This);
4765 /* Note: It might be faster to download into a texture first. */
4766 read_from_framebuffer(This, rect,
4767 This->resource.allocatedMemory,
4768 IWineD3DSurface_GetPitch(iface));
4770 } else if(flag == SFLAG_INDRAWABLE) {
4771 if(This->Flags & SFLAG_INTEXTURE) {
4772 surface_blt_to_drawable(This, rect);
4774 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4775 /* This needs a shader to convert the srgb data sampled from the GL texture into RGB
4776 * values, otherwise we get incorrect values in the target. For now go the slow way
4777 * via a system memory copy
4779 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4782 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, FALSE);
4784 /* The width is in 'length' not in bytes */
4785 width = This->currentDesc.Width;
4786 pitch = IWineD3DSurface_GetPitch(iface);
4788 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4789 * but it isn't set (yet) in all cases it is getting called. */
4790 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4791 TRACE("Removing the pbo attached to surface %p\n", This);
4792 if (!device->isInDraw) ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
4793 surface_remove_pbo(This);
4796 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4797 int height = This->currentDesc.Height;
4799 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4800 outpitch = width * bpp;
4801 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4803 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4805 ERR("Out of memory %d, %d!\n", outpitch, height);
4806 return WINED3DERR_OUTOFVIDEOMEMORY;
4808 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4810 This->Flags |= SFLAG_CONVERTED;
4812 This->Flags &= ~SFLAG_CONVERTED;
4813 mem = This->resource.allocatedMemory;
4816 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4818 /* Don't delete PBO memory */
4819 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4820 HeapFree(GetProcessHeap(), 0, mem);
4822 } else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */ {
4823 if (drawable_read_ok && (This->Flags & SFLAG_INDRAWABLE)) {
4824 read_from_framebuffer_texture(This, flag == SFLAG_INSRGBTEX);
4825 } else { /* Upload from system memory */
4826 BOOL srgb = flag == SFLAG_INSRGBTEX;
4827 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4828 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
4831 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE) {
4832 /* Performance warning ... */
4833 FIXME("%p: Downloading rgb texture to reload it as srgb\n", This);
4834 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4837 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4838 /* Performance warning ... */
4839 FIXME("%p: Downloading srgb texture to reload it as rgb\n", This);
4840 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4844 if (!device->isInDraw) ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
4845 surface_bind_and_dirtify(This, srgb);
4847 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4848 This->Flags |= SFLAG_GLCKEY;
4849 This->glCKey = This->SrcBltCKey;
4851 else This->Flags &= ~SFLAG_GLCKEY;
4853 /* The width is in 'length' not in bytes */
4854 width = This->currentDesc.Width;
4855 pitch = IWineD3DSurface_GetPitch(iface);
4857 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4858 * but it isn't set (yet) in all cases it is getting called. */
4859 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4860 TRACE("Removing the pbo attached to surface %p\n", This);
4861 surface_remove_pbo(This);
4864 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4865 int height = This->currentDesc.Height;
4867 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4868 outpitch = width * bpp;
4869 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4871 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4873 ERR("Out of memory %d, %d!\n", outpitch, height);
4874 return WINED3DERR_OUTOFVIDEOMEMORY;
4876 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4878 This->Flags |= SFLAG_CONVERTED;
4880 else if ((This->resource.format_desc->format == WINED3DFMT_P8)
4881 && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)))
4883 d3dfmt_p8_upload_palette(iface, convert);
4884 This->Flags &= ~SFLAG_CONVERTED;
4885 mem = This->resource.allocatedMemory;
4887 This->Flags &= ~SFLAG_CONVERTED;
4888 mem = This->resource.allocatedMemory;
4891 /* Make sure the correct pitch is used */
4893 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4896 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4897 TRACE("non power of two support\n");
4898 if(!(This->Flags & alloc_flag)) {
4899 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4900 This->Flags |= alloc_flag;
4902 if (mem || (This->Flags & SFLAG_PBO)) {
4903 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4906 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4907 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4909 if(!(This->Flags & alloc_flag)) {
4910 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4911 This->Flags |= alloc_flag;
4913 if (mem || (This->Flags & SFLAG_PBO)) {
4914 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4918 /* Restore the default pitch */
4920 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4923 /* Don't delete PBO memory */
4924 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4925 HeapFree(GetProcessHeap(), 0, mem);
4930 This->Flags |= flag;
4933 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !(This->Flags & SFLAG_SWAPCHAIN)
4934 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4935 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4936 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4942 static HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container)
4944 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4945 IWineD3DSwapChain *swapchain = NULL;
4947 /* Update the drawable size method */
4949 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4952 This->get_drawable_size = get_drawable_size_swapchain;
4953 IWineD3DSwapChain_Release(swapchain);
4954 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4955 switch(wined3d_settings.offscreen_rendering_mode) {
4956 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4957 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4958 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4962 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4965 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4966 return SURFACE_OPENGL;
4969 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
4970 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4973 /* If there's no destination surface there is nothing to do */
4974 if(!This->overlay_dest) return WINED3D_OK;
4976 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
4977 * update the overlay. Prevent an endless recursion
4979 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
4982 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
4983 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
4984 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
4985 NULL, WINED3DTEXF_LINEAR);
4986 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
4991 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4994 IWineD3DBaseSurfaceImpl_QueryInterface,
4995 IWineD3DBaseSurfaceImpl_AddRef,
4996 IWineD3DSurfaceImpl_Release,
4997 /* IWineD3DResource */
4998 IWineD3DBaseSurfaceImpl_GetParent,
4999 IWineD3DBaseSurfaceImpl_GetDevice,
5000 IWineD3DBaseSurfaceImpl_SetPrivateData,
5001 IWineD3DBaseSurfaceImpl_GetPrivateData,
5002 IWineD3DBaseSurfaceImpl_FreePrivateData,
5003 IWineD3DBaseSurfaceImpl_SetPriority,
5004 IWineD3DBaseSurfaceImpl_GetPriority,
5005 IWineD3DSurfaceImpl_PreLoad,
5006 IWineD3DSurfaceImpl_UnLoad,
5007 IWineD3DBaseSurfaceImpl_GetType,
5008 /* IWineD3DSurface */
5009 IWineD3DBaseSurfaceImpl_GetContainer,
5010 IWineD3DBaseSurfaceImpl_GetDesc,
5011 IWineD3DSurfaceImpl_LockRect,
5012 IWineD3DSurfaceImpl_UnlockRect,
5013 IWineD3DSurfaceImpl_GetDC,
5014 IWineD3DSurfaceImpl_ReleaseDC,
5015 IWineD3DSurfaceImpl_Flip,
5016 IWineD3DSurfaceImpl_Blt,
5017 IWineD3DBaseSurfaceImpl_GetBltStatus,
5018 IWineD3DBaseSurfaceImpl_GetFlipStatus,
5019 IWineD3DBaseSurfaceImpl_IsLost,
5020 IWineD3DBaseSurfaceImpl_Restore,
5021 IWineD3DSurfaceImpl_BltFast,
5022 IWineD3DBaseSurfaceImpl_GetPalette,
5023 IWineD3DBaseSurfaceImpl_SetPalette,
5024 IWineD3DSurfaceImpl_RealizePalette,
5025 IWineD3DBaseSurfaceImpl_SetColorKey,
5026 IWineD3DBaseSurfaceImpl_GetPitch,
5027 IWineD3DSurfaceImpl_SetMem,
5028 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
5029 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
5030 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
5031 IWineD3DBaseSurfaceImpl_UpdateOverlay,
5032 IWineD3DBaseSurfaceImpl_SetClipper,
5033 IWineD3DBaseSurfaceImpl_GetClipper,
5035 IWineD3DSurfaceImpl_LoadTexture,
5036 IWineD3DSurfaceImpl_BindTexture,
5037 IWineD3DSurfaceImpl_SaveSnapshot,
5038 IWineD3DSurfaceImpl_SetContainer,
5039 IWineD3DBaseSurfaceImpl_GetData,
5040 IWineD3DSurfaceImpl_SetFormat,
5041 IWineD3DSurfaceImpl_PrivateSetup,
5042 IWineD3DSurfaceImpl_ModifyLocation,
5043 IWineD3DSurfaceImpl_LoadLocation,
5044 IWineD3DSurfaceImpl_GetImplType,
5045 IWineD3DSurfaceImpl_DrawOverlay
5047 #undef GLINFO_LOCATION
5049 #define GLINFO_LOCATION device->adapter->gl_info
5050 static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
5051 /* Context activation is done by the caller. */
5052 static void ffp_blit_free(IWineD3DDevice *iface) { }
5054 /* Context activation is done by the caller. */
5055 static HRESULT ffp_blit_set(IWineD3DDevice *iface, const struct GlPixelFormatDesc *format_desc,
5056 GLenum textype, UINT width, UINT height)
5060 checkGLcall("glEnable(textype)");
5065 /* Context activation is done by the caller. */
5066 static void ffp_blit_unset(IWineD3DDevice *iface) {
5067 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
5069 glDisable(GL_TEXTURE_2D);
5070 checkGLcall("glDisable(GL_TEXTURE_2D)");
5071 if(GL_SUPPORT(ARB_TEXTURE_CUBE_MAP)) {
5072 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
5073 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
5075 if(GL_SUPPORT(ARB_TEXTURE_RECTANGLE)) {
5076 glDisable(GL_TEXTURE_RECTANGLE_ARB);
5077 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
5082 static BOOL ffp_blit_color_fixup_supported(struct color_fixup_desc fixup)
5084 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
5086 TRACE("Checking support for fixup:\n");
5087 dump_color_fixup_desc(fixup);
5090 /* We only support identity conversions. */
5091 if (is_identity_fixup(fixup))
5097 TRACE("[FAILED]\n");
5101 const struct blit_shader ffp_blit = {
5106 ffp_blit_color_fixup_supported