push 5b1efc32b5a8acb1d5b5e60584746392dd0c436e
[wine/hacks.git] / dlls / wined3d / surface.c
blob3d2a3378d3101095de151475ac90455c51f4d1f2
1 /*
2 * IWineD3DSurface Implementation
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006-2008 Stefan Dösinger for CodeWeavers
11 * Copyright 2007-2008 Henri Verbeet
12 * Copyright 2006-2008 Roderick Colenbrander
13 * Copyright 2009 Henri Verbeet for CodeWeavers
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "config.h"
31 #include "wine/port.h"
32 #include "wined3d_private.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
35 WINE_DECLARE_DEBUG_CHANNEL(d3d);
37 #define GLINFO_LOCATION (*gl_info)
39 static void surface_cleanup(IWineD3DSurfaceImpl *This)
41 IWineD3DDeviceImpl *device = This->resource.device;
42 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
43 struct wined3d_context *context = NULL;
44 renderbuffer_entry_t *entry, *entry2;
46 TRACE("(%p) : Cleaning up.\n", This);
48 /* Need a context to destroy the texture. Use the currently active render
49 * target, but only if the primary render target exists. Otherwise
50 * lastActiveRenderTarget is garbage. When destroying the primary render
51 * target, Uninit3D() will activate a context before doing anything. */
52 if (device->render_targets && device->render_targets[0])
54 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
57 ENTER_GL();
59 if (This->texture_name)
61 /* Release the OpenGL texture. */
62 TRACE("Deleting texture %u.\n", This->texture_name);
63 glDeleteTextures(1, &This->texture_name);
66 if (This->Flags & SFLAG_PBO)
68 /* Delete the PBO. */
69 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
72 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry)
74 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
75 HeapFree(GetProcessHeap(), 0, entry);
78 LEAVE_GL();
80 if (This->Flags & SFLAG_DIBSECTION)
82 /* Release the DC. */
83 SelectObject(This->hDC, This->dib.holdbitmap);
84 DeleteDC(This->hDC);
85 /* Release the DIB section. */
86 DeleteObject(This->dib.DIBsection);
87 This->dib.bitmap_data = NULL;
88 This->resource.allocatedMemory = NULL;
91 if (This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem((IWineD3DSurface *)This, NULL);
92 if (This->overlay_dest) list_remove(&This->overlay_entry);
94 HeapFree(GetProcessHeap(), 0, This->palette9);
96 resource_cleanup((IWineD3DResource *)This);
98 if (context) context_release(context);
101 UINT surface_calculate_size(const struct GlPixelFormatDesc *format_desc, UINT alignment, UINT width, UINT height)
103 UINT size;
105 if (format_desc->format == WINED3DFMT_UNKNOWN)
107 size = 0;
109 else if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
111 UINT row_block_count = (width + format_desc->block_width - 1) / format_desc->block_width;
112 UINT row_count = (height + format_desc->block_height - 1) / format_desc->block_height;
113 size = row_count * row_block_count * format_desc->block_byte_count;
115 else
117 /* The pitch is a multiple of 4 bytes. */
118 size = height * (((width * format_desc->byte_count) + alignment - 1) & ~(alignment - 1));
121 if (format_desc->heightscale != 0.0f) size *= format_desc->heightscale;
123 return size;
126 HRESULT surface_init(IWineD3DSurfaceImpl *surface, WINED3DSURFTYPE surface_type, UINT alignment,
127 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
128 UINT multisample_quality, IWineD3DDeviceImpl *device, DWORD usage, WINED3DFORMAT format,
129 WINED3DPOOL pool, IUnknown *parent, const struct wined3d_parent_ops *parent_ops)
131 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
132 const struct GlPixelFormatDesc *format_desc = getFormatDescEntry(format, gl_info);
133 void (*cleanup)(IWineD3DSurfaceImpl *This);
134 unsigned int resource_size;
135 HRESULT hr;
137 if (multisample_quality > 0)
139 FIXME("multisample_quality set to %u, substituting 0\n", multisample_quality);
140 multisample_quality = 0;
143 /* FIXME: Check that the format is supported by the device. */
145 resource_size = surface_calculate_size(format_desc, alignment, width, height);
147 /* Look at the implementation and set the correct Vtable. */
148 switch (surface_type)
150 case SURFACE_OPENGL:
151 surface->lpVtbl = &IWineD3DSurface_Vtbl;
152 cleanup = surface_cleanup;
153 break;
155 case SURFACE_GDI:
156 surface->lpVtbl = &IWineGDISurface_Vtbl;
157 cleanup = surface_gdi_cleanup;
158 break;
160 default:
161 ERR("Requested unknown surface implementation %#x.\n", surface_type);
162 return WINED3DERR_INVALIDCALL;
165 hr = resource_init((IWineD3DResource *)surface, WINED3DRTYPE_SURFACE,
166 device, resource_size, usage, format_desc, pool, parent, parent_ops);
167 if (FAILED(hr))
169 WARN("Failed to initialize resource, returning %#x.\n", hr);
170 return hr;
173 /* "Standalone" surface. */
174 IWineD3DSurface_SetContainer((IWineD3DSurface *)surface, NULL);
176 surface->currentDesc.Width = width;
177 surface->currentDesc.Height = height;
178 surface->currentDesc.MultiSampleType = multisample_type;
179 surface->currentDesc.MultiSampleQuality = multisample_quality;
180 surface->texture_level = level;
181 list_init(&surface->overlays);
183 /* Flags */
184 surface->Flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
185 if (discard) surface->Flags |= SFLAG_DISCARD;
186 if (lockable || format == WINED3DFMT_D16_LOCKABLE) surface->Flags |= SFLAG_LOCKABLE;
188 /* Quick lockable sanity check.
189 * TODO: remove this after surfaces, usage and lockability have been debugged properly
190 * this function is too deep to need to care about things like this.
191 * Levels need to be checked too, since they all affect what can be done. */
192 switch (pool)
194 case WINED3DPOOL_SCRATCH:
195 if(!lockable)
197 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
198 "which are mutually exclusive, setting lockable to TRUE.\n");
199 lockable = TRUE;
201 break;
203 case WINED3DPOOL_SYSTEMMEM:
204 if (!lockable)
205 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
206 break;
208 case WINED3DPOOL_MANAGED:
209 if (usage & WINED3DUSAGE_DYNAMIC)
210 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
211 break;
213 case WINED3DPOOL_DEFAULT:
214 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
215 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
216 break;
218 default:
219 FIXME("Unknown pool %#x.\n", pool);
220 break;
223 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
225 FIXME("Trying to create a render target that isn't in the default pool.\n");
228 /* Mark the texture as dirty so that it gets loaded first time around. */
229 surface_add_dirty_rect((IWineD3DSurface *)surface, NULL);
230 list_init(&surface->renderbuffers);
232 TRACE("surface %p, memory %p, size %u\n", surface, surface->resource.allocatedMemory, surface->resource.size);
234 /* Call the private setup routine */
235 hr = IWineD3DSurface_PrivateSetup((IWineD3DSurface *)surface);
236 if (FAILED(hr))
238 ERR("Private setup failed, returning %#x\n", hr);
239 cleanup(surface);
240 return hr;
243 return hr;
246 static void surface_force_reload(IWineD3DSurface *iface)
248 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
250 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
253 void surface_set_texture_name(IWineD3DSurface *iface, GLuint new_name, BOOL srgb)
255 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
256 GLuint *name;
257 DWORD flag;
259 if(srgb)
261 name = &This->texture_name_srgb;
262 flag = SFLAG_INSRGBTEX;
264 else
266 name = &This->texture_name;
267 flag = SFLAG_INTEXTURE;
270 TRACE("(%p) : setting texture name %u\n", This, new_name);
272 if (!*name && new_name)
274 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
275 * surface has no texture name yet. See if we can get rid of this. */
276 if (This->Flags & flag)
277 ERR("Surface has SFLAG_INTEXTURE set, but no texture name\n");
278 IWineD3DSurface_ModifyLocation(iface, flag, FALSE);
281 *name = new_name;
282 surface_force_reload(iface);
285 void surface_set_texture_target(IWineD3DSurface *iface, GLenum target)
287 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
289 TRACE("(%p) : setting target %#x\n", This, target);
291 if (This->texture_target != target)
293 if (target == GL_TEXTURE_RECTANGLE_ARB)
295 This->Flags &= ~SFLAG_NORMCOORD;
297 else if (This->texture_target == GL_TEXTURE_RECTANGLE_ARB)
299 This->Flags |= SFLAG_NORMCOORD;
302 This->texture_target = target;
303 surface_force_reload(iface);
306 /* Context activation is done by the caller. */
307 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This, BOOL srgb) {
308 DWORD active_sampler;
310 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
311 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
312 * gl states. The current texture unit should always be a valid one.
314 * To be more specific, this is tricky because we can implicitly be called
315 * from sampler() in state.c. This means we can't touch anything other than
316 * whatever happens to be the currently active texture, or we would risk
317 * marking already applied sampler states dirty again.
319 * TODO: Track the current active texture per GL context instead of using glGet
321 GLint active_texture;
322 ENTER_GL();
323 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
324 LEAVE_GL();
325 active_sampler = This->resource.device->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
327 if (active_sampler != WINED3D_UNMAPPED_STAGE)
329 IWineD3DDeviceImpl_MarkStateDirty(This->resource.device, STATE_SAMPLER(active_sampler));
331 IWineD3DSurface_BindTexture((IWineD3DSurface *)This, srgb);
334 /* This function checks if the primary render target uses the 8bit paletted format. */
335 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
337 if (device->render_targets && device->render_targets[0]) {
338 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
339 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
340 && (render_target->resource.format_desc->format == WINED3DFMT_P8_UINT))
341 return TRUE;
343 return FALSE;
346 #undef GLINFO_LOCATION
348 #define GLINFO_LOCATION This->resource.device->adapter->gl_info
350 /* This call just downloads data, the caller is responsible for binding the
351 * correct texture. */
352 /* Context activation is done by the caller. */
353 static void surface_download_data(IWineD3DSurfaceImpl *This) {
354 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
356 /* Only support read back of converted P8 surfaces */
357 if (This->Flags & SFLAG_CONVERTED && format_desc->format != WINED3DFMT_P8_UINT)
359 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(format_desc->format));
360 return;
363 ENTER_GL();
365 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
367 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
368 This, This->texture_level, format_desc->glFormat, format_desc->glType,
369 This->resource.allocatedMemory);
371 if (This->Flags & SFLAG_PBO)
373 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
374 checkGLcall("glBindBufferARB");
375 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target, This->texture_level, NULL));
376 checkGLcall("glGetCompressedTexImageARB");
377 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
378 checkGLcall("glBindBufferARB");
380 else
382 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target,
383 This->texture_level, This->resource.allocatedMemory));
384 checkGLcall("glGetCompressedTexImageARB");
387 LEAVE_GL();
388 } else {
389 void *mem;
390 GLenum format = format_desc->glFormat;
391 GLenum type = format_desc->glType;
392 int src_pitch = 0;
393 int dst_pitch = 0;
395 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
396 if (format_desc->format == WINED3DFMT_P8_UINT && primary_render_target_is_p8(This->resource.device))
398 format = GL_ALPHA;
399 type = GL_UNSIGNED_BYTE;
402 if (This->Flags & SFLAG_NONPOW2) {
403 unsigned char alignment = This->resource.device->surface_alignment;
404 src_pitch = format_desc->byte_count * This->pow2Width;
405 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
406 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
407 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
408 } else {
409 mem = This->resource.allocatedMemory;
412 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
413 This, This->texture_level, format, type, mem);
415 if(This->Flags & SFLAG_PBO) {
416 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
417 checkGLcall("glBindBufferARB");
419 glGetTexImage(This->texture_target, This->texture_level, format, type, NULL);
420 checkGLcall("glGetTexImage");
422 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
423 checkGLcall("glBindBufferARB");
424 } else {
425 glGetTexImage(This->texture_target, This->texture_level, format, type, mem);
426 checkGLcall("glGetTexImage");
428 LEAVE_GL();
430 if (This->Flags & SFLAG_NONPOW2) {
431 const BYTE *src_data;
432 BYTE *dst_data;
433 UINT y;
435 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
436 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
437 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
439 * We're doing this...
441 * instead of boxing the texture :
442 * |<-texture width ->| -->pow2width| /\
443 * |111111111111111111| | |
444 * |222 Texture 222222| boxed empty | texture height
445 * |3333 Data 33333333| | |
446 * |444444444444444444| | \/
447 * ----------------------------------- |
448 * | boxed empty | boxed empty | pow2height
449 * | | | \/
450 * -----------------------------------
453 * we're repacking the data to the expected texture width
455 * |<-texture width ->| -->pow2width| /\
456 * |111111111111111111222222222222222| |
457 * |222333333333333333333444444444444| texture height
458 * |444444 | |
459 * | | \/
460 * | | |
461 * | empty | pow2height
462 * | | \/
463 * -----------------------------------
465 * == is the same as
467 * |<-texture width ->| /\
468 * |111111111111111111|
469 * |222222222222222222|texture height
470 * |333333333333333333|
471 * |444444444444444444| \/
472 * --------------------
474 * this also means that any references to allocatedMemory should work with the data as if were a
475 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
477 * internally the texture is still stored in a boxed format so any references to textureName will
478 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
480 * Performance should not be an issue, because applications normally do not lock the surfaces when
481 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
482 * and doesn't have to be re-read.
484 src_data = mem;
485 dst_data = This->resource.allocatedMemory;
486 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
487 for (y = 1 ; y < This->currentDesc.Height; y++) {
488 /* skip the first row */
489 src_data += src_pitch;
490 dst_data += dst_pitch;
491 memcpy(dst_data, src_data, dst_pitch);
494 HeapFree(GetProcessHeap(), 0, mem);
498 /* Surface has now been downloaded */
499 This->Flags |= SFLAG_INSYSMEM;
502 /* This call just uploads data, the caller is responsible for binding the
503 * correct texture. */
504 /* Context activation is done by the caller. */
505 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
506 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
508 TRACE("This %p, internal %#x, width %d, height %d, format %#x, type %#x, data %p.\n",
509 This, internal, width, height, format, type, data);
510 TRACE("target %#x, level %u, resource size %u.\n",
511 This->texture_target, This->texture_level, This->resource.size);
513 if (format_desc->heightscale != 1.0f && format_desc->heightscale != 0.0f) height *= format_desc->heightscale;
515 ENTER_GL();
517 if (This->Flags & SFLAG_PBO)
519 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
520 checkGLcall("glBindBufferARB");
522 TRACE("(%p) pbo: %#x, data: %p.\n", This, This->pbo, data);
523 data = NULL;
526 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
528 TRACE("Calling glCompressedTexSubImage2DARB.\n");
530 GL_EXTCALL(glCompressedTexSubImage2DARB(This->texture_target, This->texture_level,
531 0, 0, width, height, internal, This->resource.size, data));
532 checkGLcall("glCompressedTexSubImage2DARB");
534 else
536 TRACE("Calling glTexSubImage2D.\n");
538 glTexSubImage2D(This->texture_target, This->texture_level,
539 0, 0, width, height, format, type, data);
540 checkGLcall("glTexSubImage2D");
543 if (This->Flags & SFLAG_PBO)
545 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
546 checkGLcall("glBindBufferARB");
549 LEAVE_GL();
552 /* This call just allocates the texture, the caller is responsible for binding
553 * the correct texture. */
554 /* Context activation is done by the caller. */
555 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
556 const struct wined3d_gl_info *gl_info = &This->resource.device->adapter->gl_info;
557 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
558 BOOL enable_client_storage = FALSE;
559 const BYTE *mem = NULL;
561 if (format_desc->heightscale != 1.0f && format_desc->heightscale != 0.0f) height *= format_desc->heightscale;
563 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",
564 This, This->texture_target, This->texture_level, debug_d3dformat(format_desc->format),
565 internal, width, height, format, type);
567 ENTER_GL();
569 if (gl_info->supported[APPLE_CLIENT_STORAGE])
571 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
572 /* In some cases we want to disable client storage.
573 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
574 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
575 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
576 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
577 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
579 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
580 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
581 This->Flags &= ~SFLAG_CLIENT;
582 enable_client_storage = TRUE;
583 } else {
584 This->Flags |= SFLAG_CLIENT;
586 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
587 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
589 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
593 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED && mem)
595 GL_EXTCALL(glCompressedTexImage2DARB(This->texture_target, This->texture_level,
596 internal, width, height, 0, This->resource.size, mem));
598 else
600 glTexImage2D(This->texture_target, This->texture_level,
601 internal, width, height, 0, format, type, mem);
602 checkGLcall("glTexImage2D");
605 if(enable_client_storage) {
606 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
607 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
609 LEAVE_GL();
612 /* In D3D the depth stencil dimensions have to be greater than or equal to the
613 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
614 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
615 /* GL locking is done by the caller */
616 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
617 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
618 const struct wined3d_gl_info *gl_info = &This->resource.device->adapter->gl_info;
619 renderbuffer_entry_t *entry;
620 GLuint renderbuffer = 0;
621 unsigned int src_width, src_height;
623 src_width = This->pow2Width;
624 src_height = This->pow2Height;
626 /* A depth stencil smaller than the render target is not valid */
627 if (width > src_width || height > src_height) return;
629 /* Remove any renderbuffer set if the sizes match */
630 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
631 || (width == src_width && height == src_height))
633 This->current_renderbuffer = NULL;
634 return;
637 /* Look if we've already got a renderbuffer of the correct dimensions */
638 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
639 if (entry->width == width && entry->height == height) {
640 renderbuffer = entry->id;
641 This->current_renderbuffer = entry;
642 break;
646 if (!renderbuffer) {
647 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
648 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
649 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
650 This->resource.format_desc->glInternal, width, height);
652 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
653 entry->width = width;
654 entry->height = height;
655 entry->id = renderbuffer;
656 list_add_head(&This->renderbuffers, &entry->entry);
658 This->current_renderbuffer = entry;
661 checkGLcall("set_compatible_renderbuffer");
664 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
665 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
666 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
668 TRACE("(%p) : swapchain %p\n", This, swapchain);
670 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
671 if(swapchain_impl->render_to_fbo) {
672 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
673 return GL_COLOR_ATTACHMENT0;
675 TRACE("Returning GL_BACK\n");
676 return GL_BACK;
677 } else if (swapchain_impl->frontBuffer == iface) {
678 TRACE("Returning GL_FRONT\n");
679 return GL_FRONT;
682 FIXME("Higher back buffer, returning GL_BACK\n");
683 return GL_BACK;
686 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
687 void surface_add_dirty_rect(IWineD3DSurface *iface, const RECT *dirty_rect)
689 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
690 IWineD3DBaseTexture *baseTexture = NULL;
692 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
693 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
695 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
696 if (dirty_rect)
698 This->dirtyRect.left = min(This->dirtyRect.left, dirty_rect->left);
699 This->dirtyRect.top = min(This->dirtyRect.top, dirty_rect->top);
700 This->dirtyRect.right = max(This->dirtyRect.right, dirty_rect->right);
701 This->dirtyRect.bottom = max(This->dirtyRect.bottom, dirty_rect->bottom);
703 else
705 This->dirtyRect.left = 0;
706 This->dirtyRect.top = 0;
707 This->dirtyRect.right = This->currentDesc.Width;
708 This->dirtyRect.bottom = This->currentDesc.Height;
711 TRACE("(%p) : Dirty: yes, Rect:(%d, %d, %d, %d)\n", This, This->dirtyRect.left,
712 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
714 /* if the container is a basetexture then mark it dirty. */
715 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture)))
717 TRACE("Passing to container\n");
718 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
719 IWineD3DBaseTexture_Release(baseTexture);
723 static inline BOOL surface_can_stretch_rect(IWineD3DSurfaceImpl *src, IWineD3DSurfaceImpl *dst)
725 return ((src->resource.format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE)
726 || (src->resource.usage & WINED3DUSAGE_RENDERTARGET))
727 && ((dst->resource.format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE)
728 || (dst->resource.usage & WINED3DUSAGE_RENDERTARGET))
729 && (src->resource.format_desc->format == dst->resource.format_desc->format
730 || (is_identity_fixup(src->resource.format_desc->color_fixup)
731 && is_identity_fixup(dst->resource.format_desc->color_fixup)));
734 static ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface)
736 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
737 ULONG ref = InterlockedDecrement(&This->resource.ref);
738 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
740 if (!ref)
742 surface_cleanup(This);
743 This->resource.parent_ops->wined3d_object_destroyed(This->resource.parent);
745 TRACE("(%p) Released.\n", This);
746 HeapFree(GetProcessHeap(), 0, This);
749 return ref;
752 /* ****************************************************
753 IWineD3DSurface IWineD3DResource parts follow
754 **************************************************** */
756 void surface_internal_preload(IWineD3DSurface *iface, enum WINED3DSRGB srgb)
758 /* TODO: check for locks */
759 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
760 IWineD3DDeviceImpl *device = This->resource.device;
761 IWineD3DBaseTexture *baseTexture = NULL;
763 TRACE("(%p)Checking to see if the container is a base texture\n", This);
764 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
765 IWineD3DBaseTextureImpl *tex_impl = (IWineD3DBaseTextureImpl *) baseTexture;
766 TRACE("Passing to container\n");
767 tex_impl->baseTexture.internal_preload(baseTexture, srgb);
768 IWineD3DBaseTexture_Release(baseTexture);
769 } else {
770 struct wined3d_context *context = NULL;
772 TRACE("(%p) : About to load surface\n", This);
774 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
776 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
777 || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
779 if(palette9_changed(This)) {
780 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
781 /* TODO: This is not necessarily needed with hw palettized texture support */
782 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
783 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
784 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
788 IWineD3DSurface_LoadTexture(iface, srgb == SRGB_SRGB ? TRUE : FALSE);
790 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
791 /* Tell opengl to try and keep this texture in video ram (well mostly) */
792 GLclampf tmp;
793 tmp = 0.9f;
794 ENTER_GL();
795 glPrioritizeTextures(1, &This->texture_name, &tmp);
796 LEAVE_GL();
799 if (context) context_release(context);
801 return;
804 static void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
805 surface_internal_preload(iface, SRGB_ANY);
808 /* Context activation is done by the caller. */
809 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
810 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
811 This->resource.allocatedMemory =
812 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
814 ENTER_GL();
815 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
816 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
817 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
818 checkGLcall("glGetBufferSubDataARB");
819 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
820 checkGLcall("glDeleteBuffersARB");
821 LEAVE_GL();
823 This->pbo = 0;
824 This->Flags &= ~SFLAG_PBO;
827 BOOL surface_init_sysmem(IWineD3DSurface *iface)
829 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
831 if(!This->resource.allocatedMemory)
833 This->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->resource.size + RESOURCE_ALIGNMENT);
834 if(!This->resource.heapMemory)
836 ERR("Out of memory\n");
837 return FALSE;
839 This->resource.allocatedMemory =
840 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
842 else
844 memset(This->resource.allocatedMemory, 0, This->resource.size);
847 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
848 return TRUE;
851 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
852 IWineD3DBaseTexture *texture = NULL;
853 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
854 IWineD3DDeviceImpl *device = This->resource.device;
855 const struct wined3d_gl_info *gl_info;
856 renderbuffer_entry_t *entry, *entry2;
857 struct wined3d_context *context;
859 TRACE("(%p)\n", iface);
861 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
862 /* Default pool resources are supposed to be destroyed before Reset is called.
863 * Implicit resources stay however. So this means we have an implicit render target
864 * or depth stencil. The content may be destroyed, but we still have to tear down
865 * opengl resources, so we cannot leave early.
867 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
868 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
869 * or the depth stencil into an FBO the texture or render buffer will be removed
870 * and all flags get lost
872 surface_init_sysmem(iface);
873 } else {
874 /* Load the surface into system memory */
875 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
876 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
878 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
879 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSRGBTEX, FALSE);
880 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
882 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
883 gl_info = context->gl_info;
885 /* Destroy PBOs, but load them into real sysmem before */
886 if(This->Flags & SFLAG_PBO) {
887 surface_remove_pbo(This);
890 /* Destroy fbo render buffers. This is needed for implicit render targets, for
891 * all application-created targets the application has to release the surface
892 * before calling _Reset
894 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
895 ENTER_GL();
896 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
897 LEAVE_GL();
898 list_remove(&entry->entry);
899 HeapFree(GetProcessHeap(), 0, entry);
901 list_init(&This->renderbuffers);
902 This->current_renderbuffer = NULL;
904 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
905 * destroy it
907 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
908 if(!texture) {
909 ENTER_GL();
910 glDeleteTextures(1, &This->texture_name);
911 This->texture_name = 0;
912 glDeleteTextures(1, &This->texture_name_srgb);
913 This->texture_name_srgb = 0;
914 LEAVE_GL();
915 } else {
916 IWineD3DBaseTexture_Release(texture);
919 context_release(context);
921 return;
924 /* ******************************************************
925 IWineD3DSurface IWineD3DSurface parts follow
926 ****************************************************** */
928 /* Read the framebuffer back into the surface */
929 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, const RECT *rect, void *dest, UINT pitch)
931 IWineD3DDeviceImpl *myDevice = This->resource.device;
932 struct wined3d_context *context;
933 BYTE *mem;
934 GLint fmt;
935 GLint type;
936 BYTE *row, *top, *bottom;
937 int i;
938 BOOL bpp;
939 RECT local_rect;
940 BOOL srcIsUpsideDown;
941 GLint rowLen = 0;
942 GLint skipPix = 0;
943 GLint skipRow = 0;
945 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
946 static BOOL warned = FALSE;
947 if(!warned) {
948 ERR("The application tries to lock the render target, but render target locking is disabled\n");
949 warned = TRUE;
951 return;
954 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
955 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
956 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
957 * context->last_was_blit set on the unlock.
959 context = context_acquire(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
960 ENTER_GL();
962 /* Select the correct read buffer, and give some debug output.
963 * There is no need to keep track of the current read buffer or reset it, every part of the code
964 * that reads sets the read buffer as desired.
966 if (surface_is_offscreen((IWineD3DSurface *) This))
968 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
969 * Read from the back buffer
971 TRACE("Locking offscreen render target\n");
972 glReadBuffer(myDevice->offscreenBuffer);
973 srcIsUpsideDown = TRUE;
975 else
977 /* Onscreen surfaces are always part of a swapchain */
978 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *) This->container);
979 TRACE("Locking %#x buffer\n", buffer);
980 glReadBuffer(buffer);
981 checkGLcall("glReadBuffer");
982 srcIsUpsideDown = FALSE;
985 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
986 if(!rect) {
987 local_rect.left = 0;
988 local_rect.top = 0;
989 local_rect.right = This->currentDesc.Width;
990 local_rect.bottom = This->currentDesc.Height;
991 } else {
992 local_rect = *rect;
994 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
996 switch(This->resource.format_desc->format)
998 case WINED3DFMT_P8_UINT:
1000 if(primary_render_target_is_p8(myDevice)) {
1001 /* In case of P8 render targets the index is stored in the alpha component */
1002 fmt = GL_ALPHA;
1003 type = GL_UNSIGNED_BYTE;
1004 mem = dest;
1005 bpp = This->resource.format_desc->byte_count;
1006 } else {
1007 /* GL can't return palettized data, so read ARGB pixels into a
1008 * separate block of memory and convert them into palettized format
1009 * in software. Slow, but if the app means to use palettized render
1010 * targets and locks it...
1012 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
1013 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
1014 * for the color channels when palettizing the colors.
1016 fmt = GL_RGB;
1017 type = GL_UNSIGNED_BYTE;
1018 pitch *= 3;
1019 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
1020 if(!mem) {
1021 ERR("Out of memory\n");
1022 LEAVE_GL();
1023 return;
1025 bpp = This->resource.format_desc->byte_count * 3;
1028 break;
1030 default:
1031 mem = dest;
1032 fmt = This->resource.format_desc->glFormat;
1033 type = This->resource.format_desc->glType;
1034 bpp = This->resource.format_desc->byte_count;
1037 if(This->Flags & SFLAG_PBO) {
1038 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
1039 checkGLcall("glBindBufferARB");
1040 if(mem != NULL) {
1041 ERR("mem not null for pbo -- unexpected\n");
1042 mem = NULL;
1046 /* Save old pixel store pack state */
1047 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
1048 checkGLcall("glGetIntegerv");
1049 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
1050 checkGLcall("glGetIntegerv");
1051 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
1052 checkGLcall("glGetIntegerv");
1054 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1055 glPixelStorei(GL_PACK_ROW_LENGTH, This->currentDesc.Width);
1056 checkGLcall("glPixelStorei");
1057 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
1058 checkGLcall("glPixelStorei");
1059 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
1060 checkGLcall("glPixelStorei");
1062 glReadPixels(local_rect.left, (!srcIsUpsideDown) ? (This->currentDesc.Height - local_rect.bottom) : local_rect.top ,
1063 local_rect.right - local_rect.left,
1064 local_rect.bottom - local_rect.top,
1065 fmt, type, mem);
1066 checkGLcall("glReadPixels");
1068 /* Reset previous pixel store pack state */
1069 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
1070 checkGLcall("glPixelStorei");
1071 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
1072 checkGLcall("glPixelStorei");
1073 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
1074 checkGLcall("glPixelStorei");
1076 if(This->Flags & SFLAG_PBO) {
1077 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1078 checkGLcall("glBindBufferARB");
1080 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
1081 * to get a pointer to it and perform the flipping in software. This is a lot
1082 * faster than calling glReadPixels for each line. In case we want more speed
1083 * we should rerender it flipped in a FBO and read the data back from the FBO. */
1084 if(!srcIsUpsideDown) {
1085 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1086 checkGLcall("glBindBufferARB");
1088 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1089 checkGLcall("glMapBufferARB");
1093 /* TODO: Merge this with the palettization loop below for P8 targets */
1094 if(!srcIsUpsideDown) {
1095 UINT len, off;
1096 /* glReadPixels returns the image upside down, and there is no way to prevent this.
1097 Flip the lines in software */
1098 len = (local_rect.right - local_rect.left) * bpp;
1099 off = local_rect.left * bpp;
1101 row = HeapAlloc(GetProcessHeap(), 0, len);
1102 if(!row) {
1103 ERR("Out of memory\n");
1104 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT) HeapFree(GetProcessHeap(), 0, mem);
1105 LEAVE_GL();
1106 return;
1109 top = mem + pitch * local_rect.top;
1110 bottom = mem + pitch * (local_rect.bottom - 1);
1111 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
1112 memcpy(row, top + off, len);
1113 memcpy(top + off, bottom + off, len);
1114 memcpy(bottom + off, row, len);
1115 top += pitch;
1116 bottom -= pitch;
1118 HeapFree(GetProcessHeap(), 0, row);
1120 /* Unmap the temp PBO buffer */
1121 if(This->Flags & SFLAG_PBO) {
1122 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1123 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1127 LEAVE_GL();
1128 context_release(context);
1130 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
1131 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
1132 * the same color but we have no choice.
1133 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
1135 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(myDevice))
1137 const PALETTEENTRY *pal = NULL;
1138 DWORD width = pitch / 3;
1139 int x, y, c;
1141 if(This->palette) {
1142 pal = This->palette->palents;
1143 } else {
1144 ERR("Palette is missing, cannot perform inverse palette lookup\n");
1145 HeapFree(GetProcessHeap(), 0, mem);
1146 return ;
1149 for(y = local_rect.top; y < local_rect.bottom; y++) {
1150 for(x = local_rect.left; x < local_rect.right; x++) {
1151 /* start lines pixels */
1152 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
1153 const BYTE *green = blue + 1;
1154 const BYTE *red = green + 1;
1156 for(c = 0; c < 256; c++) {
1157 if(*red == pal[c].peRed &&
1158 *green == pal[c].peGreen &&
1159 *blue == pal[c].peBlue)
1161 *((BYTE *) dest + y * width + x) = c;
1162 break;
1167 HeapFree(GetProcessHeap(), 0, mem);
1171 /* Read the framebuffer contents into a texture */
1172 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This, BOOL srgb)
1174 IWineD3DDeviceImpl *device = This->resource.device;
1175 IWineD3DSwapChainImpl *swapchain;
1176 struct wined3d_context *context;
1177 int bpp;
1178 GLenum format, internal, type;
1179 CONVERT_TYPES convert;
1180 GLint prevRead;
1181 BOOL alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
1183 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
1185 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
1186 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
1187 * states in the stateblock, and no driver was found yet that had bugs in that regard.
1189 context = context_acquire(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
1190 surface_bind_and_dirtify(This, srgb);
1192 ENTER_GL();
1193 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1194 LEAVE_GL();
1196 /* Select the correct read buffer, and give some debug output.
1197 * There is no need to keep track of the current read buffer or reset it, every part of the code
1198 * that reads sets the read buffer as desired.
1200 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
1202 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1203 TRACE("Locking %#x buffer\n", buffer);
1205 ENTER_GL();
1206 glReadBuffer(buffer);
1207 checkGLcall("glReadBuffer");
1208 LEAVE_GL();
1210 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1211 } else {
1212 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1213 * Read from the back buffer
1215 TRACE("Locking offscreen render target\n");
1216 ENTER_GL();
1217 glReadBuffer(device->offscreenBuffer);
1218 checkGLcall("glReadBuffer");
1219 LEAVE_GL();
1222 if(!(This->Flags & alloc_flag)) {
1223 surface_allocate_surface(This, internal, This->pow2Width,
1224 This->pow2Height, format, type);
1225 This->Flags |= alloc_flag;
1228 ENTER_GL();
1229 /* If !SrcIsUpsideDown we should flip the surface.
1230 * This can be done using glCopyTexSubImage2D but this
1231 * is VERY slow, so don't do that. We should prevent
1232 * this code from getting called in such cases or perhaps
1233 * we can use FBOs */
1235 glCopyTexSubImage2D(This->texture_target, This->texture_level,
1236 0, 0, 0, 0, This->currentDesc.Width, This->currentDesc.Height);
1237 checkGLcall("glCopyTexSubImage2D");
1239 glReadBuffer(prevRead);
1240 checkGLcall("glReadBuffer");
1242 LEAVE_GL();
1244 context_release(context);
1246 TRACE("Updated target %d\n", This->texture_target);
1249 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This)
1251 IWineD3DDeviceImpl *device = This->resource.device;
1252 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1254 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
1255 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
1256 * changed
1258 if(!(This->Flags & SFLAG_DYNLOCK)) {
1259 This->lockCount++;
1260 /* MAXLOCKCOUNT is defined in wined3d_private.h */
1261 if(This->lockCount > MAXLOCKCOUNT) {
1262 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
1263 This->Flags |= SFLAG_DYNLOCK;
1267 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
1268 * Also don't create a PBO for systemmem surfaces.
1270 if (gl_info->supported[ARB_PIXEL_BUFFER_OBJECT] && (This->Flags & SFLAG_DYNLOCK)
1271 && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2))
1272 && (This->resource.pool != WINED3DPOOL_SYSTEMMEM))
1274 GLenum error;
1275 struct wined3d_context *context;
1277 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
1278 ENTER_GL();
1280 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
1281 error = glGetError();
1282 if(This->pbo == 0 || error != GL_NO_ERROR) {
1283 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
1286 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
1288 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1289 checkGLcall("glBindBufferARB");
1291 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1292 checkGLcall("glBufferDataARB");
1294 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1295 checkGLcall("glBindBufferARB");
1297 /* We don't need the system memory anymore and we can't even use it for PBOs */
1298 if(!(This->Flags & SFLAG_CLIENT)) {
1299 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1300 This->resource.heapMemory = NULL;
1302 This->resource.allocatedMemory = NULL;
1303 This->Flags |= SFLAG_PBO;
1304 LEAVE_GL();
1305 context_release(context);
1307 else if (!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO))
1309 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1310 * or a pbo to map
1312 if(!This->resource.heapMemory) {
1313 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1315 This->resource.allocatedMemory =
1316 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1317 if(This->Flags & SFLAG_INSYSMEM) {
1318 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1323 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1324 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1325 IWineD3DDeviceImpl *myDevice = This->resource.device;
1326 const RECT *pass_rect = pRect;
1328 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1330 /* This is also done in the base class, but we have to verify this before loading any data from
1331 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1332 * may interfere, and all other bad things may happen
1334 if (This->Flags & SFLAG_LOCKED) {
1335 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1336 return WINED3DERR_INVALIDCALL;
1338 This->Flags |= SFLAG_LOCKED;
1340 if (!(This->Flags & SFLAG_LOCKABLE))
1342 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1345 if (Flags & WINED3DLOCK_DISCARD) {
1346 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1347 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1348 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1349 This->Flags |= SFLAG_INSYSMEM;
1350 goto lock_end;
1353 if (This->Flags & SFLAG_INSYSMEM) {
1354 TRACE("Local copy is up to date, not downloading data\n");
1355 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1356 goto lock_end;
1359 /* IWineD3DSurface_LoadLocation() does not check if the rectangle specifies
1360 * the full surface. Most callers don't need that, so do it here. */
1361 if (pRect && pRect->top == 0 && pRect->left == 0
1362 && pRect->right == This->currentDesc.Width
1363 && pRect->bottom == This->currentDesc.Height)
1365 pass_rect = NULL;
1368 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
1369 && ((This->Flags & SFLAG_SWAPCHAIN) || iface == myDevice->render_targets[0])))
1371 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1374 lock_end:
1375 if (This->Flags & SFLAG_PBO)
1377 struct wined3d_context *context;
1379 context = context_acquire(myDevice, NULL, CTXUSAGE_RESOURCELOAD);
1380 ENTER_GL();
1381 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1382 checkGLcall("glBindBufferARB");
1384 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1385 if(This->resource.allocatedMemory) {
1386 ERR("The surface already has PBO memory allocated!\n");
1389 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1390 checkGLcall("glMapBufferARB");
1392 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1393 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1394 checkGLcall("glBindBufferARB");
1396 LEAVE_GL();
1397 context_release(context);
1400 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1401 /* Don't dirtify */
1402 } else {
1403 IWineD3DBaseTexture *pBaseTexture;
1405 * Dirtify on lock
1406 * as seen in msdn docs
1408 surface_add_dirty_rect(iface, pRect);
1410 /** Dirtify Container if needed */
1411 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1412 TRACE("Making container dirty\n");
1413 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1414 IWineD3DBaseTexture_Release(pBaseTexture);
1415 } else {
1416 TRACE("Surface is standalone, no need to dirty the container\n");
1420 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1423 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1424 GLint prev_store;
1425 GLint prev_rasterpos[4];
1426 GLint skipBytes = 0;
1427 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1428 IWineD3DDeviceImpl *myDevice = This->resource.device;
1429 IWineD3DSwapChainImpl *swapchain;
1430 struct wined3d_context *context;
1432 /* Activate the correct context for the render target */
1433 context = context_acquire(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1434 ENTER_GL();
1436 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
1437 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1438 TRACE("Unlocking %#x buffer\n", buffer);
1439 glDrawBuffer(buffer);
1440 checkGLcall("glDrawBuffer");
1442 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1443 } else {
1444 /* Primary offscreen render target */
1445 TRACE("Offscreen render target\n");
1446 glDrawBuffer(myDevice->offscreenBuffer);
1447 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1450 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1451 checkGLcall("glGetIntegerv");
1452 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1453 checkGLcall("glGetIntegerv");
1454 glPixelZoom(1.0f, -1.0f);
1455 checkGLcall("glPixelZoom");
1457 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1458 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1459 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1461 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1462 checkGLcall("glRasterPos3i");
1464 /* Some drivers(radeon dri, others?) don't like exceptions during
1465 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1466 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1467 * catch to put the dib section in InSync mode, which leads to a crash
1468 * and a blocked x server on my radeon card.
1470 * The following lines read the dib section so it is put in InSync mode
1471 * before glDrawPixels is called and the crash is prevented. There won't
1472 * be any interfering gdi accesses, because UnlockRect is called from
1473 * ReleaseDC, and the app won't use the dc any more afterwards.
1475 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1476 volatile BYTE read;
1477 read = This->resource.allocatedMemory[0];
1480 if(This->Flags & SFLAG_PBO) {
1481 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1482 checkGLcall("glBindBufferARB");
1485 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1486 if(This->Flags & SFLAG_LOCKED) {
1487 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1488 (This->lockedRect.bottom - This->lockedRect.top)-1,
1489 fmt, type,
1490 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1491 checkGLcall("glDrawPixels");
1492 } else {
1493 glDrawPixels(This->currentDesc.Width,
1494 This->currentDesc.Height,
1495 fmt, type, mem);
1496 checkGLcall("glDrawPixels");
1499 if(This->Flags & SFLAG_PBO) {
1500 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1501 checkGLcall("glBindBufferARB");
1504 glPixelZoom(1.0f, 1.0f);
1505 checkGLcall("glPixelZoom");
1507 glRasterPos3iv(&prev_rasterpos[0]);
1508 checkGLcall("glRasterPos3iv");
1510 /* Reset to previous pack row length */
1511 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1512 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH)");
1514 if(!swapchain) {
1515 glDrawBuffer(myDevice->offscreenBuffer);
1516 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1517 } else if(swapchain->backBuffer) {
1518 glDrawBuffer(GL_BACK);
1519 checkGLcall("glDrawBuffer(GL_BACK)");
1520 } else {
1521 glDrawBuffer(GL_FRONT);
1522 checkGLcall("glDrawBuffer(GL_FRONT)");
1524 LEAVE_GL();
1525 context_release(context);
1527 return;
1530 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1531 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1532 IWineD3DDeviceImpl *myDevice = This->resource.device;
1533 BOOL fullsurface;
1535 if (!(This->Flags & SFLAG_LOCKED)) {
1536 WARN("trying to Unlock an unlocked surf@%p\n", This);
1537 return WINEDDERR_NOTLOCKED;
1540 if (This->Flags & SFLAG_PBO)
1542 struct wined3d_context *context;
1544 TRACE("Freeing PBO memory\n");
1546 context = context_acquire(myDevice, NULL, CTXUSAGE_RESOURCELOAD);
1547 ENTER_GL();
1548 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1549 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1550 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1551 checkGLcall("glUnmapBufferARB");
1552 LEAVE_GL();
1553 context_release(context);
1555 This->resource.allocatedMemory = NULL;
1558 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1560 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1561 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1562 goto unlock_end;
1565 if ((This->Flags & SFLAG_SWAPCHAIN) || (myDevice->render_targets && iface == myDevice->render_targets[0]))
1567 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1568 static BOOL warned = FALSE;
1569 if(!warned) {
1570 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1571 warned = TRUE;
1573 goto unlock_end;
1576 if(This->dirtyRect.left == 0 &&
1577 This->dirtyRect.top == 0 &&
1578 This->dirtyRect.right == This->currentDesc.Width &&
1579 This->dirtyRect.bottom == This->currentDesc.Height) {
1580 fullsurface = TRUE;
1581 } else {
1582 /* TODO: Proper partial rectangle tracking */
1583 fullsurface = FALSE;
1584 This->Flags |= SFLAG_INSYSMEM;
1587 switch(wined3d_settings.rendertargetlock_mode) {
1588 case RTL_READTEX:
1589 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1590 /* drop through */
1592 case RTL_READDRAW:
1593 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1594 break;
1597 if(!fullsurface) {
1598 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1599 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1600 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1601 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1602 * not fully up to date because only a subrectangle was read in LockRect.
1604 This->Flags &= ~SFLAG_INSYSMEM;
1605 This->Flags |= SFLAG_INDRAWABLE;
1608 This->dirtyRect.left = This->currentDesc.Width;
1609 This->dirtyRect.top = This->currentDesc.Height;
1610 This->dirtyRect.right = 0;
1611 This->dirtyRect.bottom = 0;
1612 } else if(iface == myDevice->stencilBufferTarget) {
1613 FIXME("Depth Stencil buffer locking is not implemented\n");
1614 } else {
1615 /* The rest should be a normal texture */
1616 IWineD3DBaseTextureImpl *impl;
1617 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1618 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1619 * states need resetting
1621 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1622 if(impl->baseTexture.bindCount) {
1623 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1625 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1629 unlock_end:
1630 This->Flags &= ~SFLAG_LOCKED;
1631 memset(&This->lockedRect, 0, sizeof(RECT));
1633 /* Overlays have to be redrawn manually after changes with the GL implementation */
1634 if(This->overlay_dest) {
1635 IWineD3DSurface_DrawOverlay(iface);
1637 return WINED3D_OK;
1640 static HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC)
1642 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1643 WINED3DLOCKED_RECT lock;
1644 HRESULT hr;
1645 RGBQUAD col[256];
1647 TRACE("(%p)->(%p)\n",This,pHDC);
1649 if(This->Flags & SFLAG_USERPTR) {
1650 ERR("Not supported on surfaces with an application-provided surfaces\n");
1651 return WINEDDERR_NODC;
1654 /* Give more detailed info for ddraw */
1655 if (This->Flags & SFLAG_DCINUSE)
1656 return WINEDDERR_DCALREADYCREATED;
1658 /* Can't GetDC if the surface is locked */
1659 if (This->Flags & SFLAG_LOCKED)
1660 return WINED3DERR_INVALIDCALL;
1662 memset(&lock, 0, sizeof(lock)); /* To be sure */
1664 /* Create a DIB section if there isn't a hdc yet */
1665 if(!This->hDC) {
1666 hr = IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1667 if(FAILED(hr)) return WINED3DERR_INVALIDCALL;
1668 if(This->Flags & SFLAG_CLIENT) {
1669 surface_internal_preload(iface, SRGB_RGB);
1672 /* Use the dib section from now on if we are not using a PBO */
1673 if(!(This->Flags & SFLAG_PBO))
1674 This->resource.allocatedMemory = This->dib.bitmap_data;
1677 /* Lock the surface */
1678 hr = IWineD3DSurface_LockRect(iface,
1679 &lock,
1680 NULL,
1683 if(This->Flags & SFLAG_PBO) {
1684 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1685 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1688 if(FAILED(hr)) {
1689 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1690 /* keep the dib section */
1691 return hr;
1694 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
1695 || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
1697 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1698 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1699 unsigned int n;
1700 const PALETTEENTRY *pal = NULL;
1702 if(This->palette) {
1703 pal = This->palette->palents;
1704 } else {
1705 IWineD3DSurfaceImpl *dds_primary;
1706 IWineD3DSwapChainImpl *swapchain;
1707 swapchain = (IWineD3DSwapChainImpl *)This->resource.device->swapchains[0];
1708 dds_primary = (IWineD3DSurfaceImpl *)swapchain->frontBuffer;
1709 if (dds_primary && dds_primary->palette)
1710 pal = dds_primary->palette->palents;
1713 if (pal) {
1714 for (n=0; n<256; n++) {
1715 col[n].rgbRed = pal[n].peRed;
1716 col[n].rgbGreen = pal[n].peGreen;
1717 col[n].rgbBlue = pal[n].peBlue;
1718 col[n].rgbReserved = 0;
1720 SetDIBColorTable(This->hDC, 0, 256, col);
1724 *pHDC = This->hDC;
1725 TRACE("returning %p\n",*pHDC);
1726 This->Flags |= SFLAG_DCINUSE;
1728 return WINED3D_OK;
1731 static HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC)
1733 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1735 TRACE("(%p)->(%p)\n",This,hDC);
1737 if (!(This->Flags & SFLAG_DCINUSE))
1738 return WINEDDERR_NODC;
1740 if (This->hDC !=hDC) {
1741 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1742 return WINEDDERR_NODC;
1745 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1746 /* Copy the contents of the DIB over to the PBO */
1747 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1750 /* we locked first, so unlock now */
1751 IWineD3DSurface_UnlockRect(iface);
1753 This->Flags &= ~SFLAG_DCINUSE;
1755 return WINED3D_OK;
1758 /* ******************************************************
1759 IWineD3DSurface Internal (No mapping to directx api) parts follow
1760 ****************************************************** */
1762 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) {
1763 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1764 const struct GlPixelFormatDesc *glDesc = This->resource.format_desc;
1765 IWineD3DDeviceImpl *device = This->resource.device;
1766 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1768 /* Default values: From the surface */
1769 *format = glDesc->glFormat;
1770 *type = glDesc->glType;
1771 *convert = NO_CONVERSION;
1772 *target_bpp = glDesc->byte_count;
1774 if(srgb_mode) {
1775 *internal = glDesc->glGammaInternal;
1777 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET
1778 && surface_is_offscreen((IWineD3DSurface *) This))
1780 *internal = glDesc->rtInternal;
1781 } else {
1782 *internal = glDesc->glInternal;
1785 /* Ok, now look if we have to do any conversion */
1786 switch(This->resource.format_desc->format)
1788 case WINED3DFMT_P8_UINT:
1789 /* ****************
1790 Paletted Texture
1791 **************** */
1793 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1794 * of the two is available make sure texturing is requested as neither of the two works in
1795 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1796 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1797 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1798 * conflicts with this.
1800 if (!(gl_info->supported[EXT_PALETTED_TEXTURE] || (gl_info->supported[ARB_FRAGMENT_PROGRAM]
1801 && device->render_targets && This == (IWineD3DSurfaceImpl*)device->render_targets[0]))
1802 || colorkey_active || !use_texturing)
1804 *format = GL_RGBA;
1805 *internal = GL_RGBA;
1806 *type = GL_UNSIGNED_BYTE;
1807 *target_bpp = 4;
1808 if(colorkey_active) {
1809 *convert = CONVERT_PALETTED_CK;
1810 } else {
1811 *convert = CONVERT_PALETTED;
1814 else if (!gl_info->supported[EXT_PALETTED_TEXTURE] && gl_info->supported[ARB_FRAGMENT_PROGRAM])
1816 *format = GL_ALPHA;
1817 *type = GL_UNSIGNED_BYTE;
1818 *target_bpp = 1;
1821 break;
1823 case WINED3DFMT_B2G3R3_UNORM:
1824 /* **********************
1825 GL_UNSIGNED_BYTE_3_3_2
1826 ********************** */
1827 if (colorkey_active) {
1828 /* This texture format will never be used.. So do not care about color keying
1829 up until the point in time it will be needed :-) */
1830 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1832 break;
1834 case WINED3DFMT_B5G6R5_UNORM:
1835 if (colorkey_active) {
1836 *convert = CONVERT_CK_565;
1837 *format = GL_RGBA;
1838 *internal = GL_RGB5_A1;
1839 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1841 break;
1843 case WINED3DFMT_B5G5R5X1_UNORM:
1844 if (colorkey_active) {
1845 *convert = CONVERT_CK_5551;
1846 *format = GL_BGRA;
1847 *internal = GL_RGB5_A1;
1848 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1850 break;
1852 case WINED3DFMT_B8G8R8_UNORM:
1853 if (colorkey_active) {
1854 *convert = CONVERT_CK_RGB24;
1855 *format = GL_RGBA;
1856 *internal = GL_RGBA8;
1857 *type = GL_UNSIGNED_INT_8_8_8_8;
1858 *target_bpp = 4;
1860 break;
1862 case WINED3DFMT_B8G8R8X8_UNORM:
1863 if (colorkey_active) {
1864 *convert = CONVERT_RGB32_888;
1865 *format = GL_RGBA;
1866 *internal = GL_RGBA8;
1867 *type = GL_UNSIGNED_INT_8_8_8_8;
1869 break;
1871 case WINED3DFMT_R8G8_SNORM:
1872 if (gl_info->supported[NV_TEXTURE_SHADER]) break;
1873 *convert = CONVERT_V8U8;
1874 *format = GL_BGR;
1875 *type = GL_UNSIGNED_BYTE;
1876 *target_bpp = 3;
1877 break;
1879 case WINED3DFMT_R5G5_SNORM_L6_UNORM:
1880 *convert = CONVERT_L6V5U5;
1881 if (gl_info->supported[NV_TEXTURE_SHADER])
1883 *target_bpp = 3;
1884 /* Use format and types from table */
1885 } else {
1886 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1887 *target_bpp = 2;
1888 *format = GL_RGB;
1889 *type = GL_UNSIGNED_SHORT_5_6_5;
1891 break;
1893 case WINED3DFMT_R8G8_SNORM_L8X8_UNORM:
1894 *convert = CONVERT_X8L8V8U8;
1895 *target_bpp = 4;
1896 if (gl_info->supported[NV_TEXTURE_SHADER])
1898 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1899 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1900 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1901 * the needed type and format parameter, so the internal format contains a
1902 * 4th component, which is returned as alpha
1904 } else {
1905 *format = GL_BGRA;
1906 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1908 break;
1910 case WINED3DFMT_R8G8B8A8_SNORM:
1911 if (gl_info->supported[NV_TEXTURE_SHADER]) break;
1912 *convert = CONVERT_Q8W8V8U8;
1913 *format = GL_BGRA;
1914 *type = GL_UNSIGNED_BYTE;
1915 *target_bpp = 4;
1916 break;
1918 case WINED3DFMT_R16G16_SNORM:
1919 if (gl_info->supported[NV_TEXTURE_SHADER]) break;
1920 *convert = CONVERT_V16U16;
1921 *format = GL_BGR;
1922 *type = GL_UNSIGNED_SHORT;
1923 *target_bpp = 6;
1924 break;
1926 case WINED3DFMT_L4A4_UNORM:
1927 /* WINED3DFMT_L4A4_UNORM exists as an internal gl format, but for some reason there is not
1928 * format+type combination to load it. Thus convert it to A8L8, then load it
1929 * with A4L4 internal, but A8L8 format+type
1931 *convert = CONVERT_A4L4;
1932 *format = GL_LUMINANCE_ALPHA;
1933 *type = GL_UNSIGNED_BYTE;
1934 *target_bpp = 2;
1935 break;
1937 case WINED3DFMT_R16G16_UNORM:
1938 *convert = CONVERT_G16R16;
1939 *format = GL_RGB;
1940 *type = GL_UNSIGNED_SHORT;
1941 *target_bpp = 6;
1942 break;
1944 case WINED3DFMT_R16G16_FLOAT:
1945 *convert = CONVERT_R16G16F;
1946 *format = GL_RGB;
1947 *type = GL_HALF_FLOAT_ARB;
1948 *target_bpp = 6;
1949 break;
1951 case WINED3DFMT_R32G32_FLOAT:
1952 *convert = CONVERT_R32G32F;
1953 *format = GL_RGB;
1954 *type = GL_FLOAT;
1955 *target_bpp = 12;
1956 break;
1958 case WINED3DFMT_S1_UINT_D15_UNORM:
1959 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1960 || gl_info->supported[EXT_PACKED_DEPTH_STENCIL])
1962 *convert = CONVERT_D15S1;
1963 *target_bpp = 4;
1965 break;
1967 case WINED3DFMT_S4X4_UINT_D24_UNORM:
1968 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1969 || gl_info->supported[EXT_PACKED_DEPTH_STENCIL])
1971 *convert = CONVERT_D24X4S4;
1973 break;
1975 case WINED3DFMT_S8_UINT_D24_FLOAT:
1976 if (gl_info->supported[ARB_DEPTH_BUFFER_FLOAT])
1978 *convert = CONVERT_D24FS8;
1979 *target_bpp = 8;
1981 break;
1983 default:
1984 break;
1987 return WINED3D_OK;
1990 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey)
1992 IWineD3DDeviceImpl *device = This->resource.device;
1993 IWineD3DPaletteImpl *pal = This->palette;
1994 BOOL index_in_alpha = FALSE;
1995 unsigned int i;
1997 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
1998 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
1999 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2000 * duplicate entries. Store the color key in the unused alpha component to speed the
2001 * download up and to make conversion unneeded. */
2002 index_in_alpha = primary_render_target_is_p8(device);
2004 if (!pal)
2006 UINT dxVersion = ((IWineD3DImpl *)device->wined3d)->dxVersion;
2008 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2009 if (dxVersion <= 7)
2011 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2012 if (index_in_alpha)
2014 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
2015 * there's no palette at this time. */
2016 for (i = 0; i < 256; i++) table[i][3] = i;
2019 else
2021 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2022 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2023 * capability flag is present (wine does advertise this capability) */
2024 for (i = 0; i < 256; ++i)
2026 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2027 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2028 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2029 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2033 else
2035 TRACE("Using surface palette %p\n", pal);
2036 /* Get the surface's palette */
2037 for (i = 0; i < 256; ++i)
2039 table[i][0] = pal->palents[i].peRed;
2040 table[i][1] = pal->palents[i].peGreen;
2041 table[i][2] = pal->palents[i].peBlue;
2043 /* When index_in_alpha is set the palette index is stored in the
2044 * alpha component. In case of a readback we can then read
2045 * GL_ALPHA. Color keying is handled in BltOverride using a
2046 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
2047 * color key itself is passed to glAlphaFunc in other cases the
2048 * alpha component of pixels that should be masked away is set to 0. */
2049 if (index_in_alpha)
2051 table[i][3] = i;
2053 else if (colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue)
2054 && (i <= This->SrcBltCKey.dwColorSpaceHighValue))
2056 table[i][3] = 0x00;
2058 else if(pal->Flags & WINEDDPCAPS_ALPHA)
2060 table[i][3] = pal->palents[i].peFlags;
2062 else
2064 table[i][3] = 0xFF;
2070 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
2071 UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This)
2073 IWineD3DDeviceImpl *device = This->resource.device;
2074 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
2075 const BYTE *source;
2076 BYTE *dest;
2077 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
2079 switch (convert) {
2080 case NO_CONVERSION:
2082 memcpy(dst, src, pitch * height);
2083 break;
2085 case CONVERT_PALETTED:
2086 case CONVERT_PALETTED_CK:
2088 IWineD3DPaletteImpl* pal = This->palette;
2089 BYTE table[256][4];
2090 unsigned int x, y;
2092 if( pal == NULL) {
2093 /* TODO: If we are a sublevel, try to get the palette from level 0 */
2096 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2098 for (y = 0; y < height; y++)
2100 source = src + pitch * y;
2101 dest = dst + outpitch * y;
2102 /* This is an 1 bpp format, using the width here is fine */
2103 for (x = 0; x < width; x++) {
2104 BYTE color = *source++;
2105 *dest++ = table[color][0];
2106 *dest++ = table[color][1];
2107 *dest++ = table[color][2];
2108 *dest++ = table[color][3];
2112 break;
2114 case CONVERT_CK_565:
2116 /* Converting the 565 format in 5551 packed to emulate color-keying.
2118 Note : in all these conversion, it would be best to average the averaging
2119 pixels to get the color of the pixel that will be color-keyed to
2120 prevent 'color bleeding'. This will be done later on if ever it is
2121 too visible.
2123 Note2: Nvidia documents say that their driver does not support alpha + color keying
2124 on the same surface and disables color keying in such a case
2126 unsigned int x, y;
2127 const WORD *Source;
2128 WORD *Dest;
2130 TRACE("Color keyed 565\n");
2132 for (y = 0; y < height; y++) {
2133 Source = (const WORD *)(src + y * pitch);
2134 Dest = (WORD *) (dst + y * outpitch);
2135 for (x = 0; x < width; x++ ) {
2136 WORD color = *Source++;
2137 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
2138 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2139 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2140 *Dest |= 0x0001;
2142 Dest++;
2146 break;
2148 case CONVERT_CK_5551:
2150 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
2151 unsigned int x, y;
2152 const WORD *Source;
2153 WORD *Dest;
2154 TRACE("Color keyed 5551\n");
2155 for (y = 0; y < height; y++) {
2156 Source = (const WORD *)(src + y * pitch);
2157 Dest = (WORD *) (dst + y * outpitch);
2158 for (x = 0; x < width; x++ ) {
2159 WORD color = *Source++;
2160 *Dest = color;
2161 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2162 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2163 *Dest |= (1 << 15);
2165 else {
2166 *Dest &= ~(1 << 15);
2168 Dest++;
2172 break;
2174 case CONVERT_CK_RGB24:
2176 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
2177 unsigned int x, y;
2178 for (y = 0; y < height; y++)
2180 source = src + pitch * y;
2181 dest = dst + outpitch * y;
2182 for (x = 0; x < width; x++) {
2183 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
2184 DWORD dstcolor = color << 8;
2185 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2186 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2187 dstcolor |= 0xff;
2189 *(DWORD*)dest = dstcolor;
2190 source += 3;
2191 dest += 4;
2195 break;
2197 case CONVERT_RGB32_888:
2199 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
2200 unsigned int x, y;
2201 for (y = 0; y < height; y++)
2203 source = src + pitch * y;
2204 dest = dst + outpitch * y;
2205 for (x = 0; x < width; x++) {
2206 DWORD color = 0xffffff & *(const DWORD*)source;
2207 DWORD dstcolor = color << 8;
2208 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2209 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2210 dstcolor |= 0xff;
2212 *(DWORD*)dest = dstcolor;
2213 source += 4;
2214 dest += 4;
2218 break;
2220 case CONVERT_V8U8:
2222 unsigned int x, y;
2223 const short *Source;
2224 unsigned char *Dest;
2225 for(y = 0; y < height; y++) {
2226 Source = (const short *)(src + y * pitch);
2227 Dest = dst + y * outpitch;
2228 for (x = 0; x < width; x++ ) {
2229 long color = (*Source++);
2230 /* B */ Dest[0] = 0xff;
2231 /* G */ Dest[1] = (color >> 8) + 128; /* V */
2232 /* R */ Dest[2] = (color) + 128; /* U */
2233 Dest += 3;
2236 break;
2239 case CONVERT_V16U16:
2241 unsigned int x, y;
2242 const DWORD *Source;
2243 unsigned short *Dest;
2244 for(y = 0; y < height; y++) {
2245 Source = (const DWORD *)(src + y * pitch);
2246 Dest = (unsigned short *) (dst + y * outpitch);
2247 for (x = 0; x < width; x++ ) {
2248 DWORD color = (*Source++);
2249 /* B */ Dest[0] = 0xffff;
2250 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
2251 /* R */ Dest[2] = (color ) + 32768; /* U */
2252 Dest += 3;
2255 break;
2258 case CONVERT_Q8W8V8U8:
2260 unsigned int x, y;
2261 const DWORD *Source;
2262 unsigned char *Dest;
2263 for(y = 0; y < height; y++) {
2264 Source = (const DWORD *)(src + y * pitch);
2265 Dest = dst + y * outpitch;
2266 for (x = 0; x < width; x++ ) {
2267 long color = (*Source++);
2268 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
2269 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2270 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2271 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
2272 Dest += 4;
2275 break;
2278 case CONVERT_L6V5U5:
2280 unsigned int x, y;
2281 const WORD *Source;
2282 unsigned char *Dest;
2284 if (gl_info->supported[NV_TEXTURE_SHADER])
2286 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
2287 * fixed function and shaders without further conversion once the surface is
2288 * loaded
2290 for(y = 0; y < height; y++) {
2291 Source = (const WORD *)(src + y * pitch);
2292 Dest = dst + y * outpitch;
2293 for (x = 0; x < width; x++ ) {
2294 short color = (*Source++);
2295 unsigned char l = ((color >> 10) & 0xfc);
2296 char v = ((color >> 5) & 0x3e);
2297 char u = ((color ) & 0x1f);
2299 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
2300 * and doubles the positive range. Thus shift left only once, gl does the 2nd
2301 * shift. GL reads a signed value and converts it into an unsigned value.
2303 /* M */ Dest[2] = l << 1;
2305 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
2306 * from 5 bit values to 8 bit values.
2308 /* V */ Dest[1] = v << 3;
2309 /* U */ Dest[0] = u << 3;
2310 Dest += 3;
2313 } else {
2314 for(y = 0; y < height; y++) {
2315 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
2316 Source = (const WORD *)(src + y * pitch);
2317 for (x = 0; x < width; x++ ) {
2318 short color = (*Source++);
2319 unsigned char l = ((color >> 10) & 0xfc);
2320 short v = ((color >> 5) & 0x3e);
2321 short u = ((color ) & 0x1f);
2322 short v_conv = v + 16;
2323 short u_conv = u + 16;
2325 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
2326 Dest_s += 1;
2330 break;
2333 case CONVERT_X8L8V8U8:
2335 unsigned int x, y;
2336 const DWORD *Source;
2337 unsigned char *Dest;
2339 if (gl_info->supported[NV_TEXTURE_SHADER])
2341 /* This implementation works with the fixed function pipeline and shaders
2342 * without further modification after converting the surface.
2344 for(y = 0; y < height; y++) {
2345 Source = (const DWORD *)(src + y * pitch);
2346 Dest = dst + y * outpitch;
2347 for (x = 0; x < width; x++ ) {
2348 long color = (*Source++);
2349 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
2350 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
2351 /* U */ Dest[0] = (color & 0xff); /* U */
2352 /* I */ Dest[3] = 255; /* X */
2353 Dest += 4;
2356 } else {
2357 /* Doesn't work correctly with the fixed function pipeline, but can work in
2358 * shaders if the shader is adjusted. (There's no use for this format in gl's
2359 * standard fixed function pipeline anyway).
2361 for(y = 0; y < height; y++) {
2362 Source = (const DWORD *)(src + y * pitch);
2363 Dest = dst + y * outpitch;
2364 for (x = 0; x < width; x++ ) {
2365 long color = (*Source++);
2366 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
2367 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2368 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2369 Dest += 4;
2373 break;
2376 case CONVERT_A4L4:
2378 unsigned int x, y;
2379 const unsigned char *Source;
2380 unsigned char *Dest;
2381 for(y = 0; y < height; y++) {
2382 Source = src + y * pitch;
2383 Dest = dst + y * outpitch;
2384 for (x = 0; x < width; x++ ) {
2385 unsigned char color = (*Source++);
2386 /* A */ Dest[1] = (color & 0xf0) << 0;
2387 /* L */ Dest[0] = (color & 0x0f) << 4;
2388 Dest += 2;
2391 break;
2394 case CONVERT_G16R16:
2395 case CONVERT_R16G16F:
2397 unsigned int x, y;
2398 const WORD *Source;
2399 WORD *Dest;
2401 for(y = 0; y < height; y++) {
2402 Source = (const WORD *)(src + y * pitch);
2403 Dest = (WORD *) (dst + y * outpitch);
2404 for (x = 0; x < width; x++ ) {
2405 WORD green = (*Source++);
2406 WORD red = (*Source++);
2407 Dest[0] = green;
2408 Dest[1] = red;
2409 /* Strictly speaking not correct for R16G16F, but it doesn't matter because the
2410 * shader overwrites it anyway
2412 Dest[2] = 0xffff;
2413 Dest += 3;
2416 break;
2419 case CONVERT_R32G32F:
2421 unsigned int x, y;
2422 const float *Source;
2423 float *Dest;
2424 for(y = 0; y < height; y++) {
2425 Source = (const float *)(src + y * pitch);
2426 Dest = (float *) (dst + y * outpitch);
2427 for (x = 0; x < width; x++ ) {
2428 float green = (*Source++);
2429 float red = (*Source++);
2430 Dest[0] = green;
2431 Dest[1] = red;
2432 Dest[2] = 1.0f;
2433 Dest += 3;
2436 break;
2439 case CONVERT_D15S1:
2441 unsigned int x, y;
2443 for (y = 0; y < height; ++y)
2445 const WORD *source = (const WORD *)(src + y * pitch);
2446 DWORD *dest = (DWORD *)(dst + y * outpitch);
2448 for (x = 0; x < width; ++x)
2450 /* The depth data is normalized, so needs to be scaled,
2451 * the stencil data isn't. Scale depth data by
2452 * (2^24-1)/(2^15-1) ~~ (2^9 + 2^-6). */
2453 WORD d15 = source[x] >> 1;
2454 DWORD d24 = (d15 << 9) + (d15 >> 6);
2455 dest[x] = (d24 << 8) | (source[x] & 0x1);
2458 break;
2461 case CONVERT_D24X4S4:
2463 unsigned int x, y;
2465 for (y = 0; y < height; ++y)
2467 const DWORD *source = (const DWORD *)(src + y * pitch);
2468 DWORD *dest = (DWORD *)(dst + y * outpitch);
2470 for (x = 0; x < width; ++x)
2472 /* Just need to clear out the X4 part. */
2473 dest[x] = source[x] & ~0xf0;
2476 break;
2479 case CONVERT_D24FS8:
2481 unsigned int x, y;
2483 for (y = 0; y < height; ++y)
2485 const DWORD *source = (const DWORD *)(src + y * pitch);
2486 float *dest_f = (float *)(dst + y * outpitch);
2487 DWORD *dest_s = (DWORD *)(dst + y * outpitch);
2489 for (x = 0; x < width; ++x)
2491 dest_f[x * 2] = float_24_to_32((source[x] & 0xffffff00) >> 8);
2492 dest_s[x * 2 + 1] = source[x] & 0xff;
2495 break;
2498 default:
2499 ERR("Unsupported conversion type %#x.\n", convert);
2501 return WINED3D_OK;
2504 /* This function is used in case of 8bit paletted textures to upload the palette.
2505 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2506 extensions like ATI_fragment_shaders is possible.
2508 /* Context activation is done by the caller. */
2509 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2510 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2511 BYTE table[256][4];
2512 IWineD3DDeviceImpl *device = This->resource.device;
2513 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
2515 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2517 /* Try to use the paletted texture extension */
2518 if (gl_info->supported[EXT_PALETTED_TEXTURE])
2520 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2521 ENTER_GL();
2522 GL_EXTCALL(glColorTableEXT(This->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
2523 LEAVE_GL();
2525 else
2527 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2528 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2529 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2531 ENTER_GL();
2533 /* Create the fragment program if we don't have it */
2534 if(!device->paletteConversionShader)
2536 const char *fragment_palette_conversion =
2537 "!!ARBfp1.0\n"
2538 "TEMP index;\n"
2539 /* { 255/256, 0.5/255*255/256, 0, 0 } */
2540 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n"
2541 /* The alpha-component contains the palette index */
2542 "TEX index, fragment.texcoord[0], texture[0], 2D;\n"
2543 /* Scale the index by 255/256 and add a bias of '0.5' in order to sample in the middle */
2544 "MAD index.a, index.a, constants.x, constants.y;\n"
2545 /* Use the alpha-component as an index in the palette to get the final color */
2546 "TEX result.color, index.a, texture[1], 1D;\n"
2547 "END";
2549 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2550 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2551 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2552 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), fragment_palette_conversion));
2553 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2556 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2557 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2559 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2560 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2562 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2563 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2564 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2565 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2567 /* Switch back to unit 0 in which the 2D texture will be stored. */
2568 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2570 /* Rebind the texture because it isn't bound anymore */
2571 glBindTexture(This->texture_target, This->texture_name);
2573 LEAVE_GL();
2577 BOOL palette9_changed(IWineD3DSurfaceImpl *This)
2579 IWineD3DDeviceImpl *device = This->resource.device;
2581 if (This->palette || (This->resource.format_desc->format != WINED3DFMT_P8_UINT
2582 && This->resource.format_desc->format != WINED3DFMT_P8_UINT_A8_UNORM))
2584 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2585 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2587 return FALSE;
2590 if (This->palette9)
2592 if (!memcmp(This->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256))
2594 return FALSE;
2596 } else {
2597 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2599 memcpy(This->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2600 return TRUE;
2603 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2604 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2605 DWORD flag = srgb_mode ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2607 if (!(This->Flags & flag)) {
2608 TRACE("Reloading because surface is dirty\n");
2609 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2610 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2611 /* Reload: vice versa OR */
2612 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2613 /* Also reload: Color key is active AND the color key has changed */
2614 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2615 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2616 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2617 TRACE("Reloading because of color keying\n");
2618 /* To perform the color key conversion we need a sysmem copy of
2619 * the surface. Make sure we have it
2622 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2623 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2624 /* TODO: This is not necessarily needed with hw palettized texture support */
2625 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2626 } else {
2627 TRACE("surface is already in texture\n");
2628 return WINED3D_OK;
2631 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2632 * These resources are not bound by device size or format restrictions. Because of this,
2633 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2634 * However, these resources can always be created, locked, and copied.
2636 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2638 FIXME("(%p) Operation not supported for scratch textures\n",This);
2639 return WINED3DERR_INVALIDCALL;
2642 IWineD3DSurface_LoadLocation(iface, flag, NULL /* no partial locking for textures yet */);
2644 #if 0
2646 static unsigned int gen = 0;
2647 char buffer[4096];
2648 ++gen;
2649 if ((gen % 10) == 0) {
2650 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm",
2651 This, This->texture_target, This->texture_level, gen);
2652 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2655 * debugging crash code
2656 if (gen == 250) {
2657 void** test = NULL;
2658 *test = 0;
2662 #endif
2664 if (!(This->Flags & SFLAG_DONOTFREE)) {
2665 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2666 This->resource.allocatedMemory = NULL;
2667 This->resource.heapMemory = NULL;
2668 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2671 return WINED3D_OK;
2674 /* Context activation is done by the caller. */
2675 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface, BOOL srgb) {
2676 /* TODO: check for locks */
2677 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2678 IWineD3DDeviceImpl *device = This->resource.device;
2679 IWineD3DBaseTexture *baseTexture = NULL;
2681 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2682 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2683 TRACE("Passing to container\n");
2684 IWineD3DBaseTexture_BindTexture(baseTexture, srgb);
2685 IWineD3DBaseTexture_Release(baseTexture);
2687 else
2689 struct wined3d_context *context = NULL;
2690 GLuint *name;
2692 TRACE("(%p) : Binding surface\n", This);
2694 name = srgb ? &This->texture_name_srgb : &This->texture_name;
2695 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
2697 ENTER_GL();
2699 if (!This->texture_level)
2701 if (!*name) {
2702 glGenTextures(1, name);
2703 checkGLcall("glGenTextures");
2704 TRACE("Surface %p given name %d\n", This, *name);
2706 glBindTexture(This->texture_target, *name);
2707 checkGLcall("glBindTexture");
2708 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2709 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2710 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2711 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2712 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2713 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2714 glTexParameteri(This->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2715 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2716 glTexParameteri(This->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2717 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2719 /* This is where we should be reducing the amount of GLMemoryUsed */
2720 } else if (*name) {
2721 /* Mipmap surfaces should have a base texture container */
2722 ERR("Mipmap surface has a glTexture bound to it!\n");
2725 glBindTexture(This->texture_target, *name);
2726 checkGLcall("glBindTexture");
2728 LEAVE_GL();
2730 if (context) context_release(context);
2732 return;
2735 #include <errno.h>
2736 #include <stdio.h>
2737 static HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename)
2739 FILE* f = NULL;
2740 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2741 char *allocatedMemory;
2742 const char *textureRow;
2743 IWineD3DSwapChain *swapChain = NULL;
2744 int width, height, i, y;
2745 GLuint tmpTexture = 0;
2746 DWORD color;
2747 /*FIXME:
2748 Textures may not be stored in ->allocatedgMemory and a GlTexture
2749 so we should lock the surface before saving a snapshot, or at least check that
2751 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2752 by calling GetTexImage and in compressed form by calling
2753 GetCompressedTexImageARB. Queried compressed images can be saved and
2754 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2755 texture images do not need to be processed by the GL and should
2756 significantly improve texture loading performance relative to uncompressed
2757 images. */
2759 /* Setup the width and height to be the internal texture width and height. */
2760 width = This->pow2Width;
2761 height = This->pow2Height;
2762 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2763 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2765 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2766 /* if were not a real texture then read the back buffer into a real texture */
2767 /* we don't want to interfere with the back buffer so read the data into a temporary
2768 * texture and then save the data out of the temporary texture
2770 GLint prevRead;
2771 ENTER_GL();
2772 TRACE("(%p) Reading render target into texture\n", This);
2774 glGenTextures(1, &tmpTexture);
2775 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2777 glTexImage2D(GL_TEXTURE_2D,
2779 GL_RGBA,
2780 width,
2781 height,
2782 0/*border*/,
2783 GL_RGBA,
2784 GL_UNSIGNED_INT_8_8_8_8_REV,
2785 NULL);
2787 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2788 checkGLcall("glGetIntegerv");
2789 glReadBuffer(swapChain ? GL_BACK : This->resource.device->offscreenBuffer);
2790 checkGLcall("glReadBuffer");
2791 glCopyTexImage2D(GL_TEXTURE_2D,
2793 GL_RGBA,
2796 width,
2797 height,
2800 checkGLcall("glCopyTexImage2D");
2801 glReadBuffer(prevRead);
2802 LEAVE_GL();
2804 } else { /* bind the real texture, and make sure it up to date */
2805 surface_internal_preload(iface, SRGB_RGB);
2806 surface_bind_and_dirtify(This, FALSE);
2808 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2809 ENTER_GL();
2810 FIXME("Saving texture level %d width %d height %d\n", This->texture_level, width, height);
2811 glGetTexImage(GL_TEXTURE_2D, This->texture_level, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, allocatedMemory);
2812 checkGLcall("glGetTexImage");
2813 if (tmpTexture) {
2814 glBindTexture(GL_TEXTURE_2D, 0);
2815 glDeleteTextures(1, &tmpTexture);
2817 LEAVE_GL();
2819 f = fopen(filename, "w+");
2820 if (NULL == f) {
2821 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2822 return WINED3DERR_INVALIDCALL;
2824 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2825 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format_desc->format));
2826 /* TGA header */
2827 fputc(0,f);
2828 fputc(0,f);
2829 fputc(2,f);
2830 fputc(0,f);
2831 fputc(0,f);
2832 fputc(0,f);
2833 fputc(0,f);
2834 fputc(0,f);
2835 fputc(0,f);
2836 fputc(0,f);
2837 fputc(0,f);
2838 fputc(0,f);
2839 /* short width*/
2840 fwrite(&width,2,1,f);
2841 /* short height */
2842 fwrite(&height,2,1,f);
2843 /* format rgba */
2844 fputc(0x20,f);
2845 fputc(0x28,f);
2846 /* raw data */
2847 /* 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 */
2848 if(swapChain)
2849 textureRow = allocatedMemory + (width * (height - 1) *4);
2850 else
2851 textureRow = allocatedMemory;
2852 for (y = 0 ; y < height; y++) {
2853 for (i = 0; i < width; i++) {
2854 color = *((const DWORD*)textureRow);
2855 fputc((color >> 16) & 0xFF, f); /* B */
2856 fputc((color >> 8) & 0xFF, f); /* G */
2857 fputc((color >> 0) & 0xFF, f); /* R */
2858 fputc((color >> 24) & 0xFF, f); /* A */
2859 textureRow += 4;
2861 /* take two rows of the pointer to the texture memory */
2862 if(swapChain)
2863 (textureRow-= width << 3);
2866 TRACE("Closing file\n");
2867 fclose(f);
2869 if(swapChain) {
2870 IWineD3DSwapChain_Release(swapChain);
2872 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2873 return WINED3D_OK;
2876 static HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2877 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2878 HRESULT hr;
2880 TRACE("(%p) : Calling base function first\n", This);
2881 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2882 if(SUCCEEDED(hr)) {
2883 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2884 TRACE("(%p) : glFormat %d, glFormatInternal %d, glType %d\n", This, This->resource.format_desc->glFormat,
2885 This->resource.format_desc->glInternal, This->resource.format_desc->glType);
2887 return hr;
2890 static HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2891 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2893 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2894 WARN("Surface is locked or the HDC is in use\n");
2895 return WINED3DERR_INVALIDCALL;
2898 if(Mem && Mem != This->resource.allocatedMemory) {
2899 void *release = NULL;
2901 /* Do I have to copy the old surface content? */
2902 if(This->Flags & SFLAG_DIBSECTION) {
2903 /* Release the DC. No need to hold the critical section for the update
2904 * Thread because this thread runs only on front buffers, but this method
2905 * fails for render targets in the check above.
2907 SelectObject(This->hDC, This->dib.holdbitmap);
2908 DeleteDC(This->hDC);
2909 /* Release the DIB section */
2910 DeleteObject(This->dib.DIBsection);
2911 This->dib.bitmap_data = NULL;
2912 This->resource.allocatedMemory = NULL;
2913 This->hDC = NULL;
2914 This->Flags &= ~SFLAG_DIBSECTION;
2915 } else if(!(This->Flags & SFLAG_USERPTR)) {
2916 release = This->resource.heapMemory;
2917 This->resource.heapMemory = NULL;
2919 This->resource.allocatedMemory = Mem;
2920 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2922 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2923 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2925 /* For client textures opengl has to be notified */
2926 if(This->Flags & SFLAG_CLIENT) {
2927 DWORD oldFlags = This->Flags;
2928 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2929 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2930 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2931 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2934 /* Now free the old memory if any */
2935 HeapFree(GetProcessHeap(), 0, release);
2936 } else if(This->Flags & SFLAG_USERPTR) {
2937 /* LockRect and GetDC will re-create the dib section and allocated memory */
2938 This->resource.allocatedMemory = NULL;
2939 /* HeapMemory should be NULL already */
2940 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2941 This->Flags &= ~SFLAG_USERPTR;
2943 if(This->Flags & SFLAG_CLIENT) {
2944 DWORD oldFlags = This->Flags;
2945 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2946 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2947 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2948 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2951 return WINED3D_OK;
2954 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
2956 /* Flip the surface contents */
2957 /* Flip the DC */
2959 HDC tmp;
2960 tmp = front->hDC;
2961 front->hDC = back->hDC;
2962 back->hDC = tmp;
2965 /* Flip the DIBsection */
2967 HBITMAP tmp;
2968 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
2969 tmp = front->dib.DIBsection;
2970 front->dib.DIBsection = back->dib.DIBsection;
2971 back->dib.DIBsection = tmp;
2973 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
2974 else front->Flags &= ~SFLAG_DIBSECTION;
2975 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
2976 else back->Flags &= ~SFLAG_DIBSECTION;
2979 /* Flip the surface data */
2981 void* tmp;
2983 tmp = front->dib.bitmap_data;
2984 front->dib.bitmap_data = back->dib.bitmap_data;
2985 back->dib.bitmap_data = tmp;
2987 tmp = front->resource.allocatedMemory;
2988 front->resource.allocatedMemory = back->resource.allocatedMemory;
2989 back->resource.allocatedMemory = tmp;
2991 tmp = front->resource.heapMemory;
2992 front->resource.heapMemory = back->resource.heapMemory;
2993 back->resource.heapMemory = tmp;
2996 /* Flip the PBO */
2998 GLuint tmp_pbo = front->pbo;
2999 front->pbo = back->pbo;
3000 back->pbo = tmp_pbo;
3003 /* client_memory should not be different, but just in case */
3005 BOOL tmp;
3006 tmp = front->dib.client_memory;
3007 front->dib.client_memory = back->dib.client_memory;
3008 back->dib.client_memory = tmp;
3011 /* Flip the opengl texture */
3013 GLuint tmp;
3015 tmp = back->texture_name;
3016 back->texture_name = front->texture_name;
3017 front->texture_name = tmp;
3019 tmp = back->texture_name_srgb;
3020 back->texture_name_srgb = front->texture_name_srgb;
3021 front->texture_name_srgb = tmp;
3025 DWORD tmp_flags = back->Flags;
3026 back->Flags = front->Flags;
3027 front->Flags = tmp_flags;
3031 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
3032 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3033 IWineD3DSwapChainImpl *swapchain = NULL;
3034 HRESULT hr;
3035 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
3037 /* Flipping is only supported on RenderTargets and overlays*/
3038 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
3039 WARN("Tried to flip a non-render target, non-overlay surface\n");
3040 return WINEDDERR_NOTFLIPPABLE;
3043 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
3044 flip_surface(This, (IWineD3DSurfaceImpl *) override);
3046 /* Update the overlay if it is visible */
3047 if(This->overlay_dest) {
3048 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
3049 } else {
3050 return WINED3D_OK;
3054 if(override) {
3055 /* DDraw sets this for the X11 surfaces, so don't confuse the user
3056 * FIXME("(%p) Target override is not supported by now\n", This);
3057 * Additionally, it isn't really possible to support triple-buffering
3058 * properly on opengl at all
3062 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
3063 if(!swapchain) {
3064 ERR("Flipped surface is not on a swapchain\n");
3065 return WINEDDERR_NOTFLIPPABLE;
3068 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
3069 * and only d3d8 and d3d9 apps specify the presentation interval
3071 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
3072 /* Most common case first to avoid wasting time on all the other cases */
3073 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
3074 } else if(Flags & WINEDDFLIP_NOVSYNC) {
3075 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3076 } else if(Flags & WINEDDFLIP_INTERVAL2) {
3077 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
3078 } else if(Flags & WINEDDFLIP_INTERVAL3) {
3079 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
3080 } else {
3081 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
3084 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
3085 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
3086 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
3087 return hr;
3090 /* Does a direct frame buffer -> texture copy. Stretching is done
3091 * with single pixel copy calls
3093 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3094 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
3095 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
3097 IWineD3DDeviceImpl *myDevice = This->resource.device;
3098 float xrel, yrel;
3099 UINT row;
3100 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3101 struct wined3d_context *context;
3104 context = context_acquire(myDevice, SrcSurface, CTXUSAGE_BLIT);
3105 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3106 ENTER_GL();
3108 /* Bind the target texture */
3109 glBindTexture(This->texture_target, This->texture_name);
3110 checkGLcall("glBindTexture");
3111 if(surface_is_offscreen(SrcSurface)) {
3112 TRACE("Reading from an offscreen target\n");
3113 upsidedown = !upsidedown;
3114 glReadBuffer(myDevice->offscreenBuffer);
3115 } else {
3116 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
3117 glReadBuffer(buffer);
3119 checkGLcall("glReadBuffer");
3121 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
3122 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
3124 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3126 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3128 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
3129 ERR("Texture filtering not supported in direct blit\n");
3132 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
3133 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3135 ERR("Texture filtering not supported in direct blit\n");
3138 if (upsidedown
3139 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3140 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3142 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
3144 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3145 drect->x1 /*xoffset */, drect->y1 /* y offset */,
3146 srect->x1, Src->currentDesc.Height - srect->y2,
3147 drect->x2 - drect->x1, drect->y2 - drect->y1);
3148 } else {
3149 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
3150 /* I have to process this row by row to swap the image,
3151 * otherwise it would be upside down, so stretching in y direction
3152 * doesn't cost extra time
3154 * However, stretching in x direction can be avoided if not necessary
3156 for(row = drect->y1; row < drect->y2; row++) {
3157 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3159 /* Well, that stuff works, but it's very slow.
3160 * find a better way instead
3162 UINT col;
3164 for(col = drect->x1; col < drect->x2; col++) {
3165 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3166 drect->x1 + col /* x offset */, row /* y offset */,
3167 srect->x1 + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3169 } else {
3170 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3171 drect->x1 /* x offset */, row /* y offset */,
3172 srect->x1, yoffset - (int) (row * yrel), drect->x2-drect->x1, 1);
3176 checkGLcall("glCopyTexSubImage2D");
3178 LEAVE_GL();
3179 context_release(context);
3181 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3182 * path is never entered
3184 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3187 /* Uses the hardware to stretch and flip the image */
3188 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3189 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
3190 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
3192 IWineD3DDeviceImpl *myDevice = This->resource.device;
3193 GLuint src, backup = 0;
3194 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3195 float left, right, top, bottom; /* Texture coordinates */
3196 UINT fbwidth = Src->currentDesc.Width;
3197 UINT fbheight = Src->currentDesc.Height;
3198 struct wined3d_context *context;
3199 GLenum drawBuffer = GL_BACK;
3200 GLenum texture_target;
3201 BOOL noBackBufferBackup;
3203 TRACE("Using hwstretch blit\n");
3204 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3205 context = context_acquire(myDevice, SrcSurface, CTXUSAGE_BLIT);
3206 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3208 noBackBufferBackup = !swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3209 if (!noBackBufferBackup && !Src->texture_name)
3211 /* Get it a description */
3212 surface_internal_preload(SrcSurface, SRGB_RGB);
3214 ENTER_GL();
3216 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3217 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3219 if (context->aux_buffers >= 2)
3221 /* Got more than one aux buffer? Use the 2nd aux buffer */
3222 drawBuffer = GL_AUX1;
3224 else if ((swapchain || myDevice->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3226 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3227 drawBuffer = GL_AUX0;
3230 if(noBackBufferBackup) {
3231 glGenTextures(1, &backup);
3232 checkGLcall("glGenTextures");
3233 glBindTexture(GL_TEXTURE_2D, backup);
3234 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3235 texture_target = GL_TEXTURE_2D;
3236 } else {
3237 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3238 * we are reading from the back buffer, the backup can be used as source texture
3240 texture_target = Src->texture_target;
3241 glBindTexture(texture_target, Src->texture_name);
3242 checkGLcall("glBindTexture(texture_target, Src->texture_name)");
3243 glEnable(texture_target);
3244 checkGLcall("glEnable(texture_target)");
3246 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3247 Src->Flags &= ~SFLAG_INTEXTURE;
3250 if(surface_is_offscreen(SrcSurface)) {
3251 TRACE("Reading from an offscreen target\n");
3252 upsidedown = !upsidedown;
3253 glReadBuffer(myDevice->offscreenBuffer);
3254 } else {
3255 glReadBuffer(surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain));
3258 /* TODO: Only back up the part that will be overwritten */
3259 glCopyTexSubImage2D(texture_target, 0,
3260 0, 0 /* read offsets */,
3261 0, 0,
3262 fbwidth,
3263 fbheight);
3265 checkGLcall("glCopyTexSubImage2D");
3267 /* No issue with overriding these - the sampler is dirty due to blit usage */
3268 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3269 wined3d_gl_mag_filter(magLookup, Filter));
3270 checkGLcall("glTexParameteri");
3271 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3272 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
3273 checkGLcall("glTexParameteri");
3275 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
3276 src = backup ? backup : Src->texture_name;
3277 } else {
3278 glReadBuffer(GL_FRONT);
3279 checkGLcall("glReadBuffer(GL_FRONT)");
3281 glGenTextures(1, &src);
3282 checkGLcall("glGenTextures(1, &src)");
3283 glBindTexture(GL_TEXTURE_2D, src);
3284 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
3286 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
3287 * out for power of 2 sizes
3289 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
3290 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3291 checkGLcall("glTexImage2D");
3292 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
3293 0, 0 /* read offsets */,
3294 0, 0,
3295 fbwidth,
3296 fbheight);
3298 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3299 checkGLcall("glTexParameteri");
3300 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3301 checkGLcall("glTexParameteri");
3303 glReadBuffer(GL_BACK);
3304 checkGLcall("glReadBuffer(GL_BACK)");
3306 if(texture_target != GL_TEXTURE_2D) {
3307 glDisable(texture_target);
3308 glEnable(GL_TEXTURE_2D);
3309 texture_target = GL_TEXTURE_2D;
3312 checkGLcall("glEnd and previous");
3314 left = srect->x1;
3315 right = srect->x2;
3317 if(upsidedown) {
3318 top = Src->currentDesc.Height - srect->y1;
3319 bottom = Src->currentDesc.Height - srect->y2;
3320 } else {
3321 top = Src->currentDesc.Height - srect->y2;
3322 bottom = Src->currentDesc.Height - srect->y1;
3325 if(Src->Flags & SFLAG_NORMCOORD) {
3326 left /= Src->pow2Width;
3327 right /= Src->pow2Width;
3328 top /= Src->pow2Height;
3329 bottom /= Src->pow2Height;
3332 /* draw the source texture stretched and upside down. The correct surface is bound already */
3333 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3334 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3336 glDrawBuffer(drawBuffer);
3337 glReadBuffer(drawBuffer);
3339 glBegin(GL_QUADS);
3340 /* bottom left */
3341 glTexCoord2f(left, bottom);
3342 glVertex2i(0, fbheight);
3344 /* top left */
3345 glTexCoord2f(left, top);
3346 glVertex2i(0, fbheight - drect->y2 - drect->y1);
3348 /* top right */
3349 glTexCoord2f(right, top);
3350 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
3352 /* bottom right */
3353 glTexCoord2f(right, bottom);
3354 glVertex2i(drect->x2 - drect->x1, fbheight);
3355 glEnd();
3356 checkGLcall("glEnd and previous");
3358 if (texture_target != This->texture_target)
3360 glDisable(texture_target);
3361 glEnable(This->texture_target);
3362 texture_target = This->texture_target;
3365 /* Now read the stretched and upside down image into the destination texture */
3366 glBindTexture(texture_target, This->texture_name);
3367 checkGLcall("glBindTexture");
3368 glCopyTexSubImage2D(texture_target,
3370 drect->x1, drect->y1, /* xoffset, yoffset */
3371 0, 0, /* We blitted the image to the origin */
3372 drect->x2 - drect->x1, drect->y2 - drect->y1);
3373 checkGLcall("glCopyTexSubImage2D");
3375 if(drawBuffer == GL_BACK) {
3376 /* Write the back buffer backup back */
3377 if(backup) {
3378 if(texture_target != GL_TEXTURE_2D) {
3379 glDisable(texture_target);
3380 glEnable(GL_TEXTURE_2D);
3381 texture_target = GL_TEXTURE_2D;
3383 glBindTexture(GL_TEXTURE_2D, backup);
3384 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3385 } else {
3386 if (texture_target != Src->texture_target)
3388 glDisable(texture_target);
3389 glEnable(Src->texture_target);
3390 texture_target = Src->texture_target;
3392 glBindTexture(Src->texture_target, Src->texture_name);
3393 checkGLcall("glBindTexture(Src->texture_target, Src->texture_name)");
3396 glBegin(GL_QUADS);
3397 /* top left */
3398 glTexCoord2f(0.0f, (float)fbheight / (float)Src->pow2Height);
3399 glVertex2i(0, 0);
3401 /* bottom left */
3402 glTexCoord2f(0.0f, 0.0f);
3403 glVertex2i(0, fbheight);
3405 /* bottom right */
3406 glTexCoord2f((float)fbwidth / (float)Src->pow2Width, 0.0f);
3407 glVertex2i(fbwidth, Src->currentDesc.Height);
3409 /* top right */
3410 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
3411 glVertex2i(fbwidth, 0);
3412 glEnd();
3413 } else {
3414 /* Restore the old draw buffer */
3415 glDrawBuffer(GL_BACK);
3417 glDisable(texture_target);
3418 checkGLcall("glDisable(texture_target)");
3420 /* Cleanup */
3421 if (src != Src->texture_name && src != backup)
3423 glDeleteTextures(1, &src);
3424 checkGLcall("glDeleteTextures(1, &src)");
3426 if(backup) {
3427 glDeleteTextures(1, &backup);
3428 checkGLcall("glDeleteTextures(1, &backup)");
3431 LEAVE_GL();
3432 context_release(context);
3434 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3435 * path is never entered
3437 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3440 /* Not called from the VTable */
3441 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3442 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx,
3443 WINED3DTEXTUREFILTERTYPE Filter)
3445 IWineD3DDeviceImpl *myDevice = This->resource.device;
3446 WINED3DRECT rect;
3447 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3448 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3450 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3452 /* Get the swapchain. One of the surfaces has to be a primary surface */
3453 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3454 WARN("Destination is in sysmem, rejecting gl blt\n");
3455 return WINED3DERR_INVALIDCALL;
3457 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3458 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3459 if(Src) {
3460 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3461 WARN("Src is in sysmem, rejecting gl blt\n");
3462 return WINED3DERR_INVALIDCALL;
3464 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3465 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3468 /* Early sort out of cases where no render target is used */
3469 if(!dstSwapchain && !srcSwapchain &&
3470 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3471 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3472 return WINED3DERR_INVALIDCALL;
3475 /* No destination color keying supported */
3476 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3477 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3478 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3479 return WINED3DERR_INVALIDCALL;
3482 if (DestRect) {
3483 rect.x1 = DestRect->left;
3484 rect.y1 = DestRect->top;
3485 rect.x2 = DestRect->right;
3486 rect.y2 = DestRect->bottom;
3487 } else {
3488 rect.x1 = 0;
3489 rect.y1 = 0;
3490 rect.x2 = This->currentDesc.Width;
3491 rect.y2 = This->currentDesc.Height;
3494 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3495 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3496 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3497 /* Half-life does a Blt from the back buffer to the front buffer,
3498 * Full surface size, no flags... Use present instead
3500 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3503 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3504 while(1)
3506 RECT mySrcRect;
3507 TRACE("Looking if a Present can be done...\n");
3508 /* Source Rectangle must be full surface */
3509 if( SrcRect ) {
3510 if(SrcRect->left != 0 || SrcRect->top != 0 ||
3511 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
3512 TRACE("No, Source rectangle doesn't match\n");
3513 break;
3516 mySrcRect.left = 0;
3517 mySrcRect.top = 0;
3518 mySrcRect.right = Src->currentDesc.Width;
3519 mySrcRect.bottom = Src->currentDesc.Height;
3521 /* No stretching may occur */
3522 if(mySrcRect.right != rect.x2 - rect.x1 ||
3523 mySrcRect.bottom != rect.y2 - rect.y1) {
3524 TRACE("No, stretching is done\n");
3525 break;
3528 /* Destination must be full surface or match the clipping rectangle */
3529 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3531 RECT cliprect;
3532 POINT pos[2];
3533 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3534 pos[0].x = rect.x1;
3535 pos[0].y = rect.y1;
3536 pos[1].x = rect.x2;
3537 pos[1].y = rect.y2;
3538 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3539 pos, 2);
3541 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3542 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3544 TRACE("No, dest rectangle doesn't match(clipper)\n");
3545 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3546 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3547 break;
3550 else
3552 if(rect.x1 != 0 || rect.y1 != 0 ||
3553 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3554 TRACE("No, dest rectangle doesn't match(surface size)\n");
3555 break;
3559 TRACE("Yes\n");
3561 /* These flags are unimportant for the flag check, remove them */
3562 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3563 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3565 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3566 * take very long, while a flip is fast.
3567 * This applies to Half-Life, which does such Blts every time it finished
3568 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3569 * menu. This is also used by all apps when they do windowed rendering
3571 * The problem is that flipping is not really the same as copying. After a
3572 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3573 * untouched. Therefore it's necessary to override the swap effect
3574 * and to set it back after the flip.
3576 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3577 * testcases.
3580 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3581 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3583 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3584 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3586 dstSwapchain->presentParms.SwapEffect = orig_swap;
3588 return WINED3D_OK;
3590 break;
3593 TRACE("Unsupported blit between buffers on the same swapchain\n");
3594 return WINED3DERR_INVALIDCALL;
3595 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3596 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3597 return WINED3DERR_INVALIDCALL;
3598 } else if(dstSwapchain && srcSwapchain) {
3599 FIXME("Implement hardware blit between two different swapchains\n");
3600 return WINED3DERR_INVALIDCALL;
3601 } else if(dstSwapchain) {
3602 if(SrcSurface == myDevice->render_targets[0]) {
3603 TRACE("Blit from active render target to a swapchain\n");
3604 /* Handled with regular texture -> swapchain blit */
3606 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3607 FIXME("Implement blit from a swapchain to the active render target\n");
3608 return WINED3DERR_INVALIDCALL;
3611 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3612 /* Blit from render target to texture */
3613 WINED3DRECT srect;
3614 BOOL upsideDown, stretchx;
3615 BOOL paletteOverride = FALSE;
3617 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3618 TRACE("Color keying not supported by frame buffer to texture blit\n");
3619 return WINED3DERR_INVALIDCALL;
3620 /* Destination color key is checked above */
3623 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3624 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3626 if(SrcRect) {
3627 if(SrcRect->top < SrcRect->bottom) {
3628 srect.y1 = SrcRect->top;
3629 srect.y2 = SrcRect->bottom;
3630 upsideDown = FALSE;
3631 } else {
3632 srect.y1 = SrcRect->bottom;
3633 srect.y2 = SrcRect->top;
3634 upsideDown = TRUE;
3636 srect.x1 = SrcRect->left;
3637 srect.x2 = SrcRect->right;
3638 } else {
3639 srect.x1 = 0;
3640 srect.y1 = 0;
3641 srect.x2 = Src->currentDesc.Width;
3642 srect.y2 = Src->currentDesc.Height;
3643 upsideDown = FALSE;
3645 if(rect.x1 > rect.x2) {
3646 UINT tmp = rect.x2;
3647 rect.x2 = rect.x1;
3648 rect.x1 = tmp;
3649 upsideDown = !upsideDown;
3652 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3653 stretchx = TRUE;
3654 } else {
3655 stretchx = FALSE;
3658 /* When blitting from a render target a texture, the texture isn't required to have a palette.
3659 * In this case grab the palette from the render target. */
3660 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT && !This->palette)
3662 paletteOverride = TRUE;
3663 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3664 This->palette = Src->palette;
3667 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3668 * flip the image nor scale it.
3670 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3671 * -> If the app wants a image width an unscaled width, copy it line per line
3672 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3673 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3674 * back buffer. This is slower than reading line per line, thus not used for flipping
3675 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3676 * pixel by pixel
3678 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3679 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3680 * backends.
3682 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
3683 && myDevice->adapter->gl_info.fbo_ops.glBlitFramebuffer
3684 && surface_can_stretch_rect(Src, This))
3686 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3687 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3688 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3689 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3690 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3691 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3692 } else {
3693 TRACE("Using hardware stretching to flip / stretch the texture\n");
3694 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3697 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3698 if(paletteOverride)
3699 This->palette = NULL;
3701 if(!(This->Flags & SFLAG_DONOTFREE)) {
3702 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3703 This->resource.allocatedMemory = NULL;
3704 This->resource.heapMemory = NULL;
3705 } else {
3706 This->Flags &= ~SFLAG_INSYSMEM;
3709 return WINED3D_OK;
3710 } else if(Src) {
3711 /* Blit from offscreen surface to render target */
3712 float glTexCoord[4];
3713 DWORD oldCKeyFlags = Src->CKeyFlags;
3714 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3715 struct wined3d_context *context;
3716 RECT SourceRectangle;
3717 BOOL paletteOverride = FALSE;
3719 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3721 if(SrcRect) {
3722 SourceRectangle.left = SrcRect->left;
3723 SourceRectangle.right = SrcRect->right;
3724 SourceRectangle.top = SrcRect->top;
3725 SourceRectangle.bottom = SrcRect->bottom;
3726 } else {
3727 SourceRectangle.left = 0;
3728 SourceRectangle.right = Src->currentDesc.Width;
3729 SourceRectangle.top = 0;
3730 SourceRectangle.bottom = Src->currentDesc.Height;
3733 /* When blitting from an offscreen surface to a rendertarget, the source
3734 * surface is not required to have a palette. Our rendering / conversion
3735 * code further down the road retrieves the palette from the surface, so
3736 * it must have a palette set. */
3737 if (Src->resource.format_desc->format == WINED3DFMT_P8_UINT && !Src->palette)
3739 paletteOverride = TRUE;
3740 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3741 Src->palette = This->palette;
3744 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
3745 && myDevice->adapter->gl_info.fbo_ops.glBlitFramebuffer
3746 && !(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3747 && surface_can_stretch_rect(Src, This))
3749 TRACE("Using stretch_rect_fbo\n");
3750 /* The source is always a texture, but never the currently active render target, and the texture
3751 * contents are never upside down
3753 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3754 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3756 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3757 if(paletteOverride)
3758 Src->palette = NULL;
3759 return WINED3D_OK;
3762 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3763 /* Fall back to software */
3764 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3765 SourceRectangle.left, SourceRectangle.top,
3766 SourceRectangle.right, SourceRectangle.bottom);
3767 return WINED3DERR_INVALIDCALL;
3770 /* Color keying: Check if we have to do a color keyed blt,
3771 * and if not check if a color key is activated.
3773 * Just modify the color keying parameters in the surface and restore them afterwards
3774 * The surface keeps track of the color key last used to load the opengl surface.
3775 * PreLoad will catch the change to the flags and color key and reload if necessary.
3777 if(Flags & WINEDDBLT_KEYSRC) {
3778 /* Use color key from surface */
3779 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3780 /* Use color key from DDBltFx */
3781 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3782 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3783 } else {
3784 /* Do not use color key */
3785 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3788 /* Now load the surface */
3789 surface_internal_preload((IWineD3DSurface *) Src, SRGB_RGB);
3791 /* Activate the destination context, set it up for blitting */
3792 context = context_acquire(myDevice, (IWineD3DSurface *)This, CTXUSAGE_BLIT);
3794 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3795 * while OpenGL coordinates are window relative.
3796 * Also beware of the origin difference(top left vs bottom left).
3797 * Also beware that the front buffer's surface size is screen width x screen height,
3798 * whereas the real gl drawable size is the size of the window.
3800 if (dstSwapchain && (IWineD3DSurface *)This == dstSwapchain->frontBuffer) {
3801 RECT windowsize;
3802 POINT offset = {0,0};
3803 UINT h;
3804 ClientToScreen(dstSwapchain->win_handle, &offset);
3805 GetClientRect(dstSwapchain->win_handle, &windowsize);
3806 h = windowsize.bottom - windowsize.top;
3807 rect.x1 -= offset.x; rect.x2 -=offset.x;
3808 rect.y1 -= offset.y; rect.y2 -=offset.y;
3809 rect.y1 += This->currentDesc.Height - h; rect.y2 += This->currentDesc.Height - h;
3812 if (!is_identity_fixup(This->resource.format_desc->color_fixup))
3814 FIXME("Destination format %s has a fixup, this is not supported.\n",
3815 debug_d3dformat(This->resource.format_desc->format));
3816 dump_color_fixup_desc(This->resource.format_desc->color_fixup);
3819 if (!myDevice->blitter->color_fixup_supported(Src->resource.format_desc->color_fixup))
3821 FIXME("Source format %s has an unsupported fixup:\n",
3822 debug_d3dformat(Src->resource.format_desc->format));
3823 dump_color_fixup_desc(Src->resource.format_desc->color_fixup);
3826 myDevice->blitter->set_shader((IWineD3DDevice *) myDevice, Src->resource.format_desc,
3827 Src->texture_target, Src->pow2Width, Src->pow2Height);
3829 ENTER_GL();
3831 /* Bind the texture */
3832 glBindTexture(Src->texture_target, Src->texture_name);
3833 checkGLcall("glBindTexture");
3835 /* Filtering for StretchRect */
3836 glTexParameteri(Src->texture_target, GL_TEXTURE_MAG_FILTER,
3837 wined3d_gl_mag_filter(magLookup, Filter));
3838 checkGLcall("glTexParameteri");
3839 glTexParameteri(Src->texture_target, GL_TEXTURE_MIN_FILTER,
3840 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
3841 checkGLcall("glTexParameteri");
3842 glTexParameteri(Src->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3843 glTexParameteri(Src->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3844 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3845 checkGLcall("glTexEnvi");
3847 /* This is for color keying */
3848 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3849 glEnable(GL_ALPHA_TEST);
3850 checkGLcall("glEnable(GL_ALPHA_TEST)");
3852 /* When the primary render target uses P8, the alpha component contains the palette index.
3853 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3854 * should be masked away have alpha set to 0. */
3855 if(primary_render_target_is_p8(myDevice))
3856 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
3857 else
3858 glAlphaFunc(GL_NOTEQUAL, 0.0f);
3859 checkGLcall("glAlphaFunc");
3860 } else {
3861 glDisable(GL_ALPHA_TEST);
3862 checkGLcall("glDisable(GL_ALPHA_TEST)");
3865 /* Draw a textured quad
3867 glBegin(GL_QUADS);
3869 glColor3f(1.0f, 1.0f, 1.0f);
3870 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3871 glVertex3f(rect.x1, rect.y1, 0.0f);
3873 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3874 glVertex3f(rect.x1, rect.y2, 0.0f);
3876 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3877 glVertex3f(rect.x2, rect.y2, 0.0f);
3879 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3880 glVertex3f(rect.x2, rect.y1, 0.0f);
3882 glEnd();
3883 checkGLcall("glEnd");
3885 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3886 glDisable(GL_ALPHA_TEST);
3887 checkGLcall("glDisable(GL_ALPHA_TEST)");
3890 glBindTexture(Src->texture_target, 0);
3891 checkGLcall("glBindTexture(Src->texture_target, 0)");
3893 /* Restore the color key parameters */
3894 Src->CKeyFlags = oldCKeyFlags;
3895 Src->SrcBltCKey = oldBltCKey;
3897 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3898 if(paletteOverride)
3899 Src->palette = NULL;
3901 LEAVE_GL();
3903 /* Leave the opengl state valid for blitting */
3904 myDevice->blitter->unset_shader((IWineD3DDevice *) myDevice);
3906 /* Flush in case the drawable is used by multiple GL contexts */
3907 if(dstSwapchain && (This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer || dstSwapchain->num_contexts >= 2))
3908 wglFlush();
3910 context_release(context);
3912 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3913 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3914 * is outdated now
3916 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3918 return WINED3D_OK;
3919 } else {
3920 /* Source-Less Blit to render target */
3921 if (Flags & WINEDDBLT_COLORFILL) {
3922 /* This is easy to handle for the D3D Device... */
3923 DWORD color;
3925 TRACE("Colorfill\n");
3927 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3928 must be true if we are here */
3929 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3930 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3931 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3932 TRACE("Surface is higher back buffer, falling back to software\n");
3933 return WINED3DERR_INVALIDCALL;
3936 /* The color as given in the Blt function is in the format of the frame-buffer...
3937 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3939 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT)
3941 DWORD alpha;
3943 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3944 else alpha = 0xFF000000;
3946 if (This->palette) {
3947 color = (alpha |
3948 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3949 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3950 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3951 } else {
3952 color = alpha;
3955 else if (This->resource.format_desc->format == WINED3DFMT_B5G6R5_UNORM)
3957 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3958 color = 0xFFFFFFFF;
3959 } else {
3960 color = ((0xFF000000) |
3961 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3962 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3963 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3966 else if (This->resource.format_desc->format == WINED3DFMT_B8G8R8_UNORM
3967 || This->resource.format_desc->format == WINED3DFMT_B8G8R8X8_UNORM)
3969 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3971 else if (This->resource.format_desc->format == WINED3DFMT_B8G8R8A8_UNORM)
3973 color = DDBltFx->u5.dwFillColor;
3975 else {
3976 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3977 return WINED3DERR_INVALIDCALL;
3980 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3981 IWineD3DDeviceImpl_ClearSurface(myDevice, This, 1 /* Number of rectangles */,
3982 &rect, WINED3DCLEAR_TARGET, color, 0.0f /* Z */, 0 /* Stencil */);
3983 return WINED3D_OK;
3987 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3988 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3989 return WINED3DERR_INVALIDCALL;
3992 static HRESULT IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3993 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx)
3995 IWineD3DDeviceImpl *myDevice = This->resource.device;
3996 float depth;
3998 if (Flags & WINEDDBLT_DEPTHFILL) {
3999 switch(This->resource.format_desc->format)
4001 case WINED3DFMT_D16_UNORM:
4002 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
4003 break;
4004 case WINED3DFMT_S1_UINT_D15_UNORM:
4005 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
4006 break;
4007 case WINED3DFMT_D24_UNORM_S8_UINT:
4008 case WINED3DFMT_X8D24_UNORM:
4009 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
4010 break;
4011 case WINED3DFMT_D32_UNORM:
4012 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
4013 break;
4014 default:
4015 depth = 0.0f;
4016 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format_desc->format));
4019 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
4020 DestRect == NULL ? 0 : 1,
4021 (const WINED3DRECT *)DestRect,
4022 WINED3DCLEAR_ZBUFFER,
4023 0x00000000,
4024 depth,
4025 0x00000000);
4028 FIXME("(%p): Unsupp depthstencil blit\n", This);
4029 return WINED3DERR_INVALIDCALL;
4032 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
4033 const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
4034 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4035 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
4036 IWineD3DDeviceImpl *myDevice = This->resource.device;
4038 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
4039 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
4041 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
4043 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
4044 return WINEDDERR_SURFACEBUSY;
4047 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
4048 * except depth blits, which seem to work
4050 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
4051 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
4052 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
4053 return WINED3DERR_INVALIDCALL;
4054 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
4055 TRACE("Z Blit override handled the blit\n");
4056 return WINED3D_OK;
4060 /* Special cases for RenderTargets */
4061 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
4062 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
4063 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
4066 /* For the rest call the X11 surface implementation.
4067 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
4068 * other Blts are rather rare
4070 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
4073 static HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
4074 IWineD3DSurface *Source, const RECT *rsrc, DWORD trans)
4076 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4077 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
4078 IWineD3DDeviceImpl *myDevice = This->resource.device;
4080 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
4082 if ( (This->Flags & SFLAG_LOCKED) || (srcImpl->Flags & SFLAG_LOCKED))
4084 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
4085 return WINEDDERR_SURFACEBUSY;
4088 if(myDevice->inScene &&
4089 (iface == myDevice->stencilBufferTarget ||
4090 (Source == myDevice->stencilBufferTarget))) {
4091 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
4092 return WINED3DERR_INVALIDCALL;
4095 /* Special cases for RenderTargets */
4096 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
4097 (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) ) {
4099 RECT SrcRect, DstRect;
4100 DWORD Flags=0;
4102 if(rsrc) {
4103 SrcRect.left = rsrc->left;
4104 SrcRect.top= rsrc->top;
4105 SrcRect.bottom = rsrc->bottom;
4106 SrcRect.right = rsrc->right;
4107 } else {
4108 SrcRect.left = 0;
4109 SrcRect.top = 0;
4110 SrcRect.right = srcImpl->currentDesc.Width;
4111 SrcRect.bottom = srcImpl->currentDesc.Height;
4114 DstRect.left = dstx;
4115 DstRect.top=dsty;
4116 DstRect.right = dstx + SrcRect.right - SrcRect.left;
4117 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
4119 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
4120 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
4121 Flags |= WINEDDBLT_KEYSRC;
4122 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
4123 Flags |= WINEDDBLT_KEYDEST;
4124 if(trans & WINEDDBLTFAST_WAIT)
4125 Flags |= WINEDDBLT_WAIT;
4126 if(trans & WINEDDBLTFAST_DONOTWAIT)
4127 Flags |= WINEDDBLT_DONOTWAIT;
4129 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
4133 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
4136 static HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface)
4138 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4139 RGBQUAD col[256];
4140 IWineD3DPaletteImpl *pal = This->palette;
4141 unsigned int n;
4142 TRACE("(%p)\n", This);
4144 if (!pal) return WINED3D_OK;
4146 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
4147 || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
4149 int bpp;
4150 GLenum format, internal, type;
4151 CONVERT_TYPES convert;
4153 /* Check if we are using a RTL mode which uses texturing for uploads */
4154 BOOL use_texture = (wined3d_settings.rendertargetlock_mode == RTL_READTEX);
4156 /* Check if we have hardware palette conversion if we have convert is set to NO_CONVERSION */
4157 d3dfmt_get_conv(This, TRUE, use_texture, &format, &internal, &type, &convert, &bpp, FALSE);
4159 if((This->resource.usage & WINED3DUSAGE_RENDERTARGET) && (convert == NO_CONVERSION))
4161 IWineD3DDeviceImpl *device = This->resource.device;
4162 struct wined3d_context *context;
4164 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
4165 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
4167 /* We want to force a palette refresh, so mark the drawable as not being up to date */
4168 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
4170 /* Re-upload the palette */
4171 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4172 d3dfmt_p8_upload_palette(iface, convert);
4173 context_release(context);
4174 } else {
4175 if(!(This->Flags & SFLAG_INSYSMEM)) {
4176 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
4177 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
4179 TRACE("Dirtifying surface\n");
4180 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
4184 if(This->Flags & SFLAG_DIBSECTION) {
4185 TRACE("(%p): Updating the hdc's palette\n", This);
4186 for (n=0; n<256; n++) {
4187 col[n].rgbRed = pal->palents[n].peRed;
4188 col[n].rgbGreen = pal->palents[n].peGreen;
4189 col[n].rgbBlue = pal->palents[n].peBlue;
4190 col[n].rgbReserved = 0;
4192 SetDIBColorTable(This->hDC, 0, 256, col);
4195 /* Propagate the changes to the drawable when we have a palette. */
4196 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
4197 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
4199 return WINED3D_OK;
4202 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
4203 /** Check against the maximum texture sizes supported by the video card **/
4204 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4205 const struct wined3d_gl_info *gl_info = &This->resource.device->adapter->gl_info;
4206 unsigned int pow2Width, pow2Height;
4208 This->texture_name = 0;
4209 This->texture_target = GL_TEXTURE_2D;
4211 /* Non-power2 support */
4212 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINE_NORMALIZED_TEXRECT])
4214 pow2Width = This->currentDesc.Width;
4215 pow2Height = This->currentDesc.Height;
4217 else
4219 /* Find the nearest pow2 match */
4220 pow2Width = pow2Height = 1;
4221 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
4222 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
4224 This->pow2Width = pow2Width;
4225 This->pow2Height = pow2Height;
4227 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
4228 /** TODO: add support for non power two compressed textures **/
4229 if (This->resource.format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
4231 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
4232 This, This->currentDesc.Width, This->currentDesc.Height);
4233 return WINED3DERR_NOTAVAILABLE;
4237 if(pow2Width != This->currentDesc.Width ||
4238 pow2Height != This->currentDesc.Height) {
4239 This->Flags |= SFLAG_NONPOW2;
4242 TRACE("%p\n", This);
4243 if ((This->pow2Width > gl_info->limits.texture_size || This->pow2Height > gl_info->limits.texture_size)
4244 && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
4246 /* one of three options
4247 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)
4248 2: Set the texture to the maximum size (bad idea)
4249 3: WARN and return WINED3DERR_NOTAVAILABLE;
4250 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.
4252 WARN("(%p) Creating an oversized surface: %ux%u (texture is %ux%u)\n",
4253 This, This->pow2Width, This->pow2Height, This->currentDesc.Width, This->currentDesc.Height);
4254 This->Flags |= SFLAG_OVERSIZE;
4256 /* This will be initialized on the first blt */
4257 This->glRect.left = 0;
4258 This->glRect.top = 0;
4259 This->glRect.right = 0;
4260 This->glRect.bottom = 0;
4261 } else {
4262 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
4263 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
4264 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
4265 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
4267 if (This->Flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
4268 && !(This->resource.format_desc->format == WINED3DFMT_P8_UINT
4269 && gl_info->supported[EXT_PALETTED_TEXTURE]
4270 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
4272 This->texture_target = GL_TEXTURE_RECTANGLE_ARB;
4273 This->pow2Width = This->currentDesc.Width;
4274 This->pow2Height = This->currentDesc.Height;
4275 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
4278 /* No oversize, gl rect is the full texture size */
4279 This->Flags &= ~SFLAG_OVERSIZE;
4280 This->glRect.left = 0;
4281 This->glRect.top = 0;
4282 This->glRect.right = This->pow2Width;
4283 This->glRect.bottom = This->pow2Height;
4286 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4287 switch(wined3d_settings.offscreen_rendering_mode) {
4288 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4289 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4290 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4294 This->Flags |= SFLAG_INSYSMEM;
4296 return WINED3D_OK;
4299 struct depth_blt_info
4301 GLenum binding;
4302 GLenum bind_target;
4303 enum tex_types tex_type;
4304 GLfloat coords[4][3];
4307 static void surface_get_depth_blt_info(GLenum target, GLsizei w, GLsizei h, struct depth_blt_info *info)
4309 GLfloat (*coords)[3] = info->coords;
4311 switch (target)
4313 default:
4314 FIXME("Unsupported texture target %#x\n", target);
4315 /* Fall back to GL_TEXTURE_2D */
4316 case GL_TEXTURE_2D:
4317 info->binding = GL_TEXTURE_BINDING_2D;
4318 info->bind_target = GL_TEXTURE_2D;
4319 info->tex_type = tex_2d;
4320 coords[0][0] = 0.0f; coords[0][1] = 1.0f; coords[0][2] = 0.0f;
4321 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 0.0f;
4322 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4323 coords[3][0] = 1.0f; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4324 break;
4326 case GL_TEXTURE_RECTANGLE_ARB:
4327 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
4328 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
4329 info->tex_type = tex_rect;
4330 coords[0][0] = 0.0f; coords[0][1] = h; coords[0][2] = 0.0f;
4331 coords[1][0] = w; coords[1][1] = h; coords[1][2] = 0.0f;
4332 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4333 coords[3][0] = w; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4334 break;
4336 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4337 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4338 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4339 info->tex_type = tex_cube;
4340 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4341 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4342 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4343 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4345 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4346 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4347 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4348 info->tex_type = tex_cube;
4349 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4350 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4351 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4352 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4354 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4355 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4356 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4357 info->tex_type = tex_cube;
4358 coords[0][0] = -1.0f; coords[0][1] = 1.0f; coords[0][2] = 1.0f;
4359 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 1.0f;
4360 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4361 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4363 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4364 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4365 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4366 info->tex_type = tex_cube;
4367 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4368 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4369 coords[2][0] = -1.0f; coords[2][1] = -1.0f; coords[2][2] = 1.0f;
4370 coords[3][0] = 1.0f; coords[3][1] = -1.0f; coords[3][2] = 1.0f;
4372 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4373 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4374 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4375 info->tex_type = tex_cube;
4376 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4377 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4378 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4379 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4381 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4382 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4383 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4384 info->tex_type = tex_cube;
4385 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4386 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4387 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4388 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4392 /* GL locking is done by the caller */
4393 static void surface_depth_blt(IWineD3DSurfaceImpl *This, GLuint texture, GLsizei w, GLsizei h, GLenum target)
4395 IWineD3DDeviceImpl *device = This->resource.device;
4396 struct depth_blt_info info;
4397 GLint old_binding = 0;
4399 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4401 glDisable(GL_CULL_FACE);
4402 glDisable(GL_BLEND);
4403 glDisable(GL_ALPHA_TEST);
4404 glDisable(GL_SCISSOR_TEST);
4405 glDisable(GL_STENCIL_TEST);
4406 glEnable(GL_DEPTH_TEST);
4407 glDepthFunc(GL_ALWAYS);
4408 glDepthMask(GL_TRUE);
4409 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4410 glViewport(0, 0, w, h);
4412 surface_get_depth_blt_info(target, w, h, &info);
4413 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4414 glGetIntegerv(info.binding, &old_binding);
4415 glBindTexture(info.bind_target, texture);
4417 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4419 glBegin(GL_TRIANGLE_STRIP);
4420 glTexCoord3fv(info.coords[0]);
4421 glVertex2f(-1.0f, -1.0f);
4422 glTexCoord3fv(info.coords[1]);
4423 glVertex2f(1.0f, -1.0f);
4424 glTexCoord3fv(info.coords[2]);
4425 glVertex2f(-1.0f, 1.0f);
4426 glTexCoord3fv(info.coords[3]);
4427 glVertex2f(1.0f, 1.0f);
4428 glEnd();
4430 glBindTexture(info.bind_target, old_binding);
4432 glPopAttrib();
4434 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4437 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
4438 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4440 TRACE("(%p) New location %#x\n", This, location);
4442 if (location & ~SFLAG_DS_LOCATIONS) {
4443 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
4446 This->Flags &= ~SFLAG_DS_LOCATIONS;
4447 This->Flags |= location;
4450 /* Context activation is done by the caller. */
4451 void surface_load_ds_location(IWineD3DSurface *iface, struct wined3d_context *context, DWORD location)
4453 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4454 IWineD3DDeviceImpl *device = This->resource.device;
4455 const struct wined3d_gl_info *gl_info = context->gl_info;
4457 TRACE("(%p) New location %#x\n", This, location);
4459 /* TODO: Make this work for modes other than FBO */
4460 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4462 if (This->Flags & location) {
4463 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
4464 return;
4467 if (This->current_renderbuffer) {
4468 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
4469 return;
4472 if (location == SFLAG_DS_OFFSCREEN) {
4473 if (This->Flags & SFLAG_DS_ONSCREEN) {
4474 GLint old_binding = 0;
4475 GLenum bind_target;
4477 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
4479 ENTER_GL();
4481 if (!device->depth_blt_texture) {
4482 glGenTextures(1, &device->depth_blt_texture);
4485 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4486 * directly on the FBO texture. That's because we need to flip. */
4487 context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4488 if (This->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4490 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4491 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4492 } else {
4493 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4494 bind_target = GL_TEXTURE_2D;
4496 glBindTexture(bind_target, device->depth_blt_texture);
4497 glCopyTexImage2D(bind_target, This->texture_level, This->resource.format_desc->glInternal,
4498 0, 0, This->currentDesc.Width, This->currentDesc.Height, 0);
4499 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4500 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4501 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4502 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4503 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4504 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4505 glBindTexture(bind_target, old_binding);
4507 /* Setup the destination */
4508 if (!device->depth_blt_rb) {
4509 gl_info->fbo_ops.glGenRenderbuffers(1, &device->depth_blt_rb);
4510 checkGLcall("glGenRenderbuffersEXT");
4512 if (device->depth_blt_rb_w != This->currentDesc.Width
4513 || device->depth_blt_rb_h != This->currentDesc.Height) {
4514 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, device->depth_blt_rb);
4515 checkGLcall("glBindRenderbufferEXT");
4516 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8,
4517 This->currentDesc.Width, This->currentDesc.Height);
4518 checkGLcall("glRenderbufferStorageEXT");
4519 device->depth_blt_rb_w = This->currentDesc.Width;
4520 device->depth_blt_rb_h = This->currentDesc.Height;
4523 context_bind_fbo(context, GL_FRAMEBUFFER, &context->dst_fbo);
4524 gl_info->fbo_ops.glFramebufferRenderbuffer(GL_FRAMEBUFFER,
4525 GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, device->depth_blt_rb);
4526 checkGLcall("glFramebufferRenderbufferEXT");
4527 context_attach_depth_stencil_fbo(context, GL_FRAMEBUFFER, iface, FALSE);
4529 /* Do the actual blit */
4530 surface_depth_blt(This, device->depth_blt_texture, This->currentDesc.Width, This->currentDesc.Height, bind_target);
4531 checkGLcall("depth_blt");
4533 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER, &context->current_fbo->id);
4534 else context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4536 LEAVE_GL();
4537 } else {
4538 FIXME("No up to date depth stencil location\n");
4540 } else if (location == SFLAG_DS_ONSCREEN) {
4541 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4542 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4544 ENTER_GL();
4546 context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4547 surface_depth_blt(This, This->texture_name, This->currentDesc.Width,
4548 This->currentDesc.Height, This->texture_target);
4549 checkGLcall("depth_blt");
4551 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER, &context->current_fbo->id);
4553 LEAVE_GL();
4554 } else {
4555 FIXME("No up to date depth stencil location\n");
4557 } else {
4558 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4561 This->Flags |= location;
4564 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4565 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4566 IWineD3DBaseTexture *texture;
4567 IWineD3DSurfaceImpl *overlay;
4569 TRACE("(%p)->(%s, %s)\n", iface, debug_surflocation(flag),
4570 persistent ? "TRUE" : "FALSE");
4572 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4573 if (surface_is_offscreen(iface))
4575 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4576 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4578 else
4580 TRACE("Surface %p is an onscreen surface\n", iface);
4584 if(persistent) {
4585 if(((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) ||
4586 ((This->Flags & SFLAG_INSRGBTEX) && !(flag & SFLAG_INSRGBTEX))) {
4587 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4588 TRACE("Passing to container\n");
4589 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4590 IWineD3DBaseTexture_Release(texture);
4593 This->Flags &= ~SFLAG_LOCATIONS;
4594 This->Flags |= flag;
4596 /* Redraw emulated overlays, if any */
4597 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4598 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4599 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4602 } else {
4603 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))) {
4604 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4605 TRACE("Passing to container\n");
4606 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4607 IWineD3DBaseTexture_Release(texture);
4610 This->Flags &= ~flag;
4613 if(!(This->Flags & SFLAG_LOCATIONS)) {
4614 ERR("%p: Surface does not have any up to date location\n", This);
4618 struct coords {
4619 GLfloat x, y, z;
4622 struct float_rect
4624 float l;
4625 float t;
4626 float r;
4627 float b;
4630 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
4632 f->l = ((r->left * 2.0f) / w) - 1.0f;
4633 f->t = ((r->top * 2.0f) / h) - 1.0f;
4634 f->r = ((r->right * 2.0f) / w) - 1.0f;
4635 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
4638 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in)
4640 IWineD3DDeviceImpl *device = This->resource.device;
4641 struct wined3d_context *context;
4642 struct coords coords[4];
4643 RECT rect;
4644 IWineD3DSwapChain *swapchain;
4645 IWineD3DBaseTexture *texture;
4646 GLenum bind_target;
4647 struct float_rect f;
4649 if(rect_in) {
4650 rect = *rect_in;
4651 } else {
4652 rect.left = 0;
4653 rect.top = 0;
4654 rect.right = This->currentDesc.Width;
4655 rect.bottom = This->currentDesc.Height;
4658 switch (This->texture_target)
4660 case GL_TEXTURE_2D:
4661 bind_target = GL_TEXTURE_2D;
4663 coords[0].x = (float)rect.left / This->pow2Width;
4664 coords[0].y = (float)rect.top / This->pow2Height;
4665 coords[0].z = 0;
4667 coords[1].x = (float)rect.left / This->pow2Width;
4668 coords[1].y = (float)rect.bottom / This->pow2Height;
4669 coords[1].z = 0;
4671 coords[2].x = (float)rect.right / This->pow2Width;
4672 coords[2].y = (float)rect.bottom / This->pow2Height;
4673 coords[2].z = 0;
4675 coords[3].x = (float)rect.right / This->pow2Width;
4676 coords[3].y = (float)rect.top / This->pow2Height;
4677 coords[3].z = 0;
4678 break;
4680 case GL_TEXTURE_RECTANGLE_ARB:
4681 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4682 coords[0].x = rect.left; coords[0].y = rect.top; coords[0].z = 0;
4683 coords[1].x = rect.left; coords[1].y = rect.bottom; coords[1].z = 0;
4684 coords[2].x = rect.right; coords[2].y = rect.bottom; coords[2].z = 0;
4685 coords[3].x = rect.right; coords[3].y = rect.top; coords[3].z = 0;
4686 break;
4688 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4689 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4690 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4691 coords[0].x = 1; coords[0].y = -f.t; coords[0].z = -f.l;
4692 coords[1].x = 1; coords[1].y = -f.b; coords[1].z = -f.l;
4693 coords[2].x = 1; coords[2].y = -f.b; coords[2].z = -f.r;
4694 coords[3].x = 1; coords[3].y = -f.t; coords[3].z = -f.r;
4695 break;
4697 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4698 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4699 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4700 coords[0].x = -1; coords[0].y = -f.t; coords[0].z = f.l;
4701 coords[1].x = -1; coords[1].y = -f.b; coords[1].z = f.l;
4702 coords[2].x = -1; coords[2].y = -f.b; coords[2].z = f.r;
4703 coords[3].x = -1; coords[3].y = -f.t; coords[3].z = f.r;
4704 break;
4706 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4707 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4708 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4709 coords[0].x = f.l; coords[0].y = 1; coords[0].z = f.t;
4710 coords[1].x = f.l; coords[1].y = 1; coords[1].z = f.b;
4711 coords[2].x = f.r; coords[2].y = 1; coords[2].z = f.b;
4712 coords[3].x = f.r; coords[3].y = 1; coords[3].z = f.t;
4713 break;
4715 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4716 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4717 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4718 coords[0].x = f.l; coords[0].y = -1; coords[0].z = -f.t;
4719 coords[1].x = f.l; coords[1].y = -1; coords[1].z = -f.b;
4720 coords[2].x = f.r; coords[2].y = -1; coords[2].z = -f.b;
4721 coords[3].x = f.r; coords[3].y = -1; coords[3].z = -f.t;
4722 break;
4724 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4725 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4726 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4727 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1;
4728 coords[1].x = f.l; coords[1].y = -f.b; coords[1].z = 1;
4729 coords[2].x = f.r; coords[2].y = -f.b; coords[2].z = 1;
4730 coords[3].x = f.r; coords[3].y = -f.t; coords[3].z = 1;
4731 break;
4733 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4734 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4735 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4736 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1;
4737 coords[1].x = -f.l; coords[1].y = -f.b; coords[1].z = -1;
4738 coords[2].x = -f.r; coords[2].y = -f.b; coords[2].z = -1;
4739 coords[3].x = -f.r; coords[3].y = -f.t; coords[3].z = -1;
4740 break;
4742 default:
4743 ERR("Unexpected texture target %#x\n", This->texture_target);
4744 return;
4747 context = context_acquire(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4749 ENTER_GL();
4751 glEnable(bind_target);
4752 checkGLcall("glEnable(bind_target)");
4753 glBindTexture(bind_target, This->texture_name);
4754 checkGLcall("glBindTexture(bind_target, This->texture_name)");
4755 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4756 checkGLcall("glTexParameteri");
4757 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4758 checkGLcall("glTexParameteri");
4760 if (context->render_offscreen)
4762 LONG tmp = rect.top;
4763 rect.top = rect.bottom;
4764 rect.bottom = tmp;
4767 glBegin(GL_QUADS);
4768 glTexCoord3fv(&coords[0].x);
4769 glVertex2i(rect.left, rect.top);
4771 glTexCoord3fv(&coords[1].x);
4772 glVertex2i(rect.left, rect.bottom);
4774 glTexCoord3fv(&coords[2].x);
4775 glVertex2i(rect.right, rect.bottom);
4777 glTexCoord3fv(&coords[3].x);
4778 glVertex2i(rect.right, rect.top);
4779 glEnd();
4780 checkGLcall("glEnd");
4782 glDisable(bind_target);
4783 checkGLcall("glDisable(bind_target)");
4785 LEAVE_GL();
4787 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain)))
4789 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
4790 if(((IWineD3DSwapChainImpl*)swapchain)->frontBuffer == (IWineD3DSurface*)This ||
4791 ((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
4792 wglFlush();
4794 IWineD3DSwapChain_Release(swapchain);
4795 } else {
4796 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
4797 * reset properly next draw
4799 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture)))
4801 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
4802 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
4803 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
4804 IWineD3DBaseTexture_Release(texture);
4808 context_release(context);
4811 /*****************************************************************************
4812 * IWineD3DSurface::LoadLocation
4814 * Copies the current surface data from wherever it is to the requested
4815 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4816 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4817 * multiple locations, the gl texture is preferred over the drawable, which is
4818 * preferred over system memory. The PBO counts as system memory. If rect is
4819 * not NULL, only the specified rectangle is copied (only supported for
4820 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4821 * location is marked up to date after the copy.
4823 * Parameters:
4824 * flag: Surface location flag to be updated
4825 * rect: rectangle to be copied
4827 * Returns:
4828 * WINED3D_OK on success
4829 * WINED3DERR_DEVICELOST on an internal error
4831 *****************************************************************************/
4832 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4833 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4834 IWineD3DDeviceImpl *device = This->resource.device;
4835 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4836 GLenum format, internal, type;
4837 CONVERT_TYPES convert;
4838 int bpp;
4839 int width, pitch, outpitch;
4840 BYTE *mem;
4841 BOOL drawable_read_ok = TRUE;
4842 BOOL in_fbo = FALSE;
4844 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4845 if (surface_is_offscreen(iface))
4847 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4848 * Prefer SFLAG_INTEXTURE. */
4849 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4850 drawable_read_ok = FALSE;
4851 in_fbo = TRUE;
4853 else
4855 TRACE("Surface %p is an onscreen surface\n", iface);
4859 TRACE("(%p)->(%s, %p)\n", iface, debug_surflocation(flag), rect);
4860 if(rect) {
4861 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4864 if(This->Flags & flag) {
4865 TRACE("Location already up to date\n");
4866 return WINED3D_OK;
4869 if(!(This->Flags & SFLAG_LOCATIONS)) {
4870 ERR("%p: Surface does not have any up to date location\n", This);
4871 This->Flags |= SFLAG_LOST;
4872 return WINED3DERR_DEVICELOST;
4875 if(flag == SFLAG_INSYSMEM) {
4876 surface_prepare_system_memory(This);
4878 /* Download the surface to system memory */
4879 if (This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
4881 struct wined3d_context *context = NULL;
4883 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4885 surface_bind_and_dirtify(This, !(This->Flags & SFLAG_INTEXTURE));
4886 surface_download_data(This);
4888 if (context) context_release(context);
4890 else
4892 /* Note: It might be faster to download into a texture first. */
4893 read_from_framebuffer(This, rect,
4894 This->resource.allocatedMemory,
4895 IWineD3DSurface_GetPitch(iface));
4897 } else if(flag == SFLAG_INDRAWABLE) {
4898 if(This->Flags & SFLAG_INTEXTURE) {
4899 surface_blt_to_drawable(This, rect);
4900 } else {
4901 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4902 /* This needs a shader to convert the srgb data sampled from the GL texture into RGB
4903 * values, otherwise we get incorrect values in the target. For now go the slow way
4904 * via a system memory copy
4906 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4909 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, FALSE);
4911 /* The width is in 'length' not in bytes */
4912 width = This->currentDesc.Width;
4913 pitch = IWineD3DSurface_GetPitch(iface);
4915 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4916 * but it isn't set (yet) in all cases it is getting called. */
4917 if ((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO))
4919 struct wined3d_context *context = NULL;
4921 TRACE("Removing the pbo attached to surface %p\n", This);
4923 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4924 surface_remove_pbo(This);
4925 if (context) context_release(context);
4928 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4929 int height = This->currentDesc.Height;
4931 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4932 outpitch = width * bpp;
4933 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4935 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4936 if(!mem) {
4937 ERR("Out of memory %d, %d!\n", outpitch, height);
4938 return WINED3DERR_OUTOFVIDEOMEMORY;
4940 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4942 This->Flags |= SFLAG_CONVERTED;
4943 } else {
4944 This->Flags &= ~SFLAG_CONVERTED;
4945 mem = This->resource.allocatedMemory;
4948 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4950 /* Don't delete PBO memory */
4951 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4952 HeapFree(GetProcessHeap(), 0, mem);
4954 } else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */ {
4955 if (drawable_read_ok && (This->Flags & SFLAG_INDRAWABLE)) {
4956 read_from_framebuffer_texture(This, flag == SFLAG_INSRGBTEX);
4958 else
4960 /* Upload from system memory */
4961 BOOL srgb = flag == SFLAG_INSRGBTEX;
4962 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4963 struct wined3d_context *context = NULL;
4965 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */,
4966 &format, &internal, &type, &convert, &bpp, srgb);
4968 if(srgb) {
4969 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE) {
4970 /* Performance warning ... */
4971 FIXME("%p: Downloading rgb texture to reload it as srgb\n", This);
4972 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4974 } else {
4975 if((This->Flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX) {
4976 /* Performance warning ... */
4977 FIXME("%p: Downloading srgb texture to reload it as rgb\n", This);
4978 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4981 if(!(This->Flags & SFLAG_INSYSMEM)) {
4982 /* Should not happen */
4983 ERR("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set\n");
4984 /* Lets hope we get it from somewhere... */
4985 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4988 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4989 surface_bind_and_dirtify(This, srgb);
4991 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4992 This->Flags |= SFLAG_GLCKEY;
4993 This->glCKey = This->SrcBltCKey;
4995 else This->Flags &= ~SFLAG_GLCKEY;
4997 /* The width is in 'length' not in bytes */
4998 width = This->currentDesc.Width;
4999 pitch = IWineD3DSurface_GetPitch(iface);
5001 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
5002 * but it isn't set (yet) in all cases it is getting called. */
5003 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
5004 TRACE("Removing the pbo attached to surface %p\n", This);
5005 surface_remove_pbo(This);
5008 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
5009 int height = This->currentDesc.Height;
5011 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
5012 outpitch = width * bpp;
5013 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5015 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
5016 if(!mem) {
5017 ERR("Out of memory %d, %d!\n", outpitch, height);
5018 if (context) context_release(context);
5019 return WINED3DERR_OUTOFVIDEOMEMORY;
5021 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
5023 This->Flags |= SFLAG_CONVERTED;
5025 else if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
5026 && (gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM]))
5028 d3dfmt_p8_upload_palette(iface, convert);
5029 This->Flags &= ~SFLAG_CONVERTED;
5030 mem = This->resource.allocatedMemory;
5031 } else {
5032 This->Flags &= ~SFLAG_CONVERTED;
5033 mem = This->resource.allocatedMemory;
5036 /* Make sure the correct pitch is used */
5037 ENTER_GL();
5038 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
5039 LEAVE_GL();
5041 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
5042 TRACE("non power of two support\n");
5043 if(!(This->Flags & alloc_flag)) {
5044 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
5045 This->Flags |= alloc_flag;
5047 if (mem || (This->Flags & SFLAG_PBO)) {
5048 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
5050 } else {
5051 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
5052 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
5054 if(!(This->Flags & alloc_flag)) {
5055 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
5056 This->Flags |= alloc_flag;
5058 if (mem || (This->Flags & SFLAG_PBO)) {
5059 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
5063 /* Restore the default pitch */
5064 ENTER_GL();
5065 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
5066 LEAVE_GL();
5068 if (context) context_release(context);
5070 /* Don't delete PBO memory */
5071 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
5072 HeapFree(GetProcessHeap(), 0, mem);
5076 if(rect == NULL) {
5077 This->Flags |= flag;
5080 if (in_fbo && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
5081 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
5082 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
5085 return WINED3D_OK;
5088 static HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container)
5090 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
5091 IWineD3DSwapChain *swapchain = NULL;
5093 /* Update the drawable size method */
5094 if(container) {
5095 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
5097 if(swapchain) {
5098 This->get_drawable_size = get_drawable_size_swapchain;
5099 IWineD3DSwapChain_Release(swapchain);
5100 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
5101 switch(wined3d_settings.offscreen_rendering_mode) {
5102 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
5103 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
5104 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
5108 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
5111 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
5112 return SURFACE_OPENGL;
5115 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
5116 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
5117 HRESULT hr;
5119 /* If there's no destination surface there is nothing to do */
5120 if(!This->overlay_dest) return WINED3D_OK;
5122 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
5123 * update the overlay. Prevent an endless recursion
5125 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
5126 return WINED3D_OK;
5128 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
5129 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
5130 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
5131 NULL, WINED3DTEXF_LINEAR);
5132 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
5134 return hr;
5137 BOOL surface_is_offscreen(IWineD3DSurface *iface)
5139 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
5140 IWineD3DSwapChainImpl *swapchain = (IWineD3DSwapChainImpl *) This->container;
5142 /* Not on a swapchain - must be offscreen */
5143 if (!(This->Flags & SFLAG_SWAPCHAIN)) return TRUE;
5145 /* The front buffer is always onscreen */
5146 if(iface == swapchain->frontBuffer) return FALSE;
5148 /* If the swapchain is rendered to an FBO, the backbuffer is
5149 * offscreen, otherwise onscreen */
5150 return swapchain->render_to_fbo;
5153 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
5155 /* IUnknown */
5156 IWineD3DBaseSurfaceImpl_QueryInterface,
5157 IWineD3DBaseSurfaceImpl_AddRef,
5158 IWineD3DSurfaceImpl_Release,
5159 /* IWineD3DResource */
5160 IWineD3DBaseSurfaceImpl_GetParent,
5161 IWineD3DBaseSurfaceImpl_SetPrivateData,
5162 IWineD3DBaseSurfaceImpl_GetPrivateData,
5163 IWineD3DBaseSurfaceImpl_FreePrivateData,
5164 IWineD3DBaseSurfaceImpl_SetPriority,
5165 IWineD3DBaseSurfaceImpl_GetPriority,
5166 IWineD3DSurfaceImpl_PreLoad,
5167 IWineD3DSurfaceImpl_UnLoad,
5168 IWineD3DBaseSurfaceImpl_GetType,
5169 /* IWineD3DSurface */
5170 IWineD3DBaseSurfaceImpl_GetContainer,
5171 IWineD3DBaseSurfaceImpl_GetDesc,
5172 IWineD3DSurfaceImpl_LockRect,
5173 IWineD3DSurfaceImpl_UnlockRect,
5174 IWineD3DSurfaceImpl_GetDC,
5175 IWineD3DSurfaceImpl_ReleaseDC,
5176 IWineD3DSurfaceImpl_Flip,
5177 IWineD3DSurfaceImpl_Blt,
5178 IWineD3DBaseSurfaceImpl_GetBltStatus,
5179 IWineD3DBaseSurfaceImpl_GetFlipStatus,
5180 IWineD3DBaseSurfaceImpl_IsLost,
5181 IWineD3DBaseSurfaceImpl_Restore,
5182 IWineD3DSurfaceImpl_BltFast,
5183 IWineD3DBaseSurfaceImpl_GetPalette,
5184 IWineD3DBaseSurfaceImpl_SetPalette,
5185 IWineD3DSurfaceImpl_RealizePalette,
5186 IWineD3DBaseSurfaceImpl_SetColorKey,
5187 IWineD3DBaseSurfaceImpl_GetPitch,
5188 IWineD3DSurfaceImpl_SetMem,
5189 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
5190 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
5191 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
5192 IWineD3DBaseSurfaceImpl_UpdateOverlay,
5193 IWineD3DBaseSurfaceImpl_SetClipper,
5194 IWineD3DBaseSurfaceImpl_GetClipper,
5195 /* Internal use: */
5196 IWineD3DSurfaceImpl_LoadTexture,
5197 IWineD3DSurfaceImpl_BindTexture,
5198 IWineD3DSurfaceImpl_SaveSnapshot,
5199 IWineD3DSurfaceImpl_SetContainer,
5200 IWineD3DBaseSurfaceImpl_GetData,
5201 IWineD3DSurfaceImpl_SetFormat,
5202 IWineD3DSurfaceImpl_PrivateSetup,
5203 IWineD3DSurfaceImpl_ModifyLocation,
5204 IWineD3DSurfaceImpl_LoadLocation,
5205 IWineD3DSurfaceImpl_GetImplType,
5206 IWineD3DSurfaceImpl_DrawOverlay
5208 #undef GLINFO_LOCATION
5210 #define GLINFO_LOCATION device->adapter->gl_info
5211 static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
5212 /* Context activation is done by the caller. */
5213 static void ffp_blit_free(IWineD3DDevice *iface) { }
5215 /* Context activation is done by the caller. */
5216 static HRESULT ffp_blit_set(IWineD3DDevice *iface, const struct GlPixelFormatDesc *format_desc,
5217 GLenum textype, UINT width, UINT height)
5219 ENTER_GL();
5220 glEnable(textype);
5221 checkGLcall("glEnable(textype)");
5222 LEAVE_GL();
5223 return WINED3D_OK;
5226 /* Context activation is done by the caller. */
5227 static void ffp_blit_unset(IWineD3DDevice *iface)
5229 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
5230 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5232 ENTER_GL();
5233 glDisable(GL_TEXTURE_2D);
5234 checkGLcall("glDisable(GL_TEXTURE_2D)");
5235 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
5237 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
5238 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
5240 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
5242 glDisable(GL_TEXTURE_RECTANGLE_ARB);
5243 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
5245 LEAVE_GL();
5248 static BOOL ffp_blit_color_fixup_supported(struct color_fixup_desc fixup)
5250 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
5252 TRACE("Checking support for fixup:\n");
5253 dump_color_fixup_desc(fixup);
5256 /* We only support identity conversions. */
5257 if (is_identity_fixup(fixup))
5259 TRACE("[OK]\n");
5260 return TRUE;
5263 TRACE("[FAILED]\n");
5264 return FALSE;
5267 const struct blit_shader ffp_blit = {
5268 ffp_blit_alloc,
5269 ffp_blit_free,
5270 ffp_blit_set,
5271 ffp_blit_unset,
5272 ffp_blit_color_fixup_supported