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
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d);
36 #define GLINFO_LOCATION (*gl_info)
38 static void surface_cleanup(IWineD3DSurfaceImpl *This)
40 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
41 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
42 renderbuffer_entry_t *entry, *entry2;
44 TRACE("(%p) : Cleaning up.\n", This);
46 /* Need a context to destroy the texture. Use the currently active render
47 * target, but only if the primary render target exists. Otherwise
48 * lastActiveRenderTarget is garbage. When destroying the primary render
49 * target, Uninit3D() will activate a context before doing anything. */
50 if (device->render_targets && device->render_targets[0])
52 ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
57 if (This->texture_name)
59 /* Release the OpenGL texture. */
60 TRACE("Deleting texture %u.\n", This->texture_name);
61 glDeleteTextures(1, &This->texture_name);
64 if (This->Flags & SFLAG_PBO)
67 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
70 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry)
72 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
73 HeapFree(GetProcessHeap(), 0, entry);
78 if (This->Flags & SFLAG_DIBSECTION)
81 SelectObject(This->hDC, This->dib.holdbitmap);
83 /* Release the DIB section. */
84 DeleteObject(This->dib.DIBsection);
85 This->dib.bitmap_data = NULL;
86 This->resource.allocatedMemory = NULL;
89 if (This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem((IWineD3DSurface *)This, NULL);
90 if (This->overlay_dest) list_remove(&This->overlay_entry);
92 HeapFree(GetProcessHeap(), 0, This->palette9);
94 resource_cleanup((IWineD3DResource *)This);
97 UINT surface_calculate_size(const struct GlPixelFormatDesc *format_desc, UINT alignment, UINT width, UINT height)
101 if (format_desc->format == WINED3DFMT_UNKNOWN)
105 else if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
107 UINT row_block_count = (width + format_desc->block_width - 1) / format_desc->block_width;
108 UINT row_count = (height + format_desc->block_height - 1) / format_desc->block_height;
109 size = row_count * row_block_count * format_desc->block_byte_count;
113 /* The pitch is a multiple of 4 bytes. */
114 size = height * (((width * format_desc->byte_count) + alignment - 1) & ~(alignment - 1));
117 if (format_desc->heightscale != 0.0f) size *= format_desc->heightscale;
122 HRESULT surface_init(IWineD3DSurfaceImpl *surface, WINED3DSURFTYPE surface_type, UINT alignment,
123 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
124 UINT multisample_quality, IWineD3DDeviceImpl *device, DWORD usage, WINED3DFORMAT format,
125 WINED3DPOOL pool, IUnknown *parent)
127 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
128 const struct GlPixelFormatDesc *format_desc = getFormatDescEntry(format, &GLINFO_LOCATION);
129 void (*cleanup)(IWineD3DSurfaceImpl *This);
130 unsigned int resource_size;
133 if (multisample_quality > 0)
135 FIXME("multisample_quality set to %u, substituting 0\n", multisample_quality);
136 multisample_quality = 0;
139 /* FIXME: Check that the format is supported by the device. */
141 resource_size = surface_calculate_size(format_desc, alignment, width, height);
143 /* Look at the implementation and set the correct Vtable. */
144 switch (surface_type)
147 surface->lpVtbl = &IWineD3DSurface_Vtbl;
148 cleanup = surface_cleanup;
152 surface->lpVtbl = &IWineGDISurface_Vtbl;
153 cleanup = surface_gdi_cleanup;
157 ERR("Requested unknown surface implementation %#x.\n", surface_type);
158 return WINED3DERR_INVALIDCALL;
161 hr = resource_init((IWineD3DResource *)surface, WINED3DRTYPE_SURFACE,
162 device, resource_size, usage, format_desc, pool, parent);
165 WARN("Failed to initialize resource, returning %#x.\n", hr);
169 /* "Standalone" surface. */
170 IWineD3DSurface_SetContainer((IWineD3DSurface *)surface, NULL);
172 surface->currentDesc.Width = width;
173 surface->currentDesc.Height = height;
174 surface->currentDesc.MultiSampleType = multisample_type;
175 surface->currentDesc.MultiSampleQuality = multisample_quality;
176 surface->texture_level = level;
177 list_init(&surface->overlays);
180 surface->Flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
181 if (discard) surface->Flags |= SFLAG_DISCARD;
182 if (lockable || format == WINED3DFMT_D16_LOCKABLE) surface->Flags |= SFLAG_LOCKABLE;
184 /* Quick lockable sanity check.
185 * TODO: remove this after surfaces, usage and lockability have been debugged properly
186 * this function is too deep to need to care about things like this.
187 * Levels need to be checked too, since they all affect what can be done. */
190 case WINED3DPOOL_SCRATCH:
193 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
194 "which are mutually exclusive, setting lockable to TRUE.\n");
199 case WINED3DPOOL_SYSTEMMEM:
201 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
204 case WINED3DPOOL_MANAGED:
205 if (usage & WINED3DUSAGE_DYNAMIC)
206 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
209 case WINED3DPOOL_DEFAULT:
210 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
211 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
215 FIXME("Unknown pool %#x.\n", pool);
219 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
221 FIXME("Trying to create a render target that isn't in the default pool.\n");
224 /* Mark the texture as dirty so that it gets loaded first time around. */
225 surface_add_dirty_rect((IWineD3DSurface *)surface, NULL);
226 list_init(&surface->renderbuffers);
228 TRACE("surface %p, memory %p, size %u\n", surface, surface->resource.allocatedMemory, surface->resource.size);
230 /* Call the private setup routine */
231 hr = IWineD3DSurface_PrivateSetup((IWineD3DSurface *)surface);
234 ERR("Private setup failed, returning %#x\n", hr);
242 static void surface_force_reload(IWineD3DSurface *iface)
244 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
246 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
249 void surface_set_texture_name(IWineD3DSurface *iface, GLuint new_name, BOOL srgb)
251 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
257 name = &This->texture_name_srgb;
258 flag = SFLAG_INSRGBTEX;
262 name = &This->texture_name;
263 flag = SFLAG_INTEXTURE;
266 TRACE("(%p) : setting texture name %u\n", This, new_name);
268 if (!*name && new_name)
270 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
271 * surface has no texture name yet. See if we can get rid of this. */
272 if (This->Flags & flag)
273 ERR("Surface has SFLAG_INTEXTURE set, but no texture name\n");
274 IWineD3DSurface_ModifyLocation(iface, flag, FALSE);
278 surface_force_reload(iface);
281 void surface_set_texture_target(IWineD3DSurface *iface, GLenum target)
283 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
285 TRACE("(%p) : setting target %#x\n", This, target);
287 if (This->texture_target != target)
289 if (target == GL_TEXTURE_RECTANGLE_ARB)
291 This->Flags &= ~SFLAG_NORMCOORD;
293 else if (This->texture_target == GL_TEXTURE_RECTANGLE_ARB)
295 This->Flags |= SFLAG_NORMCOORD;
298 This->texture_target = target;
299 surface_force_reload(iface);
302 /* Context activation is done by the caller. */
303 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This, BOOL srgb) {
304 DWORD active_sampler;
306 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
307 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
308 * gl states. The current texture unit should always be a valid one.
310 * To be more specific, this is tricky because we can implicitly be called
311 * from sampler() in state.c. This means we can't touch anything other than
312 * whatever happens to be the currently active texture, or we would risk
313 * marking already applied sampler states dirty again.
315 * TODO: Track the current active texture per GL context instead of using glGet
317 GLint active_texture;
319 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
321 active_sampler = This->resource.wineD3DDevice->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
323 if (active_sampler != WINED3D_UNMAPPED_STAGE)
325 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(active_sampler));
327 IWineD3DSurface_BindTexture((IWineD3DSurface *)This, srgb);
330 /* This function checks if the primary render target uses the 8bit paletted format. */
331 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
333 if (device->render_targets && device->render_targets[0]) {
334 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
335 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
336 && (render_target->resource.format_desc->format == WINED3DFMT_P8))
342 #undef GLINFO_LOCATION
344 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
346 /* This call just downloads data, the caller is responsible for binding the
347 * correct texture. */
348 /* Context activation is done by the caller. */
349 static void surface_download_data(IWineD3DSurfaceImpl *This) {
350 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
352 /* Only support read back of converted P8 surfaces */
353 if (This->Flags & SFLAG_CONVERTED && format_desc->format != WINED3DFMT_P8)
355 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(format_desc->format));
361 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
363 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
364 This, This->texture_level, format_desc->glFormat, format_desc->glType,
365 This->resource.allocatedMemory);
367 if (This->Flags & SFLAG_PBO)
369 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
370 checkGLcall("glBindBufferARB");
371 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target, This->texture_level, NULL));
372 checkGLcall("glGetCompressedTexImageARB");
373 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
374 checkGLcall("glBindBufferARB");
378 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target,
379 This->texture_level, This->resource.allocatedMemory));
380 checkGLcall("glGetCompressedTexImageARB");
386 GLenum format = format_desc->glFormat;
387 GLenum type = format_desc->glType;
391 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
392 if (format_desc->format == WINED3DFMT_P8 && primary_render_target_is_p8(This->resource.wineD3DDevice))
395 type = GL_UNSIGNED_BYTE;
398 if (This->Flags & SFLAG_NONPOW2) {
399 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
400 src_pitch = format_desc->byte_count * This->pow2Width;
401 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
402 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
403 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
405 mem = This->resource.allocatedMemory;
408 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
409 This, This->texture_level, format, type, mem);
411 if(This->Flags & SFLAG_PBO) {
412 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
413 checkGLcall("glBindBufferARB");
415 glGetTexImage(This->texture_target, This->texture_level, format, type, NULL);
416 checkGLcall("glGetTexImage");
418 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
419 checkGLcall("glBindBufferARB");
421 glGetTexImage(This->texture_target, This->texture_level, format, type, mem);
422 checkGLcall("glGetTexImage");
426 if (This->Flags & SFLAG_NONPOW2) {
427 const BYTE *src_data;
431 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
432 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
433 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
435 * We're doing this...
437 * instead of boxing the texture :
438 * |<-texture width ->| -->pow2width| /\
439 * |111111111111111111| | |
440 * |222 Texture 222222| boxed empty | texture height
441 * |3333 Data 33333333| | |
442 * |444444444444444444| | \/
443 * ----------------------------------- |
444 * | boxed empty | boxed empty | pow2height
446 * -----------------------------------
449 * we're repacking the data to the expected texture width
451 * |<-texture width ->| -->pow2width| /\
452 * |111111111111111111222222222222222| |
453 * |222333333333333333333444444444444| texture height
457 * | empty | pow2height
459 * -----------------------------------
463 * |<-texture width ->| /\
464 * |111111111111111111|
465 * |222222222222222222|texture height
466 * |333333333333333333|
467 * |444444444444444444| \/
468 * --------------------
470 * this also means that any references to allocatedMemory should work with the data as if were a
471 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
473 * internally the texture is still stored in a boxed format so any references to textureName will
474 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
476 * Performance should not be an issue, because applications normally do not lock the surfaces when
477 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
478 * and doesn't have to be re-read.
481 dst_data = This->resource.allocatedMemory;
482 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
483 for (y = 1 ; y < This->currentDesc.Height; y++) {
484 /* skip the first row */
485 src_data += src_pitch;
486 dst_data += dst_pitch;
487 memcpy(dst_data, src_data, dst_pitch);
490 HeapFree(GetProcessHeap(), 0, mem);
494 /* Surface has now been downloaded */
495 This->Flags |= SFLAG_INSYSMEM;
498 /* This call just uploads data, the caller is responsible for binding the
499 * correct texture. */
500 /* Context activation is done by the caller. */
501 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
502 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
504 TRACE("This %p, internal %#x, width %d, height %d, format %#x, type %#x, data %p.\n",
505 This, internal, width, height, format, type, data);
506 TRACE("target %#x, level %u, resource size %u.\n",
507 This->texture_target, This->texture_level, This->resource.size);
509 if (format_desc->heightscale != 1.0f && format_desc->heightscale != 0.0f) height *= format_desc->heightscale;
513 if (This->Flags & SFLAG_PBO)
515 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
516 checkGLcall("glBindBufferARB");
518 TRACE("(%p) pbo: %#x, data: %p.\n", This, This->pbo, data);
522 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
524 TRACE("Calling glCompressedTexSubImage2DARB.\n");
526 GL_EXTCALL(glCompressedTexSubImage2DARB(This->texture_target, This->texture_level,
527 0, 0, width, height, internal, This->resource.size, data));
528 checkGLcall("glCompressedTexSubImage2DARB");
532 TRACE("Calling glTexSubImage2D.\n");
534 glTexSubImage2D(This->texture_target, This->texture_level,
535 0, 0, width, height, format, type, data);
536 checkGLcall("glTexSubImage2D");
539 if (This->Flags & SFLAG_PBO)
541 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
542 checkGLcall("glBindBufferARB");
548 /* This call just allocates the texture, the caller is responsible for binding
549 * the correct texture. */
550 /* Context activation is done by the caller. */
551 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
552 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
553 BOOL enable_client_storage = FALSE;
554 const BYTE *mem = NULL;
556 if (format_desc->heightscale != 1.0f && format_desc->heightscale != 0.0f) height *= format_desc->heightscale;
558 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",
559 This, This->texture_target, This->texture_level, debug_d3dformat(format_desc->format),
560 internal, width, height, format, type);
564 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
565 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
566 /* In some cases we want to disable client storage.
567 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
568 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
569 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
570 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
571 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
573 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
574 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
575 This->Flags &= ~SFLAG_CLIENT;
576 enable_client_storage = TRUE;
578 This->Flags |= SFLAG_CLIENT;
580 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
581 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
583 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
587 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED && mem)
589 GL_EXTCALL(glCompressedTexImage2DARB(This->texture_target, This->texture_level,
590 internal, width, height, 0, This->resource.size, mem));
594 glTexImage2D(This->texture_target, This->texture_level,
595 internal, width, height, 0, format, type, mem);
596 checkGLcall("glTexImage2D");
599 if(enable_client_storage) {
600 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
601 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
606 /* In D3D the depth stencil dimensions have to be greater than or equal to the
607 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
608 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
609 /* GL locking is done by the caller */
610 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
611 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
612 renderbuffer_entry_t *entry;
613 GLuint renderbuffer = 0;
614 unsigned int src_width, src_height;
616 src_width = This->pow2Width;
617 src_height = This->pow2Height;
619 /* A depth stencil smaller than the render target is not valid */
620 if (width > src_width || height > src_height) return;
622 /* Remove any renderbuffer set if the sizes match */
623 if (width == src_width && height == src_height) {
624 This->current_renderbuffer = NULL;
628 /* Look if we've already got a renderbuffer of the correct dimensions */
629 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
630 if (entry->width == width && entry->height == height) {
631 renderbuffer = entry->id;
632 This->current_renderbuffer = entry;
638 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
639 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
640 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
641 This->resource.format_desc->glInternal, width, height));
643 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
644 entry->width = width;
645 entry->height = height;
646 entry->id = renderbuffer;
647 list_add_head(&This->renderbuffers, &entry->entry);
649 This->current_renderbuffer = entry;
652 checkGLcall("set_compatible_renderbuffer");
655 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
656 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
657 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
659 TRACE("(%p) : swapchain %p\n", This, swapchain);
661 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
662 TRACE("Returning GL_BACK\n");
664 } else if (swapchain_impl->frontBuffer == iface) {
665 TRACE("Returning GL_FRONT\n");
669 FIXME("Higher back buffer, returning GL_BACK\n");
673 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
674 void surface_add_dirty_rect(IWineD3DSurface *iface, const RECT *dirty_rect)
676 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
677 IWineD3DBaseTexture *baseTexture = NULL;
679 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
680 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
682 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
685 This->dirtyRect.left = min(This->dirtyRect.left, dirty_rect->left);
686 This->dirtyRect.top = min(This->dirtyRect.top, dirty_rect->top);
687 This->dirtyRect.right = max(This->dirtyRect.right, dirty_rect->right);
688 This->dirtyRect.bottom = max(This->dirtyRect.bottom, dirty_rect->bottom);
692 This->dirtyRect.left = 0;
693 This->dirtyRect.top = 0;
694 This->dirtyRect.right = This->currentDesc.Width;
695 This->dirtyRect.bottom = This->currentDesc.Height;
698 TRACE("(%p) : Dirty: yes, Rect:(%d, %d, %d, %d)\n", This, This->dirtyRect.left,
699 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
701 /* if the container is a basetexture then mark it dirty. */
702 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture)))
704 TRACE("Passing to container\n");
705 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
706 IWineD3DBaseTexture_Release(baseTexture);
710 static inline BOOL surface_can_stretch_rect(IWineD3DSurfaceImpl *src, IWineD3DSurfaceImpl *dst)
712 return ((src->resource.format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE)
713 || (src->resource.usage & WINED3DUSAGE_RENDERTARGET))
714 && ((dst->resource.format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE)
715 || (dst->resource.usage & WINED3DUSAGE_RENDERTARGET))
716 && (src->resource.format_desc->format == dst->resource.format_desc->format
717 || (is_identity_fixup(src->resource.format_desc->color_fixup)
718 && is_identity_fixup(dst->resource.format_desc->color_fixup)));
721 static ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface)
723 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
724 ULONG ref = InterlockedDecrement(&This->resource.ref);
725 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
729 surface_cleanup(This);
731 TRACE("(%p) Released.\n", This);
732 HeapFree(GetProcessHeap(), 0, This);
738 /* ****************************************************
739 IWineD3DSurface IWineD3DResource parts follow
740 **************************************************** */
742 void surface_internal_preload(IWineD3DSurface *iface, enum WINED3DSRGB srgb)
744 /* TODO: check for locks */
745 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
746 IWineD3DBaseTexture *baseTexture = NULL;
747 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
749 TRACE("(%p)Checking to see if the container is a base texture\n", This);
750 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
751 IWineD3DBaseTextureImpl *tex_impl = (IWineD3DBaseTextureImpl *) baseTexture;
752 TRACE("Passing to container\n");
753 tex_impl->baseTexture.internal_preload(baseTexture, srgb);
754 IWineD3DBaseTexture_Release(baseTexture);
756 TRACE("(%p) : About to load surface\n", This);
758 if(!device->isInDraw) {
759 ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
762 if (This->resource.format_desc->format == WINED3DFMT_P8
763 || This->resource.format_desc->format == WINED3DFMT_A8P8)
765 if(palette9_changed(This)) {
766 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
767 /* TODO: This is not necessarily needed with hw palettized texture support */
768 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
769 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
770 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
774 IWineD3DSurface_LoadTexture(iface, srgb == SRGB_SRGB ? TRUE : FALSE);
776 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
777 /* Tell opengl to try and keep this texture in video ram (well mostly) */
781 glPrioritizeTextures(1, &This->texture_name, &tmp);
788 static void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
789 surface_internal_preload(iface, SRGB_ANY);
792 /* Context activation is done by the caller. */
793 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
794 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
795 This->resource.allocatedMemory =
796 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
799 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
800 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
801 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
802 checkGLcall("glGetBufferSubDataARB");
803 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
804 checkGLcall("glDeleteBuffersARB");
808 This->Flags &= ~SFLAG_PBO;
811 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
812 IWineD3DBaseTexture *texture = NULL;
813 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
814 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
815 renderbuffer_entry_t *entry, *entry2;
816 TRACE("(%p)\n", iface);
818 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
819 /* Default pool resources are supposed to be destroyed before Reset is called.
820 * Implicit resources stay however. So this means we have an implicit render target
821 * or depth stencil. The content may be destroyed, but we still have to tear down
822 * opengl resources, so we cannot leave early.
824 * Put the most up to date surface location into the drawable. D3D-wise this content
825 * is undefined, so it would be nowhere, but that would make the location management
826 * more complicated. The drawable is a sane location, because if we mark sysmem or
827 * texture up to date, drawPrim will copy the uninitialized texture or sysmem to the
828 * uninitialized drawable. That's pointless and we'd have to allocate the texture /
831 if (This->resource.usage & WINED3DUSAGE_DEPTHSTENCIL) {
832 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
834 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, TRUE);
837 /* Load the surface into system memory */
838 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
839 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
841 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
842 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSRGBTEX, FALSE);
843 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
845 ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
847 /* Destroy PBOs, but load them into real sysmem before */
848 if(This->Flags & SFLAG_PBO) {
849 surface_remove_pbo(This);
852 /* Destroy fbo render buffers. This is needed for implicit render targets, for
853 * all application-created targets the application has to release the surface
854 * before calling _Reset
856 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
858 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
860 list_remove(&entry->entry);
861 HeapFree(GetProcessHeap(), 0, entry);
863 list_init(&This->renderbuffers);
864 This->current_renderbuffer = NULL;
866 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
869 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
872 glDeleteTextures(1, &This->texture_name);
873 This->texture_name = 0;
874 glDeleteTextures(1, &This->texture_name_srgb);
875 This->texture_name_srgb = 0;
878 IWineD3DBaseTexture_Release(texture);
883 /* ******************************************************
884 IWineD3DSurface IWineD3DSurface parts follow
885 ****************************************************** */
887 /* Read the framebuffer back into the surface */
888 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
889 IWineD3DSwapChainImpl *swapchain;
890 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
894 BYTE *row, *top, *bottom;
898 BOOL srcIsUpsideDown;
903 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
904 static BOOL warned = FALSE;
906 ERR("The application tries to lock the render target, but render target locking is disabled\n");
912 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
913 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
914 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
915 * context->last_was_blit set on the unlock.
917 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
920 /* Select the correct read buffer, and give some debug output.
921 * There is no need to keep track of the current read buffer or reset it, every part of the code
922 * that reads sets the read buffer as desired.
924 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
926 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
927 TRACE("Locking %#x buffer\n", buffer);
928 glReadBuffer(buffer);
929 checkGLcall("glReadBuffer");
931 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
932 srcIsUpsideDown = FALSE;
934 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
935 * Read from the back buffer
937 TRACE("Locking offscreen render target\n");
938 glReadBuffer(myDevice->offscreenBuffer);
939 srcIsUpsideDown = TRUE;
942 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
946 local_rect.right = This->currentDesc.Width;
947 local_rect.bottom = This->currentDesc.Height;
951 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
953 switch(This->resource.format_desc->format)
957 if(primary_render_target_is_p8(myDevice)) {
958 /* In case of P8 render targets the index is stored in the alpha component */
960 type = GL_UNSIGNED_BYTE;
962 bpp = This->resource.format_desc->byte_count;
964 /* GL can't return palettized data, so read ARGB pixels into a
965 * separate block of memory and convert them into palettized format
966 * in software. Slow, but if the app means to use palettized render
967 * targets and locks it...
969 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
970 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
971 * for the color channels when palettizing the colors.
974 type = GL_UNSIGNED_BYTE;
976 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
978 ERR("Out of memory\n");
982 bpp = This->resource.format_desc->byte_count * 3;
989 fmt = This->resource.format_desc->glFormat;
990 type = This->resource.format_desc->glType;
991 bpp = This->resource.format_desc->byte_count;
994 if(This->Flags & SFLAG_PBO) {
995 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
996 checkGLcall("glBindBufferARB");
998 ERR("mem not null for pbo -- unexpected\n");
1003 /* Save old pixel store pack state */
1004 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
1005 checkGLcall("glGetIntegerv");
1006 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
1007 checkGLcall("glGetIntegerv");
1008 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
1009 checkGLcall("glGetIntegerv");
1011 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1012 glPixelStorei(GL_PACK_ROW_LENGTH, This->currentDesc.Width);
1013 checkGLcall("glPixelStorei");
1014 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
1015 checkGLcall("glPixelStorei");
1016 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
1017 checkGLcall("glPixelStorei");
1019 glReadPixels(local_rect.left, (!srcIsUpsideDown) ? (This->currentDesc.Height - local_rect.bottom) : local_rect.top ,
1020 local_rect.right - local_rect.left,
1021 local_rect.bottom - local_rect.top,
1023 checkGLcall("glReadPixels");
1025 /* Reset previous pixel store pack state */
1026 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
1027 checkGLcall("glPixelStorei");
1028 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
1029 checkGLcall("glPixelStorei");
1030 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
1031 checkGLcall("glPixelStorei");
1033 if(This->Flags & SFLAG_PBO) {
1034 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1035 checkGLcall("glBindBufferARB");
1037 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
1038 * to get a pointer to it and perform the flipping in software. This is a lot
1039 * faster than calling glReadPixels for each line. In case we want more speed
1040 * we should rerender it flipped in a FBO and read the data back from the FBO. */
1041 if(!srcIsUpsideDown) {
1042 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1043 checkGLcall("glBindBufferARB");
1045 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1046 checkGLcall("glMapBufferARB");
1050 /* TODO: Merge this with the palettization loop below for P8 targets */
1051 if(!srcIsUpsideDown) {
1053 /* glReadPixels returns the image upside down, and there is no way to prevent this.
1054 Flip the lines in software */
1055 len = (local_rect.right - local_rect.left) * bpp;
1056 off = local_rect.left * bpp;
1058 row = HeapAlloc(GetProcessHeap(), 0, len);
1060 ERR("Out of memory\n");
1061 if (This->resource.format_desc->format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
1066 top = mem + pitch * local_rect.top;
1067 bottom = mem + pitch * (local_rect.bottom - 1);
1068 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
1069 memcpy(row, top + off, len);
1070 memcpy(top + off, bottom + off, len);
1071 memcpy(bottom + off, row, len);
1075 HeapFree(GetProcessHeap(), 0, row);
1077 /* Unmap the temp PBO buffer */
1078 if(This->Flags & SFLAG_PBO) {
1079 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1080 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1086 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
1087 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
1088 * the same color but we have no choice.
1089 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
1091 if ((This->resource.format_desc->format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice))
1093 const PALETTEENTRY *pal = NULL;
1094 DWORD width = pitch / 3;
1098 pal = This->palette->palents;
1100 ERR("Palette is missing, cannot perform inverse palette lookup\n");
1101 HeapFree(GetProcessHeap(), 0, mem);
1105 for(y = local_rect.top; y < local_rect.bottom; y++) {
1106 for(x = local_rect.left; x < local_rect.right; x++) {
1107 /* start lines pixels */
1108 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
1109 const BYTE *green = blue + 1;
1110 const BYTE *red = green + 1;
1112 for(c = 0; c < 256; c++) {
1113 if(*red == pal[c].peRed &&
1114 *green == pal[c].peGreen &&
1115 *blue == pal[c].peBlue)
1117 *((BYTE *) dest + y * width + x) = c;
1123 HeapFree(GetProcessHeap(), 0, mem);
1127 /* Read the framebuffer contents into a texture */
1128 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This, BOOL srgb)
1130 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1131 IWineD3DSwapChainImpl *swapchain;
1133 GLenum format, internal, type;
1134 CONVERT_TYPES convert;
1136 BOOL alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
1138 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
1140 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
1141 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
1142 * states in the stateblock, and no driver was found yet that had bugs in that regard.
1144 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
1145 surface_bind_and_dirtify(This, srgb);
1148 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1151 /* Select the correct read buffer, and give some debug output.
1152 * There is no need to keep track of the current read buffer or reset it, every part of the code
1153 * that reads sets the read buffer as desired.
1155 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
1157 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1158 TRACE("Locking %#x buffer\n", buffer);
1161 glReadBuffer(buffer);
1162 checkGLcall("glReadBuffer");
1165 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1167 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1168 * Read from the back buffer
1170 TRACE("Locking offscreen render target\n");
1172 glReadBuffer(device->offscreenBuffer);
1173 checkGLcall("glReadBuffer");
1177 if(!(This->Flags & alloc_flag)) {
1178 surface_allocate_surface(This, internal, This->pow2Width,
1179 This->pow2Height, format, type);
1180 This->Flags |= alloc_flag;
1184 /* If !SrcIsUpsideDown we should flip the surface.
1185 * This can be done using glCopyTexSubImage2D but this
1186 * is VERY slow, so don't do that. We should prevent
1187 * this code from getting called in such cases or perhaps
1188 * we can use FBOs */
1190 glCopyTexSubImage2D(This->texture_target, This->texture_level,
1191 0, 0, 0, 0, This->currentDesc.Width, This->currentDesc.Height);
1192 checkGLcall("glCopyTexSubImage2D");
1194 glReadBuffer(prevRead);
1195 checkGLcall("glReadBuffer");
1198 TRACE("Updated target %d\n", This->texture_target);
1201 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
1202 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
1203 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
1206 if(!(This->Flags & SFLAG_DYNLOCK)) {
1208 /* MAXLOCKCOUNT is defined in wined3d_private.h */
1209 if(This->lockCount > MAXLOCKCOUNT) {
1210 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
1211 This->Flags |= SFLAG_DYNLOCK;
1215 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
1216 * Also don't create a PBO for systemmem surfaces.
1218 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
1220 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1222 ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
1225 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
1226 error = glGetError();
1227 if(This->pbo == 0 || error != GL_NO_ERROR) {
1228 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
1231 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
1233 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1234 checkGLcall("glBindBufferARB");
1236 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1237 checkGLcall("glBufferDataARB");
1239 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1240 checkGLcall("glBindBufferARB");
1242 /* We don't need the system memory anymore and we can't even use it for PBOs */
1243 if(!(This->Flags & SFLAG_CLIENT)) {
1244 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1245 This->resource.heapMemory = NULL;
1247 This->resource.allocatedMemory = NULL;
1248 This->Flags |= SFLAG_PBO;
1250 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
1251 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1254 if(!This->resource.heapMemory) {
1255 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1257 This->resource.allocatedMemory =
1258 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1259 if(This->Flags & SFLAG_INSYSMEM) {
1260 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1265 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1266 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1267 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1268 const RECT *pass_rect = pRect;
1270 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1272 /* This is also done in the base class, but we have to verify this before loading any data from
1273 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1274 * may interfere, and all other bad things may happen
1276 if (This->Flags & SFLAG_LOCKED) {
1277 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1278 return WINED3DERR_INVALIDCALL;
1280 This->Flags |= SFLAG_LOCKED;
1282 if (!(This->Flags & SFLAG_LOCKABLE))
1284 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1287 if (Flags & WINED3DLOCK_DISCARD) {
1288 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1289 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1290 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1291 This->Flags |= SFLAG_INSYSMEM;
1295 if (This->Flags & SFLAG_INSYSMEM) {
1296 TRACE("Local copy is up to date, not downloading data\n");
1297 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1301 /* IWineD3DSurface_LoadLocation() does not check if the rectangle specifies
1302 * the full surface. Most callers don't need that, so do it here. */
1303 if (pRect && pRect->top == 0 && pRect->left == 0
1304 && pRect->right == This->currentDesc.Width
1305 && pRect->bottom == This->currentDesc.Height)
1310 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
1311 && ((This->Flags & SFLAG_SWAPCHAIN) || iface == myDevice->render_targets[0])))
1313 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1317 if(This->Flags & SFLAG_PBO) {
1318 ActivateContext(myDevice, NULL, CTXUSAGE_RESOURCELOAD);
1320 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1321 checkGLcall("glBindBufferARB");
1323 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1324 if(This->resource.allocatedMemory) {
1325 ERR("The surface already has PBO memory allocated!\n");
1328 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1329 checkGLcall("glMapBufferARB");
1331 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1332 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1333 checkGLcall("glBindBufferARB");
1338 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1341 IWineD3DBaseTexture *pBaseTexture;
1344 * as seen in msdn docs
1346 surface_add_dirty_rect(iface, pRect);
1348 /** Dirtify Container if needed */
1349 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1350 TRACE("Making container dirty\n");
1351 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1352 IWineD3DBaseTexture_Release(pBaseTexture);
1354 TRACE("Surface is standalone, no need to dirty the container\n");
1358 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1361 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1363 GLint prev_rasterpos[4];
1364 GLint skipBytes = 0;
1365 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1366 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1367 IWineD3DSwapChainImpl *swapchain;
1369 /* Activate the correct context for the render target */
1370 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1373 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
1374 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1375 TRACE("Unlocking %#x buffer\n", buffer);
1376 glDrawBuffer(buffer);
1377 checkGLcall("glDrawBuffer");
1379 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1381 /* Primary offscreen render target */
1382 TRACE("Offscreen render target\n");
1383 glDrawBuffer(myDevice->offscreenBuffer);
1384 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1387 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1388 checkGLcall("glGetIntegerv");
1389 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1390 checkGLcall("glGetIntegerv");
1391 glPixelZoom(1.0f, -1.0f);
1392 checkGLcall("glPixelZoom");
1394 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1395 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1396 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1398 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1399 checkGLcall("glRasterPos3i");
1401 /* Some drivers(radeon dri, others?) don't like exceptions during
1402 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1403 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1404 * catch to put the dib section in InSync mode, which leads to a crash
1405 * and a blocked x server on my radeon card.
1407 * The following lines read the dib section so it is put in InSync mode
1408 * before glDrawPixels is called and the crash is prevented. There won't
1409 * be any interfering gdi accesses, because UnlockRect is called from
1410 * ReleaseDC, and the app won't use the dc any more afterwards.
1412 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1414 read = This->resource.allocatedMemory[0];
1417 if(This->Flags & SFLAG_PBO) {
1418 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1419 checkGLcall("glBindBufferARB");
1422 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1423 if(This->Flags & SFLAG_LOCKED) {
1424 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1425 (This->lockedRect.bottom - This->lockedRect.top)-1,
1427 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1428 checkGLcall("glDrawPixels");
1430 glDrawPixels(This->currentDesc.Width,
1431 This->currentDesc.Height,
1433 checkGLcall("glDrawPixels");
1436 if(This->Flags & SFLAG_PBO) {
1437 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1438 checkGLcall("glBindBufferARB");
1441 glPixelZoom(1.0f, 1.0f);
1442 checkGLcall("glPixelZoom");
1444 glRasterPos3iv(&prev_rasterpos[0]);
1445 checkGLcall("glRasterPos3iv");
1447 /* Reset to previous pack row length */
1448 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1449 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH)");
1452 glDrawBuffer(myDevice->offscreenBuffer);
1453 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1454 } else if(swapchain->backBuffer) {
1455 glDrawBuffer(GL_BACK);
1456 checkGLcall("glDrawBuffer(GL_BACK)");
1458 glDrawBuffer(GL_FRONT);
1459 checkGLcall("glDrawBuffer(GL_FRONT)");
1466 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1467 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1468 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1471 if (!(This->Flags & SFLAG_LOCKED)) {
1472 WARN("trying to Unlock an unlocked surf@%p\n", This);
1473 return WINEDDERR_NOTLOCKED;
1476 if (This->Flags & SFLAG_PBO) {
1477 TRACE("Freeing PBO memory\n");
1478 ActivateContext(myDevice, NULL, CTXUSAGE_RESOURCELOAD);
1480 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1481 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1482 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1483 checkGLcall("glUnmapBufferARB");
1485 This->resource.allocatedMemory = NULL;
1488 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1490 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1491 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1495 if ((This->Flags & SFLAG_SWAPCHAIN) || (myDevice->render_targets && iface == myDevice->render_targets[0]))
1497 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1498 static BOOL warned = FALSE;
1500 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1506 if(This->dirtyRect.left == 0 &&
1507 This->dirtyRect.top == 0 &&
1508 This->dirtyRect.right == This->currentDesc.Width &&
1509 This->dirtyRect.bottom == This->currentDesc.Height) {
1512 /* TODO: Proper partial rectangle tracking */
1513 fullsurface = FALSE;
1514 This->Flags |= SFLAG_INSYSMEM;
1517 switch(wined3d_settings.rendertargetlock_mode) {
1519 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1523 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1528 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1529 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1530 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1531 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1532 * not fully up to date because only a subrectangle was read in LockRect.
1534 This->Flags &= ~SFLAG_INSYSMEM;
1535 This->Flags |= SFLAG_INDRAWABLE;
1538 This->dirtyRect.left = This->currentDesc.Width;
1539 This->dirtyRect.top = This->currentDesc.Height;
1540 This->dirtyRect.right = 0;
1541 This->dirtyRect.bottom = 0;
1542 } else if(iface == myDevice->stencilBufferTarget) {
1543 FIXME("Depth Stencil buffer locking is not implemented\n");
1545 /* The rest should be a normal texture */
1546 IWineD3DBaseTextureImpl *impl;
1547 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1548 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1549 * states need resetting
1551 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1552 if(impl->baseTexture.bindCount) {
1553 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1555 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1560 This->Flags &= ~SFLAG_LOCKED;
1561 memset(&This->lockedRect, 0, sizeof(RECT));
1563 /* Overlays have to be redrawn manually after changes with the GL implementation */
1564 if(This->overlay_dest) {
1565 IWineD3DSurface_DrawOverlay(iface);
1570 static HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC)
1572 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1573 WINED3DLOCKED_RECT lock;
1577 TRACE("(%p)->(%p)\n",This,pHDC);
1579 if(This->Flags & SFLAG_USERPTR) {
1580 ERR("Not supported on surfaces with an application-provided surfaces\n");
1581 return WINEDDERR_NODC;
1584 /* Give more detailed info for ddraw */
1585 if (This->Flags & SFLAG_DCINUSE)
1586 return WINEDDERR_DCALREADYCREATED;
1588 /* Can't GetDC if the surface is locked */
1589 if (This->Flags & SFLAG_LOCKED)
1590 return WINED3DERR_INVALIDCALL;
1592 memset(&lock, 0, sizeof(lock)); /* To be sure */
1594 /* Create a DIB section if there isn't a hdc yet */
1596 hr = IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1597 if(FAILED(hr)) return WINED3DERR_INVALIDCALL;
1598 if(This->Flags & SFLAG_CLIENT) {
1599 surface_internal_preload(iface, SRGB_RGB);
1602 /* Use the dib section from now on if we are not using a PBO */
1603 if(!(This->Flags & SFLAG_PBO))
1604 This->resource.allocatedMemory = This->dib.bitmap_data;
1607 /* Lock the surface */
1608 hr = IWineD3DSurface_LockRect(iface,
1613 if(This->Flags & SFLAG_PBO) {
1614 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1615 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1619 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1620 /* keep the dib section */
1624 if (This->resource.format_desc->format == WINED3DFMT_P8
1625 || This->resource.format_desc->format == WINED3DFMT_A8P8)
1627 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1628 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1630 const PALETTEENTRY *pal = NULL;
1633 pal = This->palette->palents;
1635 IWineD3DSurfaceImpl *dds_primary;
1636 IWineD3DSwapChainImpl *swapchain;
1637 swapchain = (IWineD3DSwapChainImpl *)This->resource.wineD3DDevice->swapchains[0];
1638 dds_primary = (IWineD3DSurfaceImpl *)swapchain->frontBuffer;
1639 if (dds_primary && dds_primary->palette)
1640 pal = dds_primary->palette->palents;
1644 for (n=0; n<256; n++) {
1645 col[n].rgbRed = pal[n].peRed;
1646 col[n].rgbGreen = pal[n].peGreen;
1647 col[n].rgbBlue = pal[n].peBlue;
1648 col[n].rgbReserved = 0;
1650 SetDIBColorTable(This->hDC, 0, 256, col);
1655 TRACE("returning %p\n",*pHDC);
1656 This->Flags |= SFLAG_DCINUSE;
1661 static HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC)
1663 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1665 TRACE("(%p)->(%p)\n",This,hDC);
1667 if (!(This->Flags & SFLAG_DCINUSE))
1668 return WINEDDERR_NODC;
1670 if (This->hDC !=hDC) {
1671 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1672 return WINEDDERR_NODC;
1675 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1676 /* Copy the contents of the DIB over to the PBO */
1677 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1680 /* we locked first, so unlock now */
1681 IWineD3DSurface_UnlockRect(iface);
1683 This->Flags &= ~SFLAG_DCINUSE;
1688 /* ******************************************************
1689 IWineD3DSurface Internal (No mapping to directx api) parts follow
1690 ****************************************************** */
1692 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) {
1693 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1694 const struct GlPixelFormatDesc *glDesc = This->resource.format_desc;
1695 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1697 /* Default values: From the surface */
1698 *format = glDesc->glFormat;
1699 *type = glDesc->glType;
1700 *convert = NO_CONVERSION;
1701 *target_bpp = glDesc->byte_count;
1704 *internal = glDesc->glGammaInternal;
1706 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET
1707 && !(This->Flags & SFLAG_SWAPCHAIN))
1709 *internal = glDesc->rtInternal;
1711 *internal = glDesc->glInternal;
1714 /* Ok, now look if we have to do any conversion */
1715 switch(This->resource.format_desc->format)
1722 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1723 * of the two is available make sure texturing is requested as neither of the two works in
1724 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1725 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1726 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1727 * conflicts with this.
1729 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) ||
1730 (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) &&
1731 device->render_targets &&
1732 This == (IWineD3DSurfaceImpl*)device->render_targets[0])) ||
1733 colorkey_active || !use_texturing ) {
1735 *internal = GL_RGBA;
1736 *type = GL_UNSIGNED_BYTE;
1738 if(colorkey_active) {
1739 *convert = CONVERT_PALETTED_CK;
1741 *convert = CONVERT_PALETTED;
1744 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1746 *type = GL_UNSIGNED_BYTE;
1752 case WINED3DFMT_R3G3B2:
1753 /* **********************
1754 GL_UNSIGNED_BYTE_3_3_2
1755 ********************** */
1756 if (colorkey_active) {
1757 /* This texture format will never be used.. So do not care about color keying
1758 up until the point in time it will be needed :-) */
1759 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1763 case WINED3DFMT_R5G6B5:
1764 if (colorkey_active) {
1765 *convert = CONVERT_CK_565;
1767 *internal = GL_RGB5_A1;
1768 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1772 case WINED3DFMT_X1R5G5B5:
1773 if (colorkey_active) {
1774 *convert = CONVERT_CK_5551;
1776 *internal = GL_RGB5_A1;
1777 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1781 case WINED3DFMT_R8G8B8:
1782 if (colorkey_active) {
1783 *convert = CONVERT_CK_RGB24;
1785 *internal = GL_RGBA8;
1786 *type = GL_UNSIGNED_INT_8_8_8_8;
1791 case WINED3DFMT_X8R8G8B8:
1792 if (colorkey_active) {
1793 *convert = CONVERT_RGB32_888;
1795 *internal = GL_RGBA8;
1796 *type = GL_UNSIGNED_INT_8_8_8_8;
1800 case WINED3DFMT_R8G8_SNORM:
1801 if (GL_SUPPORT(NV_TEXTURE_SHADER)) break;
1802 *convert = CONVERT_V8U8;
1804 *type = GL_UNSIGNED_BYTE;
1808 case WINED3DFMT_L6V5U5:
1809 *convert = CONVERT_L6V5U5;
1810 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1812 /* Use format and types from table */
1814 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1817 *type = GL_UNSIGNED_SHORT_5_6_5;
1821 case WINED3DFMT_X8L8V8U8:
1822 *convert = CONVERT_X8L8V8U8;
1824 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1825 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1826 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1827 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1828 * the needed type and format parameter, so the internal format contains a
1829 * 4th component, which is returned as alpha
1833 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1837 case WINED3DFMT_R8G8B8A8_SNORM:
1838 if (GL_SUPPORT(NV_TEXTURE_SHADER)) break;
1839 *convert = CONVERT_Q8W8V8U8;
1841 *type = GL_UNSIGNED_BYTE;
1845 case WINED3DFMT_R16G16_SNORM:
1846 if (GL_SUPPORT(NV_TEXTURE_SHADER)) break;
1847 *convert = CONVERT_V16U16;
1849 *type = GL_UNSIGNED_SHORT;
1853 case WINED3DFMT_A4L4:
1854 /* A4L4 exists as an internal gl format, but for some reason there is not
1855 * format+type combination to load it. Thus convert it to A8L8, then load it
1856 * with A4L4 internal, but A8L8 format+type
1858 *convert = CONVERT_A4L4;
1859 *format = GL_LUMINANCE_ALPHA;
1860 *type = GL_UNSIGNED_BYTE;
1864 case WINED3DFMT_R16G16_UNORM:
1865 *convert = CONVERT_G16R16;
1867 *type = GL_UNSIGNED_SHORT;
1871 case WINED3DFMT_R16G16_FLOAT:
1872 *convert = CONVERT_R16G16F;
1874 *type = GL_HALF_FLOAT_ARB;
1878 case WINED3DFMT_R32G32_FLOAT:
1879 *convert = CONVERT_R32G32F;
1885 case WINED3DFMT_D15S1:
1886 if (GL_SUPPORT(EXT_PACKED_DEPTH_STENCIL))
1888 *convert = CONVERT_D15S1;
1893 case WINED3DFMT_D24X4S4:
1894 if (GL_SUPPORT(EXT_PACKED_DEPTH_STENCIL))
1896 *convert = CONVERT_D24X4S4;
1900 case WINED3DFMT_D24FS8:
1901 if (GL_SUPPORT(ARB_DEPTH_BUFFER_FLOAT))
1903 *convert = CONVERT_D24FS8;
1915 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey)
1917 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1918 IWineD3DPaletteImpl *pal = This->palette;
1919 BOOL index_in_alpha = FALSE;
1922 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
1923 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
1924 * is slow. Further RGB->P8 conversion is not possible because palettes can have
1925 * duplicate entries. Store the color key in the unused alpha component to speed the
1926 * download up and to make conversion unneeded. */
1927 index_in_alpha = primary_render_target_is_p8(device);
1931 UINT dxVersion = ((IWineD3DImpl *)device->wineD3D)->dxVersion;
1933 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
1936 ERR("This code should never get entered for DirectDraw!, expect problems\n");
1939 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
1940 * there's no palette at this time. */
1941 for (i = 0; i < 256; i++) table[i][3] = i;
1946 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
1947 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
1948 * capability flag is present (wine does advertise this capability) */
1949 for (i = 0; i < 256; ++i)
1951 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1952 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1953 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1954 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
1960 TRACE("Using surface palette %p\n", pal);
1961 /* Get the surface's palette */
1962 for (i = 0; i < 256; ++i)
1964 table[i][0] = pal->palents[i].peRed;
1965 table[i][1] = pal->palents[i].peGreen;
1966 table[i][2] = pal->palents[i].peBlue;
1968 /* When index_in_alpha is set the palette index is stored in the
1969 * alpha component. In case of a readback we can then read
1970 * GL_ALPHA. Color keying is handled in BltOverride using a
1971 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
1972 * color key itself is passed to glAlphaFunc in other cases the
1973 * alpha component of pixels that should be masked away is set to 0. */
1978 else if (colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue)
1979 && (i <= This->SrcBltCKey.dwColorSpaceHighValue))
1983 else if(pal->Flags & WINEDDPCAPS_ALPHA)
1985 table[i][3] = pal->palents[i].peFlags;
1995 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
1996 UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This)
2000 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
2005 memcpy(dst, src, pitch * height);
2008 case CONVERT_PALETTED:
2009 case CONVERT_PALETTED_CK:
2011 IWineD3DPaletteImpl* pal = This->palette;
2016 /* TODO: If we are a sublevel, try to get the palette from level 0 */
2019 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2021 for (y = 0; y < height; y++)
2023 source = src + pitch * y;
2024 dest = dst + outpitch * y;
2025 /* This is an 1 bpp format, using the width here is fine */
2026 for (x = 0; x < width; x++) {
2027 BYTE color = *source++;
2028 *dest++ = table[color][0];
2029 *dest++ = table[color][1];
2030 *dest++ = table[color][2];
2031 *dest++ = table[color][3];
2037 case CONVERT_CK_565:
2039 /* Converting the 565 format in 5551 packed to emulate color-keying.
2041 Note : in all these conversion, it would be best to average the averaging
2042 pixels to get the color of the pixel that will be color-keyed to
2043 prevent 'color bleeding'. This will be done later on if ever it is
2046 Note2: Nvidia documents say that their driver does not support alpha + color keying
2047 on the same surface and disables color keying in such a case
2053 TRACE("Color keyed 565\n");
2055 for (y = 0; y < height; y++) {
2056 Source = (const WORD *)(src + y * pitch);
2057 Dest = (WORD *) (dst + y * outpitch);
2058 for (x = 0; x < width; x++ ) {
2059 WORD color = *Source++;
2060 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
2061 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2062 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2071 case CONVERT_CK_5551:
2073 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
2077 TRACE("Color keyed 5551\n");
2078 for (y = 0; y < height; y++) {
2079 Source = (const WORD *)(src + y * pitch);
2080 Dest = (WORD *) (dst + y * outpitch);
2081 for (x = 0; x < width; x++ ) {
2082 WORD color = *Source++;
2084 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2085 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2089 *Dest &= ~(1 << 15);
2097 case CONVERT_CK_RGB24:
2099 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
2101 for (y = 0; y < height; y++)
2103 source = src + pitch * y;
2104 dest = dst + outpitch * y;
2105 for (x = 0; x < width; x++) {
2106 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
2107 DWORD dstcolor = color << 8;
2108 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2109 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2112 *(DWORD*)dest = dstcolor;
2120 case CONVERT_RGB32_888:
2122 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
2124 for (y = 0; y < height; y++)
2126 source = src + pitch * y;
2127 dest = dst + outpitch * y;
2128 for (x = 0; x < width; x++) {
2129 DWORD color = 0xffffff & *(const DWORD*)source;
2130 DWORD dstcolor = color << 8;
2131 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2132 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2135 *(DWORD*)dest = dstcolor;
2146 const short *Source;
2147 unsigned char *Dest;
2148 for(y = 0; y < height; y++) {
2149 Source = (const short *)(src + y * pitch);
2150 Dest = dst + y * outpitch;
2151 for (x = 0; x < width; x++ ) {
2152 long color = (*Source++);
2153 /* B */ Dest[0] = 0xff;
2154 /* G */ Dest[1] = (color >> 8) + 128; /* V */
2155 /* R */ Dest[2] = (color) + 128; /* U */
2162 case CONVERT_V16U16:
2165 const DWORD *Source;
2166 unsigned short *Dest;
2167 for(y = 0; y < height; y++) {
2168 Source = (const DWORD *)(src + y * pitch);
2169 Dest = (unsigned short *) (dst + y * outpitch);
2170 for (x = 0; x < width; x++ ) {
2171 DWORD color = (*Source++);
2172 /* B */ Dest[0] = 0xffff;
2173 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
2174 /* R */ Dest[2] = (color ) + 32768; /* U */
2181 case CONVERT_Q8W8V8U8:
2184 const DWORD *Source;
2185 unsigned char *Dest;
2186 for(y = 0; y < height; y++) {
2187 Source = (const DWORD *)(src + y * pitch);
2188 Dest = dst + y * outpitch;
2189 for (x = 0; x < width; x++ ) {
2190 long color = (*Source++);
2191 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
2192 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2193 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2194 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
2201 case CONVERT_L6V5U5:
2205 unsigned char *Dest;
2207 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2208 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
2209 * fixed function and shaders without further conversion once the surface is
2212 for(y = 0; y < height; y++) {
2213 Source = (const WORD *)(src + y * pitch);
2214 Dest = dst + y * outpitch;
2215 for (x = 0; x < width; x++ ) {
2216 short color = (*Source++);
2217 unsigned char l = ((color >> 10) & 0xfc);
2218 char v = ((color >> 5) & 0x3e);
2219 char u = ((color ) & 0x1f);
2221 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
2222 * and doubles the positive range. Thus shift left only once, gl does the 2nd
2223 * shift. GL reads a signed value and converts it into an unsigned value.
2225 /* M */ Dest[2] = l << 1;
2227 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
2228 * from 5 bit values to 8 bit values.
2230 /* V */ Dest[1] = v << 3;
2231 /* U */ Dest[0] = u << 3;
2236 for(y = 0; y < height; y++) {
2237 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
2238 Source = (const WORD *)(src + y * pitch);
2239 for (x = 0; x < width; x++ ) {
2240 short color = (*Source++);
2241 unsigned char l = ((color >> 10) & 0xfc);
2242 short v = ((color >> 5) & 0x3e);
2243 short u = ((color ) & 0x1f);
2244 short v_conv = v + 16;
2245 short u_conv = u + 16;
2247 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
2255 case CONVERT_X8L8V8U8:
2258 const DWORD *Source;
2259 unsigned char *Dest;
2261 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2262 /* This implementation works with the fixed function pipeline and shaders
2263 * without further modification after converting the surface.
2265 for(y = 0; y < height; y++) {
2266 Source = (const DWORD *)(src + y * pitch);
2267 Dest = dst + y * outpitch;
2268 for (x = 0; x < width; x++ ) {
2269 long color = (*Source++);
2270 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
2271 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
2272 /* U */ Dest[0] = (color & 0xff); /* U */
2273 /* I */ Dest[3] = 255; /* X */
2278 /* Doesn't work correctly with the fixed function pipeline, but can work in
2279 * shaders if the shader is adjusted. (There's no use for this format in gl's
2280 * standard fixed function pipeline anyway).
2282 for(y = 0; y < height; y++) {
2283 Source = (const DWORD *)(src + y * pitch);
2284 Dest = dst + y * outpitch;
2285 for (x = 0; x < width; x++ ) {
2286 long color = (*Source++);
2287 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
2288 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2289 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2300 const unsigned char *Source;
2301 unsigned char *Dest;
2302 for(y = 0; y < height; y++) {
2303 Source = src + y * pitch;
2304 Dest = dst + y * outpitch;
2305 for (x = 0; x < width; x++ ) {
2306 unsigned char color = (*Source++);
2307 /* A */ Dest[1] = (color & 0xf0) << 0;
2308 /* L */ Dest[0] = (color & 0x0f) << 4;
2315 case CONVERT_G16R16:
2316 case CONVERT_R16G16F:
2322 for(y = 0; y < height; y++) {
2323 Source = (const WORD *)(src + y * pitch);
2324 Dest = (WORD *) (dst + y * outpitch);
2325 for (x = 0; x < width; x++ ) {
2326 WORD green = (*Source++);
2327 WORD red = (*Source++);
2330 /* Strictly speaking not correct for R16G16F, but it doesn't matter because the
2331 * shader overwrites it anyway
2340 case CONVERT_R32G32F:
2343 const float *Source;
2345 for(y = 0; y < height; y++) {
2346 Source = (const float *)(src + y * pitch);
2347 Dest = (float *) (dst + y * outpitch);
2348 for (x = 0; x < width; x++ ) {
2349 float green = (*Source++);
2350 float red = (*Source++);
2364 for (y = 0; y < height; ++y)
2366 const WORD *source = (const WORD *)(src + y * pitch);
2367 DWORD *dest = (DWORD *)(dst + y * outpitch);
2369 for (x = 0; x < width; ++x)
2371 /* The depth data is normalized, so needs to be scaled,
2372 * the stencil data isn't. Scale depth data by
2373 * (2^24-1)/(2^15-1) ~~ (2^9 + 2^-6). */
2374 WORD d15 = source[x] >> 1;
2375 DWORD d24 = (d15 << 9) + (d15 >> 6);
2376 dest[x] = (d24 << 8) | (source[x] & 0x1);
2382 case CONVERT_D24X4S4:
2386 for (y = 0; y < height; ++y)
2388 const DWORD *source = (const DWORD *)(src + y * pitch);
2389 DWORD *dest = (DWORD *)(dst + y * outpitch);
2391 for (x = 0; x < width; ++x)
2393 /* Just need to clear out the X4 part. */
2394 dest[x] = source[x] & ~0xf0;
2400 case CONVERT_D24FS8:
2404 for (y = 0; y < height; ++y)
2406 const DWORD *source = (const DWORD *)(src + y * pitch);
2407 float *dest_f = (float *)(dst + y * outpitch);
2408 DWORD *dest_s = (DWORD *)(dst + y * outpitch);
2410 for (x = 0; x < width; ++x)
2412 dest_f[x * 2] = float_24_to_32((source[x] & 0xffffff00) >> 8);
2413 dest_s[x * 2 + 1] = source[x] & 0xff;
2420 ERR("Unsupported conversion type %#x.\n", convert);
2425 /* This function is used in case of 8bit paletted textures to upload the palette.
2426 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2427 extensions like ATI_fragment_shaders is possible.
2429 /* Context activation is done by the caller. */
2430 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2431 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2433 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2435 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2437 /* Try to use the paletted texture extension */
2438 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2440 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2442 GL_EXTCALL(glColorTableEXT(This->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
2447 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2448 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2449 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2453 /* Create the fragment program if we don't have it */
2454 if(!device->paletteConversionShader)
2456 const char *fragment_palette_conversion =
2459 /* { 255/256, 0.5/255*255/256, 0, 0 } */
2460 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n"
2461 /* The alpha-component contains the palette index */
2462 "TEX index, fragment.texcoord[0], texture[0], 2D;\n"
2463 /* Scale the index by 255/256 and add a bias of '0.5' in order to sample in the middle */
2464 "MAD index.a, index.a, constants.x, constants.y;\n"
2465 /* Use the alpha-component as an index in the palette to get the final color */
2466 "TEX result.color, index.a, texture[1], 1D;\n"
2469 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2470 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2471 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2472 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), fragment_palette_conversion));
2473 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2476 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2477 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2479 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2480 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2482 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2483 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2484 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2485 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2487 /* Switch back to unit 0 in which the 2D texture will be stored. */
2488 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2490 /* Rebind the texture because it isn't bound anymore */
2491 glBindTexture(This->texture_target, This->texture_name);
2497 BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2498 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2500 if (This->palette || (This->resource.format_desc->format != WINED3DFMT_P8
2501 && This->resource.format_desc->format != WINED3DFMT_A8P8))
2503 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2504 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2509 if(This->palette9) {
2510 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2514 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2516 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2520 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2521 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2522 DWORD flag = srgb_mode ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2524 if (!(This->Flags & flag)) {
2525 TRACE("Reloading because surface is dirty\n");
2526 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2527 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2528 /* Reload: vice versa OR */
2529 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2530 /* Also reload: Color key is active AND the color key has changed */
2531 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2532 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2533 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2534 TRACE("Reloading because of color keying\n");
2535 /* To perform the color key conversion we need a sysmem copy of
2536 * the surface. Make sure we have it
2539 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2540 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2541 /* TODO: This is not necessarily needed with hw palettized texture support */
2542 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2544 TRACE("surface is already in texture\n");
2548 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2549 * These resources are not bound by device size or format restrictions. Because of this,
2550 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2551 * However, these resources can always be created, locked, and copied.
2553 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2555 FIXME("(%p) Operation not supported for scratch textures\n",This);
2556 return WINED3DERR_INVALIDCALL;
2559 IWineD3DSurface_LoadLocation(iface, flag, NULL /* no partial locking for textures yet */);
2563 static unsigned int gen = 0;
2566 if ((gen % 10) == 0) {
2567 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm",
2568 This, This->texture_target, This->texture_level, gen);
2569 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2572 * debugging crash code
2581 if (!(This->Flags & SFLAG_DONOTFREE)) {
2582 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2583 This->resource.allocatedMemory = NULL;
2584 This->resource.heapMemory = NULL;
2585 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2591 /* Context activation is done by the caller. */
2592 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface, BOOL srgb) {
2593 /* TODO: check for locks */
2594 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2595 IWineD3DBaseTexture *baseTexture = NULL;
2596 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2598 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2599 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2600 TRACE("Passing to container\n");
2601 IWineD3DBaseTexture_BindTexture(baseTexture, srgb);
2602 IWineD3DBaseTexture_Release(baseTexture);
2605 TRACE("(%p) : Binding surface\n", This);
2607 name = srgb ? &This->texture_name_srgb : &This->texture_name;
2608 if(!device->isInDraw) {
2609 ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
2614 if (!This->texture_level)
2617 glGenTextures(1, name);
2618 checkGLcall("glGenTextures");
2619 TRACE("Surface %p given name %d\n", This, *name);
2621 glBindTexture(This->texture_target, *name);
2622 checkGLcall("glBindTexture");
2623 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2624 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2625 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2626 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2627 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2628 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2629 glTexParameteri(This->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2630 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2631 glTexParameteri(This->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2632 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2634 /* This is where we should be reducing the amount of GLMemoryUsed */
2636 /* Mipmap surfaces should have a base texture container */
2637 ERR("Mipmap surface has a glTexture bound to it!\n");
2640 glBindTexture(This->texture_target, *name);
2641 checkGLcall("glBindTexture");
2650 static HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename)
2653 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2654 char *allocatedMemory;
2655 const char *textureRow;
2656 IWineD3DSwapChain *swapChain = NULL;
2657 int width, height, i, y;
2658 GLuint tmpTexture = 0;
2661 Textures may not be stored in ->allocatedgMemory and a GlTexture
2662 so we should lock the surface before saving a snapshot, or at least check that
2664 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2665 by calling GetTexImage and in compressed form by calling
2666 GetCompressedTexImageARB. Queried compressed images can be saved and
2667 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2668 texture images do not need to be processed by the GL and should
2669 significantly improve texture loading performance relative to uncompressed
2672 /* Setup the width and height to be the internal texture width and height. */
2673 width = This->pow2Width;
2674 height = This->pow2Height;
2675 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2676 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2678 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2679 /* if were not a real texture then read the back buffer into a real texture */
2680 /* we don't want to interfere with the back buffer so read the data into a temporary
2681 * texture and then save the data out of the temporary texture
2685 TRACE("(%p) Reading render target into texture\n", This);
2687 glGenTextures(1, &tmpTexture);
2688 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2690 glTexImage2D(GL_TEXTURE_2D,
2697 GL_UNSIGNED_INT_8_8_8_8_REV,
2700 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2701 checkGLcall("glGetIntegerv");
2702 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2703 checkGLcall("glReadBuffer");
2704 glCopyTexImage2D(GL_TEXTURE_2D,
2713 checkGLcall("glCopyTexImage2D");
2714 glReadBuffer(prevRead);
2717 } else { /* bind the real texture, and make sure it up to date */
2718 surface_internal_preload(iface, SRGB_RGB);
2719 surface_bind_and_dirtify(This, FALSE);
2721 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2723 FIXME("Saving texture level %d width %d height %d\n", This->texture_level, width, height);
2724 glGetTexImage(GL_TEXTURE_2D, This->texture_level, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, allocatedMemory);
2725 checkGLcall("glGetTexImage");
2727 glBindTexture(GL_TEXTURE_2D, 0);
2728 glDeleteTextures(1, &tmpTexture);
2732 f = fopen(filename, "w+");
2734 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2735 return WINED3DERR_INVALIDCALL;
2737 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2738 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format_desc->format));
2753 fwrite(&width,2,1,f);
2755 fwrite(&height,2,1,f);
2760 /* 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 */
2762 textureRow = allocatedMemory + (width * (height - 1) *4);
2764 textureRow = allocatedMemory;
2765 for (y = 0 ; y < height; y++) {
2766 for (i = 0; i < width; i++) {
2767 color = *((const DWORD*)textureRow);
2768 fputc((color >> 16) & 0xFF, f); /* B */
2769 fputc((color >> 8) & 0xFF, f); /* G */
2770 fputc((color >> 0) & 0xFF, f); /* R */
2771 fputc((color >> 24) & 0xFF, f); /* A */
2774 /* take two rows of the pointer to the texture memory */
2776 (textureRow-= width << 3);
2779 TRACE("Closing file\n");
2783 IWineD3DSwapChain_Release(swapChain);
2785 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2789 static HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2790 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2793 TRACE("(%p) : Calling base function first\n", This);
2794 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2796 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2797 TRACE("(%p) : glFormat %d, glFormatInternal %d, glType %d\n", This, This->resource.format_desc->glFormat,
2798 This->resource.format_desc->glInternal, This->resource.format_desc->glType);
2803 static HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2804 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2806 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2807 WARN("Surface is locked or the HDC is in use\n");
2808 return WINED3DERR_INVALIDCALL;
2811 if(Mem && Mem != This->resource.allocatedMemory) {
2812 void *release = NULL;
2814 /* Do I have to copy the old surface content? */
2815 if(This->Flags & SFLAG_DIBSECTION) {
2816 /* Release the DC. No need to hold the critical section for the update
2817 * Thread because this thread runs only on front buffers, but this method
2818 * fails for render targets in the check above.
2820 SelectObject(This->hDC, This->dib.holdbitmap);
2821 DeleteDC(This->hDC);
2822 /* Release the DIB section */
2823 DeleteObject(This->dib.DIBsection);
2824 This->dib.bitmap_data = NULL;
2825 This->resource.allocatedMemory = NULL;
2827 This->Flags &= ~SFLAG_DIBSECTION;
2828 } else if(!(This->Flags & SFLAG_USERPTR)) {
2829 release = This->resource.heapMemory;
2830 This->resource.heapMemory = NULL;
2832 This->resource.allocatedMemory = Mem;
2833 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2835 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2836 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2838 /* For client textures opengl has to be notified */
2839 if(This->Flags & SFLAG_CLIENT) {
2840 DWORD oldFlags = This->Flags;
2841 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2842 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2843 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2844 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2847 /* Now free the old memory if any */
2848 HeapFree(GetProcessHeap(), 0, release);
2849 } else if(This->Flags & SFLAG_USERPTR) {
2850 /* LockRect and GetDC will re-create the dib section and allocated memory */
2851 This->resource.allocatedMemory = NULL;
2852 /* HeapMemory should be NULL already */
2853 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2854 This->Flags &= ~SFLAG_USERPTR;
2856 if(This->Flags & SFLAG_CLIENT) {
2857 DWORD oldFlags = This->Flags;
2858 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2859 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2860 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2861 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2867 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
2869 /* Flip the surface contents */
2874 front->hDC = back->hDC;
2878 /* Flip the DIBsection */
2881 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
2882 tmp = front->dib.DIBsection;
2883 front->dib.DIBsection = back->dib.DIBsection;
2884 back->dib.DIBsection = tmp;
2886 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
2887 else front->Flags &= ~SFLAG_DIBSECTION;
2888 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
2889 else back->Flags &= ~SFLAG_DIBSECTION;
2892 /* Flip the surface data */
2896 tmp = front->dib.bitmap_data;
2897 front->dib.bitmap_data = back->dib.bitmap_data;
2898 back->dib.bitmap_data = tmp;
2900 tmp = front->resource.allocatedMemory;
2901 front->resource.allocatedMemory = back->resource.allocatedMemory;
2902 back->resource.allocatedMemory = tmp;
2904 tmp = front->resource.heapMemory;
2905 front->resource.heapMemory = back->resource.heapMemory;
2906 back->resource.heapMemory = tmp;
2911 GLuint tmp_pbo = front->pbo;
2912 front->pbo = back->pbo;
2913 back->pbo = tmp_pbo;
2916 /* client_memory should not be different, but just in case */
2919 tmp = front->dib.client_memory;
2920 front->dib.client_memory = back->dib.client_memory;
2921 back->dib.client_memory = tmp;
2924 /* Flip the opengl texture */
2928 tmp = back->texture_name;
2929 back->texture_name = front->texture_name;
2930 front->texture_name = tmp;
2932 tmp = back->texture_name_srgb;
2933 back->texture_name_srgb = front->texture_name_srgb;
2934 front->texture_name_srgb = tmp;
2938 DWORD tmp_flags = back->Flags;
2939 back->Flags = front->Flags;
2940 front->Flags = tmp_flags;
2944 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2945 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2946 IWineD3DSwapChainImpl *swapchain = NULL;
2948 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2950 /* Flipping is only supported on RenderTargets and overlays*/
2951 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
2952 WARN("Tried to flip a non-render target, non-overlay surface\n");
2953 return WINEDDERR_NOTFLIPPABLE;
2956 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
2957 flip_surface(This, (IWineD3DSurfaceImpl *) override);
2959 /* Update the overlay if it is visible */
2960 if(This->overlay_dest) {
2961 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
2968 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2969 * FIXME("(%p) Target override is not supported by now\n", This);
2970 * Additionally, it isn't really possible to support triple-buffering
2971 * properly on opengl at all
2975 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2977 ERR("Flipped surface is not on a swapchain\n");
2978 return WINEDDERR_NOTFLIPPABLE;
2981 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2982 * and only d3d8 and d3d9 apps specify the presentation interval
2984 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2985 /* Most common case first to avoid wasting time on all the other cases */
2986 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2987 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2988 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2989 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2990 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2991 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2992 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2994 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2997 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2998 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
2999 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
3003 /* Does a direct frame buffer -> texture copy. Stretching is done
3004 * with single pixel copy calls
3006 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3007 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
3008 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
3010 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3013 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3016 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
3017 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3020 /* Bind the target texture */
3021 glBindTexture(This->texture_target, This->texture_name);
3022 checkGLcall("glBindTexture");
3024 TRACE("Reading from an offscreen target\n");
3025 upsidedown = !upsidedown;
3026 glReadBuffer(myDevice->offscreenBuffer);
3028 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
3029 glReadBuffer(buffer);
3031 checkGLcall("glReadBuffer");
3033 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
3034 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
3036 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3038 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3040 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
3041 ERR("Texture filtering not supported in direct blit\n");
3044 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
3045 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3047 ERR("Texture filtering not supported in direct blit\n");
3051 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3052 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3054 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
3056 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3057 drect->x1 /*xoffset */, drect->y1 /* y offset */,
3058 srect->x1, Src->currentDesc.Height - srect->y2,
3059 drect->x2 - drect->x1, drect->y2 - drect->y1);
3061 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
3062 /* I have to process this row by row to swap the image,
3063 * otherwise it would be upside down, so stretching in y direction
3064 * doesn't cost extra time
3066 * However, stretching in x direction can be avoided if not necessary
3068 for(row = drect->y1; row < drect->y2; row++) {
3069 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3071 /* Well, that stuff works, but it's very slow.
3072 * find a better way instead
3076 for(col = drect->x1; col < drect->x2; col++) {
3077 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3078 drect->x1 + col /* x offset */, row /* y offset */,
3079 srect->x1 + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3082 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3083 drect->x1 /* x offset */, row /* y offset */,
3084 srect->x1, yoffset - (int) (row * yrel), drect->x2-drect->x1, 1);
3088 checkGLcall("glCopyTexSubImage2D");
3093 /* Uses the hardware to stretch and flip the image */
3094 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3095 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
3096 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
3098 GLuint src, backup = 0;
3099 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3100 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3101 float left, right, top, bottom; /* Texture coordinates */
3102 UINT fbwidth = Src->currentDesc.Width;
3103 UINT fbheight = Src->currentDesc.Height;
3104 struct wined3d_context *context;
3105 GLenum drawBuffer = GL_BACK;
3106 GLenum texture_target;
3107 BOOL noBackBufferBackup;
3109 TRACE("Using hwstretch blit\n");
3110 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3111 context = ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
3112 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3114 noBackBufferBackup = !swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3115 if (!noBackBufferBackup && !Src->texture_name)
3117 /* Get it a description */
3118 surface_internal_preload(SrcSurface, SRGB_RGB);
3122 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3123 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3125 if (context->aux_buffers >= 2)
3127 /* Got more than one aux buffer? Use the 2nd aux buffer */
3128 drawBuffer = GL_AUX1;
3130 else if ((swapchain || myDevice->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3132 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3133 drawBuffer = GL_AUX0;
3136 if(noBackBufferBackup) {
3137 glGenTextures(1, &backup);
3138 checkGLcall("glGenTextures");
3139 glBindTexture(GL_TEXTURE_2D, backup);
3140 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3141 texture_target = GL_TEXTURE_2D;
3143 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3144 * we are reading from the back buffer, the backup can be used as source texture
3146 texture_target = Src->texture_target;
3147 glBindTexture(texture_target, Src->texture_name);
3148 checkGLcall("glBindTexture(texture_target, Src->texture_name)");
3149 glEnable(texture_target);
3150 checkGLcall("glEnable(texture_target)");
3152 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3153 Src->Flags &= ~SFLAG_INTEXTURE;
3157 glReadBuffer(surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain));
3159 TRACE("Reading from an offscreen target\n");
3160 upsidedown = !upsidedown;
3161 glReadBuffer(myDevice->offscreenBuffer);
3164 /* TODO: Only back up the part that will be overwritten */
3165 glCopyTexSubImage2D(texture_target, 0,
3166 0, 0 /* read offsets */,
3171 checkGLcall("glCopyTexSubImage2D");
3173 /* No issue with overriding these - the sampler is dirty due to blit usage */
3174 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3175 wined3d_gl_mag_filter(magLookup, Filter));
3176 checkGLcall("glTexParameteri");
3177 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3178 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
3179 checkGLcall("glTexParameteri");
3181 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
3182 src = backup ? backup : Src->texture_name;
3184 glReadBuffer(GL_FRONT);
3185 checkGLcall("glReadBuffer(GL_FRONT)");
3187 glGenTextures(1, &src);
3188 checkGLcall("glGenTextures(1, &src)");
3189 glBindTexture(GL_TEXTURE_2D, src);
3190 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
3192 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
3193 * out for power of 2 sizes
3195 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
3196 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3197 checkGLcall("glTexImage2D");
3198 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
3199 0, 0 /* read offsets */,
3204 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3205 checkGLcall("glTexParameteri");
3206 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3207 checkGLcall("glTexParameteri");
3209 glReadBuffer(GL_BACK);
3210 checkGLcall("glReadBuffer(GL_BACK)");
3212 if(texture_target != GL_TEXTURE_2D) {
3213 glDisable(texture_target);
3214 glEnable(GL_TEXTURE_2D);
3215 texture_target = GL_TEXTURE_2D;
3218 checkGLcall("glEnd and previous");
3224 top = Src->currentDesc.Height - srect->y1;
3225 bottom = Src->currentDesc.Height - srect->y2;
3227 top = Src->currentDesc.Height - srect->y2;
3228 bottom = Src->currentDesc.Height - srect->y1;
3231 if(Src->Flags & SFLAG_NORMCOORD) {
3232 left /= Src->pow2Width;
3233 right /= Src->pow2Width;
3234 top /= Src->pow2Height;
3235 bottom /= Src->pow2Height;
3238 /* draw the source texture stretched and upside down. The correct surface is bound already */
3239 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3240 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3242 glDrawBuffer(drawBuffer);
3243 glReadBuffer(drawBuffer);
3247 glTexCoord2f(left, bottom);
3248 glVertex2i(0, fbheight);
3251 glTexCoord2f(left, top);
3252 glVertex2i(0, fbheight - drect->y2 - drect->y1);
3255 glTexCoord2f(right, top);
3256 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
3259 glTexCoord2f(right, bottom);
3260 glVertex2i(drect->x2 - drect->x1, fbheight);
3262 checkGLcall("glEnd and previous");
3264 if (texture_target != This->texture_target)
3266 glDisable(texture_target);
3267 glEnable(This->texture_target);
3268 texture_target = This->texture_target;
3271 /* Now read the stretched and upside down image into the destination texture */
3272 glBindTexture(texture_target, This->texture_name);
3273 checkGLcall("glBindTexture");
3274 glCopyTexSubImage2D(texture_target,
3276 drect->x1, drect->y1, /* xoffset, yoffset */
3277 0, 0, /* We blitted the image to the origin */
3278 drect->x2 - drect->x1, drect->y2 - drect->y1);
3279 checkGLcall("glCopyTexSubImage2D");
3281 if(drawBuffer == GL_BACK) {
3282 /* Write the back buffer backup back */
3284 if(texture_target != GL_TEXTURE_2D) {
3285 glDisable(texture_target);
3286 glEnable(GL_TEXTURE_2D);
3287 texture_target = GL_TEXTURE_2D;
3289 glBindTexture(GL_TEXTURE_2D, backup);
3290 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3292 if (texture_target != Src->texture_target)
3294 glDisable(texture_target);
3295 glEnable(Src->texture_target);
3296 texture_target = Src->texture_target;
3298 glBindTexture(Src->texture_target, Src->texture_name);
3299 checkGLcall("glBindTexture(Src->texture_target, Src->texture_name)");
3304 glTexCoord2f(0.0f, (float)fbheight / (float)Src->pow2Height);
3308 glTexCoord2f(0.0f, 0.0f);
3309 glVertex2i(0, fbheight);
3312 glTexCoord2f((float)fbwidth / (float)Src->pow2Width, 0.0f);
3313 glVertex2i(fbwidth, Src->currentDesc.Height);
3316 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
3317 glVertex2i(fbwidth, 0);
3320 /* Restore the old draw buffer */
3321 glDrawBuffer(GL_BACK);
3323 glDisable(texture_target);
3324 checkGLcall("glDisable(texture_target)");
3327 if (src != Src->texture_name && src != backup)
3329 glDeleteTextures(1, &src);
3330 checkGLcall("glDeleteTextures(1, &src)");
3333 glDeleteTextures(1, &backup);
3334 checkGLcall("glDeleteTextures(1, &backup)");
3340 /* Not called from the VTable */
3341 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3342 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx,
3343 WINED3DTEXTUREFILTERTYPE Filter)
3346 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3347 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3348 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3350 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3352 /* Get the swapchain. One of the surfaces has to be a primary surface */
3353 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3354 WARN("Destination is in sysmem, rejecting gl blt\n");
3355 return WINED3DERR_INVALIDCALL;
3357 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3358 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3360 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3361 WARN("Src is in sysmem, rejecting gl blt\n");
3362 return WINED3DERR_INVALIDCALL;
3364 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3365 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3368 /* Early sort out of cases where no render target is used */
3369 if(!dstSwapchain && !srcSwapchain &&
3370 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3371 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3372 return WINED3DERR_INVALIDCALL;
3375 /* No destination color keying supported */
3376 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3377 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3378 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3379 return WINED3DERR_INVALIDCALL;
3383 rect.x1 = DestRect->left;
3384 rect.y1 = DestRect->top;
3385 rect.x2 = DestRect->right;
3386 rect.y2 = DestRect->bottom;
3390 rect.x2 = This->currentDesc.Width;
3391 rect.y2 = This->currentDesc.Height;
3394 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3395 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3396 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3397 /* Half-life does a Blt from the back buffer to the front buffer,
3398 * Full surface size, no flags... Use present instead
3400 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3403 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3407 TRACE("Looking if a Present can be done...\n");
3408 /* Source Rectangle must be full surface */
3410 if(SrcRect->left != 0 || SrcRect->top != 0 ||
3411 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
3412 TRACE("No, Source rectangle doesn't match\n");
3418 mySrcRect.right = Src->currentDesc.Width;
3419 mySrcRect.bottom = Src->currentDesc.Height;
3421 /* No stretching may occur */
3422 if(mySrcRect.right != rect.x2 - rect.x1 ||
3423 mySrcRect.bottom != rect.y2 - rect.y1) {
3424 TRACE("No, stretching is done\n");
3428 /* Destination must be full surface or match the clipping rectangle */
3429 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3433 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3438 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3441 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3442 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3444 TRACE("No, dest rectangle doesn't match(clipper)\n");
3445 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3446 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3452 if(rect.x1 != 0 || rect.y1 != 0 ||
3453 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3454 TRACE("No, dest rectangle doesn't match(surface size)\n");
3461 /* These flags are unimportant for the flag check, remove them */
3462 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3463 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3465 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3466 * take very long, while a flip is fast.
3467 * This applies to Half-Life, which does such Blts every time it finished
3468 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3469 * menu. This is also used by all apps when they do windowed rendering
3471 * The problem is that flipping is not really the same as copying. After a
3472 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3473 * untouched. Therefore it's necessary to override the swap effect
3474 * and to set it back after the flip.
3476 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3480 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3481 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3483 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3484 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3486 dstSwapchain->presentParms.SwapEffect = orig_swap;
3493 TRACE("Unsupported blit between buffers on the same swapchain\n");
3494 return WINED3DERR_INVALIDCALL;
3495 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3496 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3497 return WINED3DERR_INVALIDCALL;
3498 } else if(dstSwapchain && srcSwapchain) {
3499 FIXME("Implement hardware blit between two different swapchains\n");
3500 return WINED3DERR_INVALIDCALL;
3501 } else if(dstSwapchain) {
3502 if(SrcSurface == myDevice->render_targets[0]) {
3503 TRACE("Blit from active render target to a swapchain\n");
3504 /* Handled with regular texture -> swapchain blit */
3506 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3507 FIXME("Implement blit from a swapchain to the active render target\n");
3508 return WINED3DERR_INVALIDCALL;
3511 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3512 /* Blit from render target to texture */
3514 BOOL upsideDown, stretchx;
3515 BOOL paletteOverride = FALSE;
3517 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3518 TRACE("Color keying not supported by frame buffer to texture blit\n");
3519 return WINED3DERR_INVALIDCALL;
3520 /* Destination color key is checked above */
3523 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3524 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3527 if(SrcRect->top < SrcRect->bottom) {
3528 srect.y1 = SrcRect->top;
3529 srect.y2 = SrcRect->bottom;
3532 srect.y1 = SrcRect->bottom;
3533 srect.y2 = SrcRect->top;
3536 srect.x1 = SrcRect->left;
3537 srect.x2 = SrcRect->right;
3541 srect.x2 = Src->currentDesc.Width;
3542 srect.y2 = Src->currentDesc.Height;
3545 if(rect.x1 > rect.x2) {
3549 upsideDown = !upsideDown;
3552 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3558 /* When blitting from a render target a texture, the texture isn't required to have a palette.
3559 * In this case grab the palette from the render target. */
3560 if ((This->resource.format_desc->format == WINED3DFMT_P8) && (This->palette == NULL))
3562 paletteOverride = TRUE;
3563 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3564 This->palette = Src->palette;
3567 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3568 * flip the image nor scale it.
3570 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3571 * -> If the app wants a image width an unscaled width, copy it line per line
3572 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3573 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3574 * back buffer. This is slower than reading line per line, thus not used for flipping
3575 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3578 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3579 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3582 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)
3583 && surface_can_stretch_rect(Src, This))
3585 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3586 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3587 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3588 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3589 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3590 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3592 TRACE("Using hardware stretching to flip / stretch the texture\n");
3593 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3596 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3598 This->palette = NULL;
3600 if(!(This->Flags & SFLAG_DONOTFREE)) {
3601 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3602 This->resource.allocatedMemory = NULL;
3603 This->resource.heapMemory = NULL;
3605 This->Flags &= ~SFLAG_INSYSMEM;
3607 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3608 * path is never entered
3610 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3614 /* Blit from offscreen surface to render target */
3615 float glTexCoord[4];
3616 DWORD oldCKeyFlags = Src->CKeyFlags;
3617 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3618 RECT SourceRectangle;
3619 BOOL paletteOverride = FALSE;
3621 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3624 SourceRectangle.left = SrcRect->left;
3625 SourceRectangle.right = SrcRect->right;
3626 SourceRectangle.top = SrcRect->top;
3627 SourceRectangle.bottom = SrcRect->bottom;
3629 SourceRectangle.left = 0;
3630 SourceRectangle.right = Src->currentDesc.Width;
3631 SourceRectangle.top = 0;
3632 SourceRectangle.bottom = Src->currentDesc.Height;
3635 /* When blitting from an offscreen surface to a rendertarget, the source
3636 * surface is not required to have a palette. Our rendering / conversion
3637 * code further down the road retrieves the palette from the surface, so
3638 * it must have a palette set. */
3639 if ((Src->resource.format_desc->format == WINED3DFMT_P8) && (Src->palette == NULL))
3641 paletteOverride = TRUE;
3642 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3643 Src->palette = This->palette;
3646 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)
3647 && !(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3648 && surface_can_stretch_rect(Src, This))
3650 TRACE("Using stretch_rect_fbo\n");
3651 /* The source is always a texture, but never the currently active render target, and the texture
3652 * contents are never upside down
3654 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3655 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3657 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3659 Src->palette = NULL;
3663 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3664 /* Fall back to software */
3665 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3666 SourceRectangle.left, SourceRectangle.top,
3667 SourceRectangle.right, SourceRectangle.bottom);
3668 return WINED3DERR_INVALIDCALL;
3671 /* Color keying: Check if we have to do a color keyed blt,
3672 * and if not check if a color key is activated.
3674 * Just modify the color keying parameters in the surface and restore them afterwards
3675 * The surface keeps track of the color key last used to load the opengl surface.
3676 * PreLoad will catch the change to the flags and color key and reload if necessary.
3678 if(Flags & WINEDDBLT_KEYSRC) {
3679 /* Use color key from surface */
3680 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3681 /* Use color key from DDBltFx */
3682 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3683 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3685 /* Do not use color key */
3686 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3689 /* Now load the surface */
3690 surface_internal_preload((IWineD3DSurface *) Src, SRGB_RGB);
3692 /* Activate the destination context, set it up for blitting */
3693 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3695 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3696 * while OpenGL coordinates are window relative.
3697 * Also beware of the origin difference(top left vs bottom left).
3698 * Also beware that the front buffer's surface size is screen width x screen height,
3699 * whereas the real gl drawable size is the size of the window.
3701 if (dstSwapchain && (IWineD3DSurface *)This == dstSwapchain->frontBuffer) {
3703 POINT offset = {0,0};
3705 ClientToScreen(dstSwapchain->win_handle, &offset);
3706 GetClientRect(dstSwapchain->win_handle, &windowsize);
3707 h = windowsize.bottom - windowsize.top;
3708 rect.x1 -= offset.x; rect.x2 -=offset.x;
3709 rect.y1 -= offset.y; rect.y2 -=offset.y;
3710 rect.y1 += This->currentDesc.Height - h; rect.y2 += This->currentDesc.Height - h;
3713 if (!is_identity_fixup(This->resource.format_desc->color_fixup))
3715 FIXME("Destination format %s has a fixup, this is not supported.\n",
3716 debug_d3dformat(This->resource.format_desc->format));
3717 dump_color_fixup_desc(This->resource.format_desc->color_fixup);
3720 if (!myDevice->blitter->color_fixup_supported(Src->resource.format_desc->color_fixup))
3722 FIXME("Source format %s has an unsupported fixup:\n",
3723 debug_d3dformat(Src->resource.format_desc->format));
3724 dump_color_fixup_desc(Src->resource.format_desc->color_fixup);
3727 myDevice->blitter->set_shader((IWineD3DDevice *) myDevice, Src->resource.format_desc,
3728 Src->texture_target, Src->pow2Width, Src->pow2Height);
3732 /* Bind the texture */
3733 glBindTexture(Src->texture_target, Src->texture_name);
3734 checkGLcall("glBindTexture");
3736 /* Filtering for StretchRect */
3737 glTexParameteri(Src->texture_target, GL_TEXTURE_MAG_FILTER,
3738 wined3d_gl_mag_filter(magLookup, Filter));
3739 checkGLcall("glTexParameteri");
3740 glTexParameteri(Src->texture_target, GL_TEXTURE_MIN_FILTER,
3741 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
3742 checkGLcall("glTexParameteri");
3743 glTexParameteri(Src->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3744 glTexParameteri(Src->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3745 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3746 checkGLcall("glTexEnvi");
3748 /* This is for color keying */
3749 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3750 glEnable(GL_ALPHA_TEST);
3751 checkGLcall("glEnable(GL_ALPHA_TEST)");
3753 /* When the primary render target uses P8, the alpha component contains the palette index.
3754 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3755 * should be masked away have alpha set to 0. */
3756 if(primary_render_target_is_p8(myDevice))
3757 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
3759 glAlphaFunc(GL_NOTEQUAL, 0.0f);
3760 checkGLcall("glAlphaFunc");
3762 glDisable(GL_ALPHA_TEST);
3763 checkGLcall("glDisable(GL_ALPHA_TEST)");
3766 /* Draw a textured quad
3770 glColor3f(1.0f, 1.0f, 1.0f);
3771 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3772 glVertex3f(rect.x1, rect.y1, 0.0f);
3774 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3775 glVertex3f(rect.x1, rect.y2, 0.0f);
3777 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3778 glVertex3f(rect.x2, rect.y2, 0.0f);
3780 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3781 glVertex3f(rect.x2, rect.y1, 0.0f);
3784 checkGLcall("glEnd");
3786 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3787 glDisable(GL_ALPHA_TEST);
3788 checkGLcall("glDisable(GL_ALPHA_TEST)");
3791 glBindTexture(Src->texture_target, 0);
3792 checkGLcall("glBindTexture(Src->texture_target, 0)");
3794 /* Restore the color key parameters */
3795 Src->CKeyFlags = oldCKeyFlags;
3796 Src->SrcBltCKey = oldBltCKey;
3798 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3800 Src->palette = NULL;
3804 /* Leave the opengl state valid for blitting */
3805 myDevice->blitter->unset_shader((IWineD3DDevice *) myDevice);
3807 /* Flush in case the drawable is used by multiple GL contexts */
3808 if(dstSwapchain && (This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer || dstSwapchain->num_contexts >= 2))
3811 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3812 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3815 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3819 /* Source-Less Blit to render target */
3820 if (Flags & WINEDDBLT_COLORFILL) {
3821 /* This is easy to handle for the D3D Device... */
3824 TRACE("Colorfill\n");
3826 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3827 must be true if we are here */
3828 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3829 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3830 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3831 TRACE("Surface is higher back buffer, falling back to software\n");
3832 return WINED3DERR_INVALIDCALL;
3835 /* The color as given in the Blt function is in the format of the frame-buffer...
3836 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3838 if (This->resource.format_desc->format == WINED3DFMT_P8)
3842 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3843 else alpha = 0xFF000000;
3845 if (This->palette) {
3847 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3848 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3849 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3854 else if (This->resource.format_desc->format == WINED3DFMT_R5G6B5)
3856 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3859 color = ((0xFF000000) |
3860 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3861 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3862 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3865 else if ((This->resource.format_desc->format == WINED3DFMT_R8G8B8)
3866 || (This->resource.format_desc->format == WINED3DFMT_X8R8G8B8))
3868 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3870 else if (This->resource.format_desc->format == WINED3DFMT_A8R8G8B8)
3872 color = DDBltFx->u5.dwFillColor;
3875 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3876 return WINED3DERR_INVALIDCALL;
3879 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3880 IWineD3DDeviceImpl_ClearSurface(myDevice, This, 1 /* Number of rectangles */,
3881 &rect, WINED3DCLEAR_TARGET, color, 0.0f /* Z */, 0 /* Stencil */);
3886 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3887 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3888 return WINED3DERR_INVALIDCALL;
3891 static HRESULT IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3892 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx)
3894 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3897 if (Flags & WINEDDBLT_DEPTHFILL) {
3898 switch(This->resource.format_desc->format)
3900 case WINED3DFMT_D16_UNORM:
3901 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3903 case WINED3DFMT_D15S1:
3904 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3906 case WINED3DFMT_D24S8:
3907 case WINED3DFMT_D24X8:
3908 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3910 case WINED3DFMT_D32:
3911 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3915 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format_desc->format));
3918 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3919 DestRect == NULL ? 0 : 1,
3920 (const WINED3DRECT *)DestRect,
3921 WINED3DCLEAR_ZBUFFER,
3927 FIXME("(%p): Unsupp depthstencil blit\n", This);
3928 return WINED3DERR_INVALIDCALL;
3931 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
3932 const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3933 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3934 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3935 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3936 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3937 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3939 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3941 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3942 return WINEDDERR_SURFACEBUSY;
3945 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3946 * except depth blits, which seem to work
3948 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3949 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3950 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3951 return WINED3DERR_INVALIDCALL;
3952 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3953 TRACE("Z Blit override handled the blit\n");
3958 /* Special cases for RenderTargets */
3959 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3960 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3961 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3964 /* For the rest call the X11 surface implementation.
3965 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3966 * other Blts are rather rare
3968 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3971 static HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
3972 IWineD3DSurface *Source, const RECT *rsrc, DWORD trans)
3974 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3975 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3976 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3977 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3979 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
3981 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3982 return WINEDDERR_SURFACEBUSY;
3985 if(myDevice->inScene &&
3986 (iface == myDevice->stencilBufferTarget ||
3987 (Source && Source == myDevice->stencilBufferTarget))) {
3988 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3989 return WINED3DERR_INVALIDCALL;
3992 /* Special cases for RenderTargets */
3993 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3994 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3996 RECT SrcRect, DstRect;
4000 SrcRect.left = rsrc->left;
4001 SrcRect.top= rsrc->top;
4002 SrcRect.bottom = rsrc->bottom;
4003 SrcRect.right = rsrc->right;
4007 SrcRect.right = srcImpl->currentDesc.Width;
4008 SrcRect.bottom = srcImpl->currentDesc.Height;
4011 DstRect.left = dstx;
4013 DstRect.right = dstx + SrcRect.right - SrcRect.left;
4014 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
4016 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
4017 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
4018 Flags |= WINEDDBLT_KEYSRC;
4019 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
4020 Flags |= WINEDDBLT_KEYDEST;
4021 if(trans & WINEDDBLTFAST_WAIT)
4022 Flags |= WINEDDBLT_WAIT;
4023 if(trans & WINEDDBLTFAST_DONOTWAIT)
4024 Flags |= WINEDDBLT_DONOTWAIT;
4026 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
4030 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
4033 static HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface)
4035 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4037 IWineD3DPaletteImpl *pal = This->palette;
4039 TRACE("(%p)\n", This);
4041 if (!pal) return WINED3D_OK;
4043 if (This->resource.format_desc->format == WINED3DFMT_P8
4044 || This->resource.format_desc->format == WINED3DFMT_A8P8)
4047 GLenum format, internal, type;
4048 CONVERT_TYPES convert;
4050 /* Check if we are using a RTL mode which uses texturing for uploads */
4051 BOOL use_texture = (wined3d_settings.rendertargetlock_mode == RTL_READTEX);
4053 /* Check if we have hardware palette conversion if we have convert is set to NO_CONVERSION */
4054 d3dfmt_get_conv(This, TRUE, use_texture, &format, &internal, &type, &convert, &bpp, FALSE);
4056 if((This->resource.usage & WINED3DUSAGE_RENDERTARGET) && (convert == NO_CONVERSION))
4058 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4060 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
4061 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
4063 /* We want to force a palette refresh, so mark the drawable as not being up to date */
4064 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
4066 /* Re-upload the palette */
4067 ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
4068 d3dfmt_p8_upload_palette(iface, convert);
4070 if(!(This->Flags & SFLAG_INSYSMEM)) {
4071 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
4072 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
4074 TRACE("Dirtifying surface\n");
4075 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
4079 if(This->Flags & SFLAG_DIBSECTION) {
4080 TRACE("(%p): Updating the hdc's palette\n", This);
4081 for (n=0; n<256; n++) {
4082 col[n].rgbRed = pal->palents[n].peRed;
4083 col[n].rgbGreen = pal->palents[n].peGreen;
4084 col[n].rgbBlue = pal->palents[n].peBlue;
4085 col[n].rgbReserved = 0;
4087 SetDIBColorTable(This->hDC, 0, 256, col);
4090 /* Propagate the changes to the drawable when we have a palette. */
4091 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
4092 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
4097 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
4098 /** Check against the maximum texture sizes supported by the video card **/
4099 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4100 unsigned int pow2Width, pow2Height;
4102 This->texture_name = 0;
4103 This->texture_target = GL_TEXTURE_2D;
4105 /* Non-power2 support */
4106 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO) || GL_SUPPORT(WINE_NORMALIZED_TEXRECT)) {
4107 pow2Width = This->currentDesc.Width;
4108 pow2Height = This->currentDesc.Height;
4110 /* Find the nearest pow2 match */
4111 pow2Width = pow2Height = 1;
4112 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
4113 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
4115 This->pow2Width = pow2Width;
4116 This->pow2Height = pow2Height;
4118 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
4119 /** TODO: add support for non power two compressed textures **/
4120 if (This->resource.format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
4122 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
4123 This, This->currentDesc.Width, This->currentDesc.Height);
4124 return WINED3DERR_NOTAVAILABLE;
4128 if(pow2Width != This->currentDesc.Width ||
4129 pow2Height != This->currentDesc.Height) {
4130 This->Flags |= SFLAG_NONPOW2;
4133 TRACE("%p\n", This);
4134 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
4135 /* one of three options
4136 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)
4137 2: Set the texture to the maximum size (bad idea)
4138 3: WARN and return WINED3DERR_NOTAVAILABLE;
4139 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.
4141 WARN("(%p) Creating an oversized surface: %ux%u (texture is %ux%u)\n",
4142 This, This->pow2Width, This->pow2Height, This->currentDesc.Width, This->currentDesc.Height);
4143 This->Flags |= SFLAG_OVERSIZE;
4145 /* This will be initialized on the first blt */
4146 This->glRect.left = 0;
4147 This->glRect.top = 0;
4148 This->glRect.right = 0;
4149 This->glRect.bottom = 0;
4151 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
4152 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
4153 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
4154 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
4156 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE)
4157 && !((This->resource.format_desc->format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE)
4158 && (wined3d_settings.rendertargetlock_mode == RTL_READTEX)))
4160 This->texture_target = GL_TEXTURE_RECTANGLE_ARB;
4161 This->pow2Width = This->currentDesc.Width;
4162 This->pow2Height = This->currentDesc.Height;
4163 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
4166 /* No oversize, gl rect is the full texture size */
4167 This->Flags &= ~SFLAG_OVERSIZE;
4168 This->glRect.left = 0;
4169 This->glRect.top = 0;
4170 This->glRect.right = This->pow2Width;
4171 This->glRect.bottom = This->pow2Height;
4174 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4175 switch(wined3d_settings.offscreen_rendering_mode) {
4176 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4177 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4178 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4182 This->Flags |= SFLAG_INSYSMEM;
4187 struct depth_blt_info
4191 enum tex_types tex_type;
4192 GLfloat coords[4][3];
4195 static void surface_get_depth_blt_info(GLenum target, GLsizei w, GLsizei h, struct depth_blt_info *info)
4197 GLfloat (*coords)[3] = info->coords;
4202 FIXME("Unsupported texture target %#x\n", target);
4203 /* Fall back to GL_TEXTURE_2D */
4205 info->binding = GL_TEXTURE_BINDING_2D;
4206 info->bind_target = GL_TEXTURE_2D;
4207 info->tex_type = tex_2d;
4208 coords[0][0] = 0.0f; coords[0][1] = 1.0f; coords[0][2] = 0.0f;
4209 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 0.0f;
4210 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4211 coords[3][0] = 1.0f; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4214 case GL_TEXTURE_RECTANGLE_ARB:
4215 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
4216 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
4217 info->tex_type = tex_rect;
4218 coords[0][0] = 0.0f; coords[0][1] = h; coords[0][2] = 0.0f;
4219 coords[1][0] = w; coords[1][1] = h; coords[1][2] = 0.0f;
4220 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4221 coords[3][0] = w; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4224 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4225 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4226 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4227 info->tex_type = tex_cube;
4228 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4229 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4230 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4231 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4233 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4234 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4235 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4236 info->tex_type = tex_cube;
4237 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4238 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4239 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4240 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4242 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4243 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4244 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4245 info->tex_type = tex_cube;
4246 coords[0][0] = -1.0f; coords[0][1] = 1.0f; coords[0][2] = 1.0f;
4247 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 1.0f;
4248 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4249 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4251 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4252 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4253 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4254 info->tex_type = tex_cube;
4255 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4256 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4257 coords[2][0] = -1.0f; coords[2][1] = -1.0f; coords[2][2] = 1.0f;
4258 coords[3][0] = 1.0f; coords[3][1] = -1.0f; coords[3][2] = 1.0f;
4260 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4261 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4262 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4263 info->tex_type = tex_cube;
4264 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4265 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4266 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4267 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4269 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4270 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4271 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4272 info->tex_type = tex_cube;
4273 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4274 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4275 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4276 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4280 /* GL locking is done by the caller */
4281 static void surface_depth_blt(IWineD3DSurfaceImpl *This, GLuint texture, GLsizei w, GLsizei h, GLenum target)
4283 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4284 struct depth_blt_info info;
4285 GLint old_binding = 0;
4287 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4289 glDisable(GL_CULL_FACE);
4290 glDisable(GL_BLEND);
4291 glDisable(GL_ALPHA_TEST);
4292 glDisable(GL_SCISSOR_TEST);
4293 glDisable(GL_STENCIL_TEST);
4294 glEnable(GL_DEPTH_TEST);
4295 glDepthFunc(GL_ALWAYS);
4296 glDepthMask(GL_TRUE);
4297 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4298 glViewport(0, 0, w, h);
4300 surface_get_depth_blt_info(target, w, h, &info);
4301 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4302 glGetIntegerv(info.binding, &old_binding);
4303 glBindTexture(info.bind_target, texture);
4305 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4307 glBegin(GL_TRIANGLE_STRIP);
4308 glTexCoord3fv(info.coords[0]);
4309 glVertex2f(-1.0f, -1.0f);
4310 glTexCoord3fv(info.coords[1]);
4311 glVertex2f(1.0f, -1.0f);
4312 glTexCoord3fv(info.coords[2]);
4313 glVertex2f(-1.0f, 1.0f);
4314 glTexCoord3fv(info.coords[3]);
4315 glVertex2f(1.0f, 1.0f);
4318 glBindTexture(info.bind_target, old_binding);
4322 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4325 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
4326 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4328 TRACE("(%p) New location %#x\n", This, location);
4330 if (location & ~SFLAG_DS_LOCATIONS) {
4331 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
4334 This->Flags &= ~SFLAG_DS_LOCATIONS;
4335 This->Flags |= location;
4338 /* Context activation is done by the caller. */
4339 void surface_load_ds_location(IWineD3DSurface *iface, struct wined3d_context *context, DWORD location)
4341 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4342 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4344 TRACE("(%p) New location %#x\n", This, location);
4346 /* TODO: Make this work for modes other than FBO */
4347 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4349 if (This->Flags & location) {
4350 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
4354 if (This->current_renderbuffer) {
4355 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
4359 if (location == SFLAG_DS_OFFSCREEN) {
4360 if (This->Flags & SFLAG_DS_ONSCREEN) {
4361 GLint old_binding = 0;
4364 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
4368 if (!device->depth_blt_texture) {
4369 glGenTextures(1, &device->depth_blt_texture);
4372 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4373 * directly on the FBO texture. That's because we need to flip. */
4374 context_bind_fbo(context, GL_FRAMEBUFFER_EXT, NULL);
4375 if (This->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4377 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4378 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4380 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4381 bind_target = GL_TEXTURE_2D;
4383 glBindTexture(bind_target, device->depth_blt_texture);
4384 glCopyTexImage2D(bind_target, This->texture_level, This->resource.format_desc->glInternal,
4385 0, 0, This->currentDesc.Width, This->currentDesc.Height, 0);
4386 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4387 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4388 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4389 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4390 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4391 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4392 glBindTexture(bind_target, old_binding);
4394 /* Setup the destination */
4395 if (!device->depth_blt_rb) {
4396 GL_EXTCALL(glGenRenderbuffersEXT(1, &device->depth_blt_rb));
4397 checkGLcall("glGenRenderbuffersEXT");
4399 if (device->depth_blt_rb_w != This->currentDesc.Width
4400 || device->depth_blt_rb_h != This->currentDesc.Height) {
4401 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4402 checkGLcall("glBindRenderbufferEXT");
4403 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, This->currentDesc.Width, This->currentDesc.Height));
4404 checkGLcall("glRenderbufferStorageEXT");
4405 device->depth_blt_rb_w = This->currentDesc.Width;
4406 device->depth_blt_rb_h = This->currentDesc.Height;
4409 context_bind_fbo(context, GL_FRAMEBUFFER_EXT, &context->dst_fbo);
4410 GL_EXTCALL(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4411 checkGLcall("glFramebufferRenderbufferEXT");
4412 context_attach_depth_stencil_fbo(context, GL_FRAMEBUFFER_EXT, iface, FALSE);
4414 /* Do the actual blit */
4415 surface_depth_blt(This, device->depth_blt_texture, This->currentDesc.Width, This->currentDesc.Height, bind_target);
4416 checkGLcall("depth_blt");
4418 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER_EXT, &context->current_fbo->id);
4419 else context_bind_fbo(context, GL_FRAMEBUFFER_EXT, NULL);
4423 FIXME("No up to date depth stencil location\n");
4425 } else if (location == SFLAG_DS_ONSCREEN) {
4426 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4427 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4431 context_bind_fbo(context, GL_FRAMEBUFFER_EXT, NULL);
4432 surface_depth_blt(This, This->texture_name, This->currentDesc.Width,
4433 This->currentDesc.Height, This->texture_target);
4434 checkGLcall("depth_blt");
4436 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER_EXT, &context->current_fbo->id);
4440 FIXME("No up to date depth stencil location\n");
4443 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4446 This->Flags |= location;
4449 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4450 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4451 IWineD3DBaseTexture *texture;
4452 IWineD3DSurfaceImpl *overlay;
4454 TRACE("(%p)->(%s, %s)\n", iface, debug_surflocation(flag),
4455 persistent ? "TRUE" : "FALSE");
4457 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4458 if (This->Flags & SFLAG_SWAPCHAIN)
4460 TRACE("Surface %p is an onscreen surface\n", iface);
4462 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4463 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4468 if(((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) ||
4469 ((This->Flags & SFLAG_INSRGBTEX) && !(flag & SFLAG_INSRGBTEX))) {
4470 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4471 TRACE("Passing to container\n");
4472 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4473 IWineD3DBaseTexture_Release(texture);
4476 This->Flags &= ~SFLAG_LOCATIONS;
4477 This->Flags |= flag;
4479 /* Redraw emulated overlays, if any */
4480 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4481 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4482 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4486 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))) {
4487 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4488 TRACE("Passing to container\n");
4489 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4490 IWineD3DBaseTexture_Release(texture);
4493 This->Flags &= ~flag;
4496 if(!(This->Flags & SFLAG_LOCATIONS)) {
4497 ERR("%p: Surface does not have any up to date location\n", This);
4513 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
4515 f->l = ((r->left * 2.0f) / w) - 1.0f;
4516 f->t = ((r->top * 2.0f) / h) - 1.0f;
4517 f->r = ((r->right * 2.0f) / w) - 1.0f;
4518 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
4521 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in)
4523 const struct wined3d_context *context;
4524 struct coords coords[4];
4526 IWineD3DSwapChain *swapchain;
4527 IWineD3DBaseTexture *texture;
4528 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4530 struct float_rect f;
4537 rect.right = This->currentDesc.Width;
4538 rect.bottom = This->currentDesc.Height;
4541 switch (This->texture_target)
4544 bind_target = GL_TEXTURE_2D;
4546 coords[0].x = (float)rect.left / This->pow2Width;
4547 coords[0].y = (float)rect.top / This->pow2Height;
4550 coords[1].x = (float)rect.left / This->pow2Width;
4551 coords[1].y = (float)rect.bottom / This->pow2Height;
4554 coords[2].x = (float)rect.right / This->pow2Width;
4555 coords[2].y = (float)rect.bottom / This->pow2Height;
4558 coords[3].x = (float)rect.right / This->pow2Width;
4559 coords[3].y = (float)rect.top / This->pow2Height;
4563 case GL_TEXTURE_RECTANGLE_ARB:
4564 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4565 coords[0].x = rect.left; coords[0].y = rect.top; coords[0].z = 0;
4566 coords[1].x = rect.left; coords[1].y = rect.bottom; coords[1].z = 0;
4567 coords[2].x = rect.right; coords[2].y = rect.bottom; coords[2].z = 0;
4568 coords[3].x = rect.right; coords[3].y = rect.top; coords[3].z = 0;
4571 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4572 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4573 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4574 coords[0].x = 1; coords[0].y = -f.t; coords[0].z = -f.l;
4575 coords[1].x = 1; coords[1].y = -f.b; coords[1].z = -f.l;
4576 coords[2].x = 1; coords[2].y = -f.b; coords[2].z = -f.r;
4577 coords[3].x = 1; coords[3].y = -f.t; coords[3].z = -f.r;
4580 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4581 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4582 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4583 coords[0].x = -1; coords[0].y = -f.t; coords[0].z = f.l;
4584 coords[1].x = -1; coords[1].y = -f.b; coords[1].z = f.l;
4585 coords[2].x = -1; coords[2].y = -f.b; coords[2].z = f.r;
4586 coords[3].x = -1; coords[3].y = -f.t; coords[3].z = f.r;
4589 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4590 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4591 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4592 coords[0].x = f.l; coords[0].y = 1; coords[0].z = f.t;
4593 coords[1].x = f.l; coords[1].y = 1; coords[1].z = f.b;
4594 coords[2].x = f.r; coords[2].y = 1; coords[2].z = f.b;
4595 coords[3].x = f.r; coords[3].y = 1; coords[3].z = f.t;
4598 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4599 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4600 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4601 coords[0].x = f.l; coords[0].y = -1; coords[0].z = -f.t;
4602 coords[1].x = f.l; coords[1].y = -1; coords[1].z = -f.b;
4603 coords[2].x = f.r; coords[2].y = -1; coords[2].z = -f.b;
4604 coords[3].x = f.r; coords[3].y = -1; coords[3].z = -f.t;
4607 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4608 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4609 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4610 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1;
4611 coords[1].x = f.l; coords[1].y = -f.b; coords[1].z = 1;
4612 coords[2].x = f.r; coords[2].y = -f.b; coords[2].z = 1;
4613 coords[3].x = f.r; coords[3].y = -f.t; coords[3].z = 1;
4616 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4617 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4618 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4619 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1;
4620 coords[1].x = -f.l; coords[1].y = -f.b; coords[1].z = -1;
4621 coords[2].x = -f.r; coords[2].y = -f.b; coords[2].z = -1;
4622 coords[3].x = -f.r; coords[3].y = -f.t; coords[3].z = -1;
4626 ERR("Unexpected texture target %#x\n", This->texture_target);
4630 context = ActivateContext(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4634 glEnable(bind_target);
4635 checkGLcall("glEnable(bind_target)");
4636 glBindTexture(bind_target, This->texture_name);
4637 checkGLcall("glBindTexture(bind_target, This->texture_name)");
4638 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4639 checkGLcall("glTexParameteri");
4640 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4641 checkGLcall("glTexParameteri");
4643 if (context->render_offscreen)
4645 LONG tmp = rect.top;
4646 rect.top = rect.bottom;
4651 glTexCoord3fv(&coords[0].x);
4652 glVertex2i(rect.left, rect.top);
4654 glTexCoord3fv(&coords[1].x);
4655 glVertex2i(rect.left, rect.bottom);
4657 glTexCoord3fv(&coords[2].x);
4658 glVertex2i(rect.right, rect.bottom);
4660 glTexCoord3fv(&coords[3].x);
4661 glVertex2i(rect.right, rect.top);
4663 checkGLcall("glEnd");
4665 glDisable(bind_target);
4666 checkGLcall("glDisable(bind_target)");
4670 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain)))
4672 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
4673 if(((IWineD3DSwapChainImpl*)swapchain)->frontBuffer == (IWineD3DSurface*)This ||
4674 ((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
4677 IWineD3DSwapChain_Release(swapchain);
4679 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
4680 * reset properly next draw
4682 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture)))
4684 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
4685 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
4686 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
4687 IWineD3DBaseTexture_Release(texture);
4692 /*****************************************************************************
4693 * IWineD3DSurface::LoadLocation
4695 * Copies the current surface data from wherever it is to the requested
4696 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4697 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4698 * multiple locations, the gl texture is preferred over the drawable, which is
4699 * preferred over system memory. The PBO counts as system memory. If rect is
4700 * not NULL, only the specified rectangle is copied (only supported for
4701 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4702 * location is marked up to date after the copy.
4705 * flag: Surface location flag to be updated
4706 * rect: rectangle to be copied
4709 * WINED3D_OK on success
4710 * WINED3DERR_DEVICELOST on an internal error
4712 *****************************************************************************/
4713 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4714 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4715 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4716 GLenum format, internal, type;
4717 CONVERT_TYPES convert;
4719 int width, pitch, outpitch;
4721 BOOL drawable_read_ok = TRUE;
4723 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4724 if (This->Flags & SFLAG_SWAPCHAIN)
4726 TRACE("Surface %p is an onscreen surface\n", iface);
4728 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4729 * Prefer SFLAG_INTEXTURE. */
4730 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4731 drawable_read_ok = FALSE;
4735 TRACE("(%p)->(%s, %p)\n", iface, debug_surflocation(flag), rect);
4737 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4740 if(This->Flags & flag) {
4741 TRACE("Location already up to date\n");
4745 if(!(This->Flags & SFLAG_LOCATIONS)) {
4746 ERR("%p: Surface does not have any up to date location\n", This);
4747 This->Flags |= SFLAG_LOST;
4748 return WINED3DERR_DEVICELOST;
4751 if(flag == SFLAG_INSYSMEM) {
4752 surface_prepare_system_memory(This);
4754 /* Download the surface to system memory */
4755 if(This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) {
4756 if (!device->isInDraw) ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
4757 surface_bind_and_dirtify(This, !(This->Flags & SFLAG_INTEXTURE));
4759 surface_download_data(This);
4761 /* Note: It might be faster to download into a texture first. */
4762 read_from_framebuffer(This, rect,
4763 This->resource.allocatedMemory,
4764 IWineD3DSurface_GetPitch(iface));
4766 } else if(flag == SFLAG_INDRAWABLE) {
4767 if(This->Flags & SFLAG_INTEXTURE) {
4768 surface_blt_to_drawable(This, rect);
4770 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4771 /* This needs a shader to convert the srgb data sampled from the GL texture into RGB
4772 * values, otherwise we get incorrect values in the target. For now go the slow way
4773 * via a system memory copy
4775 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4778 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, FALSE);
4780 /* The width is in 'length' not in bytes */
4781 width = This->currentDesc.Width;
4782 pitch = IWineD3DSurface_GetPitch(iface);
4784 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4785 * but it isn't set (yet) in all cases it is getting called. */
4786 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4787 TRACE("Removing the pbo attached to surface %p\n", This);
4788 if (!device->isInDraw) ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
4789 surface_remove_pbo(This);
4792 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4793 int height = This->currentDesc.Height;
4795 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4796 outpitch = width * bpp;
4797 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4799 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4801 ERR("Out of memory %d, %d!\n", outpitch, height);
4802 return WINED3DERR_OUTOFVIDEOMEMORY;
4804 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4806 This->Flags |= SFLAG_CONVERTED;
4808 This->Flags &= ~SFLAG_CONVERTED;
4809 mem = This->resource.allocatedMemory;
4812 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4814 /* Don't delete PBO memory */
4815 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4816 HeapFree(GetProcessHeap(), 0, mem);
4818 } else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */ {
4819 if (drawable_read_ok && (This->Flags & SFLAG_INDRAWABLE)) {
4820 read_from_framebuffer_texture(This, flag == SFLAG_INSRGBTEX);
4821 } else { /* Upload from system memory */
4822 BOOL srgb = flag == SFLAG_INSRGBTEX;
4823 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4824 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
4827 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE) {
4828 /* Performance warning ... */
4829 FIXME("%p: Downloading rgb texture to reload it as srgb\n", This);
4830 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4833 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4834 /* Performance warning ... */
4835 FIXME("%p: Downloading srgb texture to reload it as rgb\n", This);
4836 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4840 if (!device->isInDraw) ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
4841 surface_bind_and_dirtify(This, srgb);
4843 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4844 This->Flags |= SFLAG_GLCKEY;
4845 This->glCKey = This->SrcBltCKey;
4847 else This->Flags &= ~SFLAG_GLCKEY;
4849 /* The width is in 'length' not in bytes */
4850 width = This->currentDesc.Width;
4851 pitch = IWineD3DSurface_GetPitch(iface);
4853 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4854 * but it isn't set (yet) in all cases it is getting called. */
4855 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4856 TRACE("Removing the pbo attached to surface %p\n", This);
4857 surface_remove_pbo(This);
4860 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4861 int height = This->currentDesc.Height;
4863 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4864 outpitch = width * bpp;
4865 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4867 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4869 ERR("Out of memory %d, %d!\n", outpitch, height);
4870 return WINED3DERR_OUTOFVIDEOMEMORY;
4872 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4874 This->Flags |= SFLAG_CONVERTED;
4876 else if ((This->resource.format_desc->format == WINED3DFMT_P8)
4877 && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)))
4879 d3dfmt_p8_upload_palette(iface, convert);
4880 This->Flags &= ~SFLAG_CONVERTED;
4881 mem = This->resource.allocatedMemory;
4883 This->Flags &= ~SFLAG_CONVERTED;
4884 mem = This->resource.allocatedMemory;
4887 /* Make sure the correct pitch is used */
4889 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4892 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4893 TRACE("non power of two support\n");
4894 if(!(This->Flags & alloc_flag)) {
4895 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4896 This->Flags |= alloc_flag;
4898 if (mem || (This->Flags & SFLAG_PBO)) {
4899 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4902 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4903 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4905 if(!(This->Flags & alloc_flag)) {
4906 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4907 This->Flags |= alloc_flag;
4909 if (mem || (This->Flags & SFLAG_PBO)) {
4910 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4914 /* Restore the default pitch */
4916 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4919 /* Don't delete PBO memory */
4920 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4921 HeapFree(GetProcessHeap(), 0, mem);
4926 This->Flags |= flag;
4929 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !(This->Flags & SFLAG_SWAPCHAIN)
4930 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4931 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4932 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4938 static HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container)
4940 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4941 IWineD3DSwapChain *swapchain = NULL;
4943 /* Update the drawable size method */
4945 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4948 This->get_drawable_size = get_drawable_size_swapchain;
4949 IWineD3DSwapChain_Release(swapchain);
4950 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4951 switch(wined3d_settings.offscreen_rendering_mode) {
4952 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4953 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4954 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4958 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4961 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4962 return SURFACE_OPENGL;
4965 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
4966 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4969 /* If there's no destination surface there is nothing to do */
4970 if(!This->overlay_dest) return WINED3D_OK;
4972 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
4973 * update the overlay. Prevent an endless recursion
4975 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
4978 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
4979 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
4980 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
4981 NULL, WINED3DTEXF_LINEAR);
4982 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
4987 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4990 IWineD3DBaseSurfaceImpl_QueryInterface,
4991 IWineD3DBaseSurfaceImpl_AddRef,
4992 IWineD3DSurfaceImpl_Release,
4993 /* IWineD3DResource */
4994 IWineD3DBaseSurfaceImpl_GetParent,
4995 IWineD3DBaseSurfaceImpl_GetDevice,
4996 IWineD3DBaseSurfaceImpl_SetPrivateData,
4997 IWineD3DBaseSurfaceImpl_GetPrivateData,
4998 IWineD3DBaseSurfaceImpl_FreePrivateData,
4999 IWineD3DBaseSurfaceImpl_SetPriority,
5000 IWineD3DBaseSurfaceImpl_GetPriority,
5001 IWineD3DSurfaceImpl_PreLoad,
5002 IWineD3DSurfaceImpl_UnLoad,
5003 IWineD3DBaseSurfaceImpl_GetType,
5004 /* IWineD3DSurface */
5005 IWineD3DBaseSurfaceImpl_GetContainer,
5006 IWineD3DBaseSurfaceImpl_GetDesc,
5007 IWineD3DSurfaceImpl_LockRect,
5008 IWineD3DSurfaceImpl_UnlockRect,
5009 IWineD3DSurfaceImpl_GetDC,
5010 IWineD3DSurfaceImpl_ReleaseDC,
5011 IWineD3DSurfaceImpl_Flip,
5012 IWineD3DSurfaceImpl_Blt,
5013 IWineD3DBaseSurfaceImpl_GetBltStatus,
5014 IWineD3DBaseSurfaceImpl_GetFlipStatus,
5015 IWineD3DBaseSurfaceImpl_IsLost,
5016 IWineD3DBaseSurfaceImpl_Restore,
5017 IWineD3DSurfaceImpl_BltFast,
5018 IWineD3DBaseSurfaceImpl_GetPalette,
5019 IWineD3DBaseSurfaceImpl_SetPalette,
5020 IWineD3DSurfaceImpl_RealizePalette,
5021 IWineD3DBaseSurfaceImpl_SetColorKey,
5022 IWineD3DBaseSurfaceImpl_GetPitch,
5023 IWineD3DSurfaceImpl_SetMem,
5024 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
5025 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
5026 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
5027 IWineD3DBaseSurfaceImpl_UpdateOverlay,
5028 IWineD3DBaseSurfaceImpl_SetClipper,
5029 IWineD3DBaseSurfaceImpl_GetClipper,
5031 IWineD3DSurfaceImpl_LoadTexture,
5032 IWineD3DSurfaceImpl_BindTexture,
5033 IWineD3DSurfaceImpl_SaveSnapshot,
5034 IWineD3DSurfaceImpl_SetContainer,
5035 IWineD3DBaseSurfaceImpl_GetData,
5036 IWineD3DSurfaceImpl_SetFormat,
5037 IWineD3DSurfaceImpl_PrivateSetup,
5038 IWineD3DSurfaceImpl_ModifyLocation,
5039 IWineD3DSurfaceImpl_LoadLocation,
5040 IWineD3DSurfaceImpl_GetImplType,
5041 IWineD3DSurfaceImpl_DrawOverlay
5043 #undef GLINFO_LOCATION
5045 #define GLINFO_LOCATION device->adapter->gl_info
5046 static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
5047 /* Context activation is done by the caller. */
5048 static void ffp_blit_free(IWineD3DDevice *iface) { }
5050 /* Context activation is done by the caller. */
5051 static HRESULT ffp_blit_set(IWineD3DDevice *iface, const struct GlPixelFormatDesc *format_desc,
5052 GLenum textype, UINT width, UINT height)
5056 checkGLcall("glEnable(textype)");
5061 /* Context activation is done by the caller. */
5062 static void ffp_blit_unset(IWineD3DDevice *iface) {
5063 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
5065 glDisable(GL_TEXTURE_2D);
5066 checkGLcall("glDisable(GL_TEXTURE_2D)");
5067 if(GL_SUPPORT(ARB_TEXTURE_CUBE_MAP)) {
5068 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
5069 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
5071 if(GL_SUPPORT(ARB_TEXTURE_RECTANGLE)) {
5072 glDisable(GL_TEXTURE_RECTANGLE_ARB);
5073 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
5078 static BOOL ffp_blit_color_fixup_supported(struct color_fixup_desc fixup)
5080 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
5082 TRACE("Checking support for fixup:\n");
5083 dump_color_fixup_desc(fixup);
5086 /* We only support identity conversions. */
5087 if (is_identity_fixup(fixup))
5093 TRACE("[FAILED]\n");
5097 const struct blit_shader ffp_blit = {
5102 ffp_blit_color_fixup_supported