kernel32/tests: Init pointers in common way, add some win_skip() while skipping.
[wine/hramrach.git] / dlls / wined3d / surface.c
blobf95692795f53fa644ea813913af81a9905bbfeab
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)
666 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
667 IWineD3DSwapChainImpl *swapchain = (IWineD3DSwapChainImpl *)This->container;
669 TRACE("iface %p.\n", iface);
671 if (!(This->Flags & SFLAG_SWAPCHAIN))
673 ERR("Surface %p is not on a swapchain.\n", iface);
674 return GL_NONE;
677 if (swapchain->backBuffer && swapchain->backBuffer[0] == iface)
679 if (swapchain->render_to_fbo)
681 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
682 return GL_COLOR_ATTACHMENT0;
684 TRACE("Returning GL_BACK\n");
685 return GL_BACK;
687 else if (swapchain->frontBuffer == iface)
689 TRACE("Returning GL_FRONT\n");
690 return GL_FRONT;
693 FIXME("Higher back buffer, returning GL_BACK\n");
694 return GL_BACK;
697 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
698 void surface_add_dirty_rect(IWineD3DSurface *iface, const RECT *dirty_rect)
700 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
701 IWineD3DBaseTexture *baseTexture = NULL;
703 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
704 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
706 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
707 if (dirty_rect)
709 This->dirtyRect.left = min(This->dirtyRect.left, dirty_rect->left);
710 This->dirtyRect.top = min(This->dirtyRect.top, dirty_rect->top);
711 This->dirtyRect.right = max(This->dirtyRect.right, dirty_rect->right);
712 This->dirtyRect.bottom = max(This->dirtyRect.bottom, dirty_rect->bottom);
714 else
716 This->dirtyRect.left = 0;
717 This->dirtyRect.top = 0;
718 This->dirtyRect.right = This->currentDesc.Width;
719 This->dirtyRect.bottom = This->currentDesc.Height;
722 TRACE("(%p) : Dirty: yes, Rect:(%d, %d, %d, %d)\n", This, This->dirtyRect.left,
723 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
725 /* if the container is a basetexture then mark it dirty. */
726 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture)))
728 TRACE("Passing to container\n");
729 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
730 IWineD3DBaseTexture_Release(baseTexture);
734 static inline BOOL surface_can_stretch_rect(IWineD3DSurfaceImpl *src, IWineD3DSurfaceImpl *dst)
736 return ((src->resource.format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE)
737 || (src->resource.usage & WINED3DUSAGE_RENDERTARGET))
738 && ((dst->resource.format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE)
739 || (dst->resource.usage & WINED3DUSAGE_RENDERTARGET))
740 && (src->resource.format_desc->format == dst->resource.format_desc->format
741 || (is_identity_fixup(src->resource.format_desc->color_fixup)
742 && is_identity_fixup(dst->resource.format_desc->color_fixup)));
745 static ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface)
747 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
748 ULONG ref = InterlockedDecrement(&This->resource.ref);
749 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
751 if (!ref)
753 surface_cleanup(This);
754 This->resource.parent_ops->wined3d_object_destroyed(This->resource.parent);
756 TRACE("(%p) Released.\n", This);
757 HeapFree(GetProcessHeap(), 0, This);
760 return ref;
763 /* ****************************************************
764 IWineD3DSurface IWineD3DResource parts follow
765 **************************************************** */
767 void surface_internal_preload(IWineD3DSurface *iface, enum WINED3DSRGB srgb)
769 /* TODO: check for locks */
770 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
771 IWineD3DDeviceImpl *device = This->resource.device;
772 IWineD3DBaseTexture *baseTexture = NULL;
774 TRACE("(%p)Checking to see if the container is a base texture\n", This);
775 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
776 IWineD3DBaseTextureImpl *tex_impl = (IWineD3DBaseTextureImpl *) baseTexture;
777 TRACE("Passing to container\n");
778 tex_impl->baseTexture.internal_preload(baseTexture, srgb);
779 IWineD3DBaseTexture_Release(baseTexture);
780 } else {
781 struct wined3d_context *context = NULL;
783 TRACE("(%p) : About to load surface\n", This);
785 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
787 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
788 || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
790 if(palette9_changed(This)) {
791 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
792 /* TODO: This is not necessarily needed with hw palettized texture support */
793 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
794 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
795 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
799 IWineD3DSurface_LoadTexture(iface, srgb == SRGB_SRGB ? TRUE : FALSE);
801 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
802 /* Tell opengl to try and keep this texture in video ram (well mostly) */
803 GLclampf tmp;
804 tmp = 0.9f;
805 ENTER_GL();
806 glPrioritizeTextures(1, &This->texture_name, &tmp);
807 LEAVE_GL();
810 if (context) context_release(context);
812 return;
815 static void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
816 surface_internal_preload(iface, SRGB_ANY);
819 /* Context activation is done by the caller. */
820 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
821 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
822 This->resource.allocatedMemory =
823 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
825 ENTER_GL();
826 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
827 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
828 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
829 checkGLcall("glGetBufferSubDataARB");
830 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
831 checkGLcall("glDeleteBuffersARB");
832 LEAVE_GL();
834 This->pbo = 0;
835 This->Flags &= ~SFLAG_PBO;
838 BOOL surface_init_sysmem(IWineD3DSurface *iface)
840 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
842 if(!This->resource.allocatedMemory)
844 This->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->resource.size + RESOURCE_ALIGNMENT);
845 if(!This->resource.heapMemory)
847 ERR("Out of memory\n");
848 return FALSE;
850 This->resource.allocatedMemory =
851 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
853 else
855 memset(This->resource.allocatedMemory, 0, This->resource.size);
858 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
859 return TRUE;
862 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
863 IWineD3DBaseTexture *texture = NULL;
864 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
865 IWineD3DDeviceImpl *device = This->resource.device;
866 const struct wined3d_gl_info *gl_info;
867 renderbuffer_entry_t *entry, *entry2;
868 struct wined3d_context *context;
870 TRACE("(%p)\n", iface);
872 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
873 /* Default pool resources are supposed to be destroyed before Reset is called.
874 * Implicit resources stay however. So this means we have an implicit render target
875 * or depth stencil. The content may be destroyed, but we still have to tear down
876 * opengl resources, so we cannot leave early.
878 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
879 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
880 * or the depth stencil into an FBO the texture or render buffer will be removed
881 * and all flags get lost
883 surface_init_sysmem(iface);
884 } else {
885 /* Load the surface into system memory */
886 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
887 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
889 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
890 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSRGBTEX, FALSE);
891 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
893 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
894 gl_info = context->gl_info;
896 /* Destroy PBOs, but load them into real sysmem before */
897 if(This->Flags & SFLAG_PBO) {
898 surface_remove_pbo(This);
901 /* Destroy fbo render buffers. This is needed for implicit render targets, for
902 * all application-created targets the application has to release the surface
903 * before calling _Reset
905 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
906 ENTER_GL();
907 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
908 LEAVE_GL();
909 list_remove(&entry->entry);
910 HeapFree(GetProcessHeap(), 0, entry);
912 list_init(&This->renderbuffers);
913 This->current_renderbuffer = NULL;
915 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
916 * destroy it
918 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
919 if(!texture) {
920 ENTER_GL();
921 glDeleteTextures(1, &This->texture_name);
922 This->texture_name = 0;
923 glDeleteTextures(1, &This->texture_name_srgb);
924 This->texture_name_srgb = 0;
925 LEAVE_GL();
926 } else {
927 IWineD3DBaseTexture_Release(texture);
930 context_release(context);
932 return;
935 /* ******************************************************
936 IWineD3DSurface IWineD3DSurface parts follow
937 ****************************************************** */
939 /* Read the framebuffer back into the surface */
940 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, const RECT *rect, void *dest, UINT pitch)
942 IWineD3DDeviceImpl *myDevice = This->resource.device;
943 struct wined3d_context *context;
944 BYTE *mem;
945 GLint fmt;
946 GLint type;
947 BYTE *row, *top, *bottom;
948 int i;
949 BOOL bpp;
950 RECT local_rect;
951 BOOL srcIsUpsideDown;
952 GLint rowLen = 0;
953 GLint skipPix = 0;
954 GLint skipRow = 0;
956 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
957 static BOOL warned = FALSE;
958 if(!warned) {
959 ERR("The application tries to lock the render target, but render target locking is disabled\n");
960 warned = TRUE;
962 return;
965 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
966 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
967 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
968 * context->last_was_blit set on the unlock.
970 context = context_acquire(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
971 ENTER_GL();
973 /* Select the correct read buffer, and give some debug output.
974 * There is no need to keep track of the current read buffer or reset it, every part of the code
975 * that reads sets the read buffer as desired.
977 if (surface_is_offscreen((IWineD3DSurface *) This))
979 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
980 * Read from the back buffer
982 TRACE("Locking offscreen render target\n");
983 glReadBuffer(myDevice->offscreenBuffer);
984 srcIsUpsideDown = TRUE;
986 else
988 /* Onscreen surfaces are always part of a swapchain */
989 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This);
990 TRACE("Locking %#x buffer\n", buffer);
991 glReadBuffer(buffer);
992 checkGLcall("glReadBuffer");
993 srcIsUpsideDown = FALSE;
996 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
997 if(!rect) {
998 local_rect.left = 0;
999 local_rect.top = 0;
1000 local_rect.right = This->currentDesc.Width;
1001 local_rect.bottom = This->currentDesc.Height;
1002 } else {
1003 local_rect = *rect;
1005 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
1007 switch(This->resource.format_desc->format)
1009 case WINED3DFMT_P8_UINT:
1011 if(primary_render_target_is_p8(myDevice)) {
1012 /* In case of P8 render targets the index is stored in the alpha component */
1013 fmt = GL_ALPHA;
1014 type = GL_UNSIGNED_BYTE;
1015 mem = dest;
1016 bpp = This->resource.format_desc->byte_count;
1017 } else {
1018 /* GL can't return palettized data, so read ARGB pixels into a
1019 * separate block of memory and convert them into palettized format
1020 * in software. Slow, but if the app means to use palettized render
1021 * targets and locks it...
1023 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
1024 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
1025 * for the color channels when palettizing the colors.
1027 fmt = GL_RGB;
1028 type = GL_UNSIGNED_BYTE;
1029 pitch *= 3;
1030 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
1031 if(!mem) {
1032 ERR("Out of memory\n");
1033 LEAVE_GL();
1034 return;
1036 bpp = This->resource.format_desc->byte_count * 3;
1039 break;
1041 default:
1042 mem = dest;
1043 fmt = This->resource.format_desc->glFormat;
1044 type = This->resource.format_desc->glType;
1045 bpp = This->resource.format_desc->byte_count;
1048 if(This->Flags & SFLAG_PBO) {
1049 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
1050 checkGLcall("glBindBufferARB");
1051 if(mem != NULL) {
1052 ERR("mem not null for pbo -- unexpected\n");
1053 mem = NULL;
1057 /* Save old pixel store pack state */
1058 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
1059 checkGLcall("glGetIntegerv");
1060 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
1061 checkGLcall("glGetIntegerv");
1062 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
1063 checkGLcall("glGetIntegerv");
1065 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1066 glPixelStorei(GL_PACK_ROW_LENGTH, This->currentDesc.Width);
1067 checkGLcall("glPixelStorei");
1068 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
1069 checkGLcall("glPixelStorei");
1070 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
1071 checkGLcall("glPixelStorei");
1073 glReadPixels(local_rect.left, (!srcIsUpsideDown) ? (This->currentDesc.Height - local_rect.bottom) : local_rect.top ,
1074 local_rect.right - local_rect.left,
1075 local_rect.bottom - local_rect.top,
1076 fmt, type, mem);
1077 checkGLcall("glReadPixels");
1079 /* Reset previous pixel store pack state */
1080 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
1081 checkGLcall("glPixelStorei");
1082 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
1083 checkGLcall("glPixelStorei");
1084 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
1085 checkGLcall("glPixelStorei");
1087 if(This->Flags & SFLAG_PBO) {
1088 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1089 checkGLcall("glBindBufferARB");
1091 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
1092 * to get a pointer to it and perform the flipping in software. This is a lot
1093 * faster than calling glReadPixels for each line. In case we want more speed
1094 * we should rerender it flipped in a FBO and read the data back from the FBO. */
1095 if(!srcIsUpsideDown) {
1096 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1097 checkGLcall("glBindBufferARB");
1099 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1100 checkGLcall("glMapBufferARB");
1104 /* TODO: Merge this with the palettization loop below for P8 targets */
1105 if(!srcIsUpsideDown) {
1106 UINT len, off;
1107 /* glReadPixels returns the image upside down, and there is no way to prevent this.
1108 Flip the lines in software */
1109 len = (local_rect.right - local_rect.left) * bpp;
1110 off = local_rect.left * bpp;
1112 row = HeapAlloc(GetProcessHeap(), 0, len);
1113 if(!row) {
1114 ERR("Out of memory\n");
1115 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT) HeapFree(GetProcessHeap(), 0, mem);
1116 LEAVE_GL();
1117 return;
1120 top = mem + pitch * local_rect.top;
1121 bottom = mem + pitch * (local_rect.bottom - 1);
1122 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
1123 memcpy(row, top + off, len);
1124 memcpy(top + off, bottom + off, len);
1125 memcpy(bottom + off, row, len);
1126 top += pitch;
1127 bottom -= pitch;
1129 HeapFree(GetProcessHeap(), 0, row);
1131 /* Unmap the temp PBO buffer */
1132 if(This->Flags & SFLAG_PBO) {
1133 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1134 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1138 LEAVE_GL();
1139 context_release(context);
1141 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
1142 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
1143 * the same color but we have no choice.
1144 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
1146 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(myDevice))
1148 const PALETTEENTRY *pal = NULL;
1149 DWORD width = pitch / 3;
1150 int x, y, c;
1152 if(This->palette) {
1153 pal = This->palette->palents;
1154 } else {
1155 ERR("Palette is missing, cannot perform inverse palette lookup\n");
1156 HeapFree(GetProcessHeap(), 0, mem);
1157 return ;
1160 for(y = local_rect.top; y < local_rect.bottom; y++) {
1161 for(x = local_rect.left; x < local_rect.right; x++) {
1162 /* start lines pixels */
1163 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
1164 const BYTE *green = blue + 1;
1165 const BYTE *red = green + 1;
1167 for(c = 0; c < 256; c++) {
1168 if(*red == pal[c].peRed &&
1169 *green == pal[c].peGreen &&
1170 *blue == pal[c].peBlue)
1172 *((BYTE *) dest + y * width + x) = c;
1173 break;
1178 HeapFree(GetProcessHeap(), 0, mem);
1182 /* Read the framebuffer contents into a texture */
1183 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This, BOOL srgb)
1185 IWineD3DDeviceImpl *device = This->resource.device;
1186 struct wined3d_context *context;
1187 int bpp;
1188 GLenum format, internal, type;
1189 CONVERT_TYPES convert;
1190 GLint prevRead;
1191 BOOL alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
1193 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
1195 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
1196 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
1197 * states in the stateblock, and no driver was found yet that had bugs in that regard.
1199 context = context_acquire(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
1200 surface_bind_and_dirtify(This, srgb);
1202 ENTER_GL();
1203 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1204 LEAVE_GL();
1206 /* Select the correct read buffer, and give some debug output.
1207 * There is no need to keep track of the current read buffer or reset it, every part of the code
1208 * that reads sets the read buffer as desired.
1210 if (!surface_is_offscreen((IWineD3DSurface *)This))
1212 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This);
1213 TRACE("Locking %#x buffer\n", buffer);
1215 ENTER_GL();
1216 glReadBuffer(buffer);
1217 checkGLcall("glReadBuffer");
1218 LEAVE_GL();
1220 else
1222 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1223 * Read from the back buffer
1225 TRACE("Locking offscreen render target\n");
1226 ENTER_GL();
1227 glReadBuffer(device->offscreenBuffer);
1228 checkGLcall("glReadBuffer");
1229 LEAVE_GL();
1232 if(!(This->Flags & alloc_flag)) {
1233 surface_allocate_surface(This, internal, This->pow2Width,
1234 This->pow2Height, format, type);
1235 This->Flags |= alloc_flag;
1238 ENTER_GL();
1239 /* If !SrcIsUpsideDown we should flip the surface.
1240 * This can be done using glCopyTexSubImage2D but this
1241 * is VERY slow, so don't do that. We should prevent
1242 * this code from getting called in such cases or perhaps
1243 * we can use FBOs */
1245 glCopyTexSubImage2D(This->texture_target, This->texture_level,
1246 0, 0, 0, 0, This->currentDesc.Width, This->currentDesc.Height);
1247 checkGLcall("glCopyTexSubImage2D");
1249 glReadBuffer(prevRead);
1250 checkGLcall("glReadBuffer");
1252 LEAVE_GL();
1254 context_release(context);
1256 TRACE("Updated target %d\n", This->texture_target);
1259 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This)
1261 IWineD3DDeviceImpl *device = This->resource.device;
1262 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1264 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
1265 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
1266 * changed
1268 if(!(This->Flags & SFLAG_DYNLOCK)) {
1269 This->lockCount++;
1270 /* MAXLOCKCOUNT is defined in wined3d_private.h */
1271 if(This->lockCount > MAXLOCKCOUNT) {
1272 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
1273 This->Flags |= SFLAG_DYNLOCK;
1277 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
1278 * Also don't create a PBO for systemmem surfaces.
1280 if (gl_info->supported[ARB_PIXEL_BUFFER_OBJECT] && (This->Flags & SFLAG_DYNLOCK)
1281 && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2))
1282 && (This->resource.pool != WINED3DPOOL_SYSTEMMEM))
1284 GLenum error;
1285 struct wined3d_context *context;
1287 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
1288 ENTER_GL();
1290 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
1291 error = glGetError();
1292 if(This->pbo == 0 || error != GL_NO_ERROR) {
1293 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
1296 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
1298 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1299 checkGLcall("glBindBufferARB");
1301 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1302 checkGLcall("glBufferDataARB");
1304 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1305 checkGLcall("glBindBufferARB");
1307 /* We don't need the system memory anymore and we can't even use it for PBOs */
1308 if(!(This->Flags & SFLAG_CLIENT)) {
1309 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1310 This->resource.heapMemory = NULL;
1312 This->resource.allocatedMemory = NULL;
1313 This->Flags |= SFLAG_PBO;
1314 LEAVE_GL();
1315 context_release(context);
1317 else if (!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO))
1319 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1320 * or a pbo to map
1322 if(!This->resource.heapMemory) {
1323 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1325 This->resource.allocatedMemory =
1326 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1327 if(This->Flags & SFLAG_INSYSMEM) {
1328 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1333 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1334 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1335 IWineD3DDeviceImpl *myDevice = This->resource.device;
1336 const RECT *pass_rect = pRect;
1338 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1340 /* This is also done in the base class, but we have to verify this before loading any data from
1341 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1342 * may interfere, and all other bad things may happen
1344 if (This->Flags & SFLAG_LOCKED) {
1345 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1346 return WINED3DERR_INVALIDCALL;
1348 This->Flags |= SFLAG_LOCKED;
1350 if (!(This->Flags & SFLAG_LOCKABLE))
1352 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1355 if (Flags & WINED3DLOCK_DISCARD) {
1356 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1357 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1358 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1359 This->Flags |= SFLAG_INSYSMEM;
1360 goto lock_end;
1363 if (This->Flags & SFLAG_INSYSMEM) {
1364 TRACE("Local copy is up to date, not downloading data\n");
1365 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1366 goto lock_end;
1369 /* IWineD3DSurface_LoadLocation() does not check if the rectangle specifies
1370 * the full surface. Most callers don't need that, so do it here. */
1371 if (pRect && pRect->top == 0 && pRect->left == 0
1372 && pRect->right == This->currentDesc.Width
1373 && pRect->bottom == This->currentDesc.Height)
1375 pass_rect = NULL;
1378 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
1379 && ((This->Flags & SFLAG_SWAPCHAIN) || iface == myDevice->render_targets[0])))
1381 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1384 lock_end:
1385 if (This->Flags & SFLAG_PBO)
1387 struct wined3d_context *context;
1389 context = context_acquire(myDevice, NULL, CTXUSAGE_RESOURCELOAD);
1390 ENTER_GL();
1391 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1392 checkGLcall("glBindBufferARB");
1394 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1395 if(This->resource.allocatedMemory) {
1396 ERR("The surface already has PBO memory allocated!\n");
1399 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1400 checkGLcall("glMapBufferARB");
1402 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1403 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1404 checkGLcall("glBindBufferARB");
1406 LEAVE_GL();
1407 context_release(context);
1410 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1411 /* Don't dirtify */
1412 } else {
1413 IWineD3DBaseTexture *pBaseTexture;
1415 * Dirtify on lock
1416 * as seen in msdn docs
1418 surface_add_dirty_rect(iface, pRect);
1420 /** Dirtify Container if needed */
1421 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1422 TRACE("Making container dirty\n");
1423 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1424 IWineD3DBaseTexture_Release(pBaseTexture);
1425 } else {
1426 TRACE("Surface is standalone, no need to dirty the container\n");
1430 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1433 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1434 GLint prev_store;
1435 GLint prev_rasterpos[4];
1436 GLint skipBytes = 0;
1437 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1438 IWineD3DDeviceImpl *myDevice = This->resource.device;
1439 struct wined3d_context *context;
1441 /* Activate the correct context for the render target */
1442 context = context_acquire(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1443 ENTER_GL();
1445 if (!surface_is_offscreen((IWineD3DSurface *)This))
1447 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This);
1448 TRACE("Unlocking %#x buffer.\n", buffer);
1449 context_set_draw_buffer(context, buffer);
1451 else
1453 /* Primary offscreen render target */
1454 TRACE("Offscreen render target.\n");
1455 context_set_draw_buffer(context, myDevice->offscreenBuffer);
1458 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1459 checkGLcall("glGetIntegerv");
1460 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1461 checkGLcall("glGetIntegerv");
1462 glPixelZoom(1.0f, -1.0f);
1463 checkGLcall("glPixelZoom");
1465 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1466 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1467 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1469 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1470 checkGLcall("glRasterPos3i");
1472 /* Some drivers(radeon dri, others?) don't like exceptions during
1473 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1474 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1475 * catch to put the dib section in InSync mode, which leads to a crash
1476 * and a blocked x server on my radeon card.
1478 * The following lines read the dib section so it is put in InSync mode
1479 * before glDrawPixels is called and the crash is prevented. There won't
1480 * be any interfering gdi accesses, because UnlockRect is called from
1481 * ReleaseDC, and the app won't use the dc any more afterwards.
1483 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1484 volatile BYTE read;
1485 read = This->resource.allocatedMemory[0];
1488 if(This->Flags & SFLAG_PBO) {
1489 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1490 checkGLcall("glBindBufferARB");
1493 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1494 if(This->Flags & SFLAG_LOCKED) {
1495 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1496 (This->lockedRect.bottom - This->lockedRect.top)-1,
1497 fmt, type,
1498 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1499 checkGLcall("glDrawPixels");
1500 } else {
1501 glDrawPixels(This->currentDesc.Width,
1502 This->currentDesc.Height,
1503 fmt, type, mem);
1504 checkGLcall("glDrawPixels");
1507 if(This->Flags & SFLAG_PBO) {
1508 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1509 checkGLcall("glBindBufferARB");
1512 glPixelZoom(1.0f, 1.0f);
1513 checkGLcall("glPixelZoom");
1515 glRasterPos3iv(&prev_rasterpos[0]);
1516 checkGLcall("glRasterPos3iv");
1518 /* Reset to previous pack row length */
1519 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1520 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH)");
1522 LEAVE_GL();
1523 context_release(context);
1526 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1527 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1528 IWineD3DDeviceImpl *myDevice = This->resource.device;
1529 BOOL fullsurface;
1531 if (!(This->Flags & SFLAG_LOCKED)) {
1532 WARN("trying to Unlock an unlocked surf@%p\n", This);
1533 return WINEDDERR_NOTLOCKED;
1536 if (This->Flags & SFLAG_PBO)
1538 struct wined3d_context *context;
1540 TRACE("Freeing PBO memory\n");
1542 context = context_acquire(myDevice, NULL, CTXUSAGE_RESOURCELOAD);
1543 ENTER_GL();
1544 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1545 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1546 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1547 checkGLcall("glUnmapBufferARB");
1548 LEAVE_GL();
1549 context_release(context);
1551 This->resource.allocatedMemory = NULL;
1554 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1556 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1557 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1558 goto unlock_end;
1561 if ((This->Flags & SFLAG_SWAPCHAIN) || (myDevice->render_targets && iface == myDevice->render_targets[0]))
1563 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1564 static BOOL warned = FALSE;
1565 if(!warned) {
1566 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1567 warned = TRUE;
1569 goto unlock_end;
1572 if(This->dirtyRect.left == 0 &&
1573 This->dirtyRect.top == 0 &&
1574 This->dirtyRect.right == This->currentDesc.Width &&
1575 This->dirtyRect.bottom == This->currentDesc.Height) {
1576 fullsurface = TRUE;
1577 } else {
1578 /* TODO: Proper partial rectangle tracking */
1579 fullsurface = FALSE;
1580 This->Flags |= SFLAG_INSYSMEM;
1583 switch(wined3d_settings.rendertargetlock_mode) {
1584 case RTL_READTEX:
1585 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1586 /* drop through */
1588 case RTL_READDRAW:
1589 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1590 break;
1593 if(!fullsurface) {
1594 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1595 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1596 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1597 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1598 * not fully up to date because only a subrectangle was read in LockRect.
1600 This->Flags &= ~SFLAG_INSYSMEM;
1601 This->Flags |= SFLAG_INDRAWABLE;
1604 This->dirtyRect.left = This->currentDesc.Width;
1605 This->dirtyRect.top = This->currentDesc.Height;
1606 This->dirtyRect.right = 0;
1607 This->dirtyRect.bottom = 0;
1608 } else if(iface == myDevice->stencilBufferTarget) {
1609 FIXME("Depth Stencil buffer locking is not implemented\n");
1610 } else {
1611 /* The rest should be a normal texture */
1612 IWineD3DBaseTextureImpl *impl;
1613 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1614 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1615 * states need resetting
1617 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1618 if(impl->baseTexture.bindCount) {
1619 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1621 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1625 unlock_end:
1626 This->Flags &= ~SFLAG_LOCKED;
1627 memset(&This->lockedRect, 0, sizeof(RECT));
1629 /* Overlays have to be redrawn manually after changes with the GL implementation */
1630 if(This->overlay_dest) {
1631 IWineD3DSurface_DrawOverlay(iface);
1633 return WINED3D_OK;
1636 static void surface_release_client_storage(IWineD3DSurface *iface)
1638 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
1639 struct wined3d_context *context;
1641 context = context_acquire(This->resource.device, NULL, CTXUSAGE_RESOURCELOAD);
1643 ENTER_GL();
1644 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
1645 if(This->texture_name)
1647 surface_bind_and_dirtify(This, FALSE);
1648 glTexImage2D(This->texture_target, This->texture_level,
1649 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
1651 if(This->texture_name_srgb)
1653 surface_bind_and_dirtify(This, TRUE);
1654 glTexImage2D(This->texture_target, This->texture_level,
1655 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
1657 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
1659 LEAVE_GL();
1660 context_release(context);
1662 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSRGBTEX, FALSE);
1663 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
1664 surface_force_reload(iface);
1667 static HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC)
1669 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1670 WINED3DLOCKED_RECT lock;
1671 HRESULT hr;
1672 RGBQUAD col[256];
1674 TRACE("(%p)->(%p)\n",This,pHDC);
1676 if(This->Flags & SFLAG_USERPTR) {
1677 ERR("Not supported on surfaces with an application-provided surfaces\n");
1678 return WINEDDERR_NODC;
1681 /* Give more detailed info for ddraw */
1682 if (This->Flags & SFLAG_DCINUSE)
1683 return WINEDDERR_DCALREADYCREATED;
1685 /* Can't GetDC if the surface is locked */
1686 if (This->Flags & SFLAG_LOCKED)
1687 return WINED3DERR_INVALIDCALL;
1689 memset(&lock, 0, sizeof(lock)); /* To be sure */
1691 /* Create a DIB section if there isn't a hdc yet */
1692 if(!This->hDC) {
1693 if(This->Flags & SFLAG_CLIENT) {
1694 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
1695 surface_release_client_storage(iface);
1697 hr = IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1698 if(FAILED(hr)) return WINED3DERR_INVALIDCALL;
1700 /* Use the dib section from now on if we are not using a PBO */
1701 if(!(This->Flags & SFLAG_PBO))
1702 This->resource.allocatedMemory = This->dib.bitmap_data;
1705 /* Lock the surface */
1706 hr = IWineD3DSurface_LockRect(iface,
1707 &lock,
1708 NULL,
1711 if(This->Flags & SFLAG_PBO) {
1712 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1713 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1716 if(FAILED(hr)) {
1717 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1718 /* keep the dib section */
1719 return hr;
1722 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
1723 || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
1725 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1726 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1727 unsigned int n;
1728 const PALETTEENTRY *pal = NULL;
1730 if(This->palette) {
1731 pal = This->palette->palents;
1732 } else {
1733 IWineD3DSurfaceImpl *dds_primary;
1734 IWineD3DSwapChainImpl *swapchain;
1735 swapchain = (IWineD3DSwapChainImpl *)This->resource.device->swapchains[0];
1736 dds_primary = (IWineD3DSurfaceImpl *)swapchain->frontBuffer;
1737 if (dds_primary && dds_primary->palette)
1738 pal = dds_primary->palette->palents;
1741 if (pal) {
1742 for (n=0; n<256; n++) {
1743 col[n].rgbRed = pal[n].peRed;
1744 col[n].rgbGreen = pal[n].peGreen;
1745 col[n].rgbBlue = pal[n].peBlue;
1746 col[n].rgbReserved = 0;
1748 SetDIBColorTable(This->hDC, 0, 256, col);
1752 *pHDC = This->hDC;
1753 TRACE("returning %p\n",*pHDC);
1754 This->Flags |= SFLAG_DCINUSE;
1756 return WINED3D_OK;
1759 static HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC)
1761 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1763 TRACE("(%p)->(%p)\n",This,hDC);
1765 if (!(This->Flags & SFLAG_DCINUSE))
1766 return WINEDDERR_NODC;
1768 if (This->hDC !=hDC) {
1769 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1770 return WINEDDERR_NODC;
1773 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1774 /* Copy the contents of the DIB over to the PBO */
1775 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1778 /* we locked first, so unlock now */
1779 IWineD3DSurface_UnlockRect(iface);
1781 This->Flags &= ~SFLAG_DCINUSE;
1783 return WINED3D_OK;
1786 /* ******************************************************
1787 IWineD3DSurface Internal (No mapping to directx api) parts follow
1788 ****************************************************** */
1790 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) {
1791 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1792 const struct GlPixelFormatDesc *glDesc = This->resource.format_desc;
1793 IWineD3DDeviceImpl *device = This->resource.device;
1794 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1796 /* Default values: From the surface */
1797 *format = glDesc->glFormat;
1798 *type = glDesc->glType;
1799 *convert = NO_CONVERSION;
1800 *target_bpp = glDesc->byte_count;
1802 if(srgb_mode) {
1803 *internal = glDesc->glGammaInternal;
1805 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET
1806 && surface_is_offscreen((IWineD3DSurface *) This))
1808 *internal = glDesc->rtInternal;
1809 } else {
1810 *internal = glDesc->glInternal;
1813 /* Ok, now look if we have to do any conversion */
1814 switch(This->resource.format_desc->format)
1816 case WINED3DFMT_P8_UINT:
1817 /* ****************
1818 Paletted Texture
1819 **************** */
1821 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1822 * of the two is available make sure texturing is requested as neither of the two works in
1823 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1824 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1825 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1826 * conflicts with this.
1828 if (!(gl_info->supported[EXT_PALETTED_TEXTURE] || (gl_info->supported[ARB_FRAGMENT_PROGRAM]
1829 && device->render_targets && This == (IWineD3DSurfaceImpl*)device->render_targets[0]))
1830 || colorkey_active || !use_texturing)
1832 *format = GL_RGBA;
1833 *internal = GL_RGBA;
1834 *type = GL_UNSIGNED_BYTE;
1835 *target_bpp = 4;
1836 if(colorkey_active) {
1837 *convert = CONVERT_PALETTED_CK;
1838 } else {
1839 *convert = CONVERT_PALETTED;
1842 else if (!gl_info->supported[EXT_PALETTED_TEXTURE] && gl_info->supported[ARB_FRAGMENT_PROGRAM])
1844 *format = GL_ALPHA;
1845 *type = GL_UNSIGNED_BYTE;
1846 *target_bpp = 1;
1849 break;
1851 case WINED3DFMT_B2G3R3_UNORM:
1852 /* **********************
1853 GL_UNSIGNED_BYTE_3_3_2
1854 ********************** */
1855 if (colorkey_active) {
1856 /* This texture format will never be used.. So do not care about color keying
1857 up until the point in time it will be needed :-) */
1858 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1860 break;
1862 case WINED3DFMT_B5G6R5_UNORM:
1863 if (colorkey_active) {
1864 *convert = CONVERT_CK_565;
1865 *format = GL_RGBA;
1866 *internal = GL_RGB5_A1;
1867 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1869 break;
1871 case WINED3DFMT_B5G5R5X1_UNORM:
1872 if (colorkey_active) {
1873 *convert = CONVERT_CK_5551;
1874 *format = GL_BGRA;
1875 *internal = GL_RGB5_A1;
1876 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1878 break;
1880 case WINED3DFMT_B8G8R8_UNORM:
1881 if (colorkey_active) {
1882 *convert = CONVERT_CK_RGB24;
1883 *format = GL_RGBA;
1884 *internal = GL_RGBA8;
1885 *type = GL_UNSIGNED_INT_8_8_8_8;
1886 *target_bpp = 4;
1888 break;
1890 case WINED3DFMT_B8G8R8X8_UNORM:
1891 if (colorkey_active) {
1892 *convert = CONVERT_RGB32_888;
1893 *format = GL_RGBA;
1894 *internal = GL_RGBA8;
1895 *type = GL_UNSIGNED_INT_8_8_8_8;
1897 break;
1899 case WINED3DFMT_R8G8_SNORM:
1900 if (gl_info->supported[NV_TEXTURE_SHADER]) break;
1901 *convert = CONVERT_V8U8;
1902 *format = GL_BGR;
1903 *type = GL_UNSIGNED_BYTE;
1904 *target_bpp = 3;
1905 break;
1907 case WINED3DFMT_R5G5_SNORM_L6_UNORM:
1908 *convert = CONVERT_L6V5U5;
1909 if (gl_info->supported[NV_TEXTURE_SHADER])
1911 *target_bpp = 3;
1912 /* Use format and types from table */
1913 } else {
1914 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1915 *target_bpp = 2;
1916 *format = GL_RGB;
1917 *type = GL_UNSIGNED_SHORT_5_6_5;
1919 break;
1921 case WINED3DFMT_R8G8_SNORM_L8X8_UNORM:
1922 *convert = CONVERT_X8L8V8U8;
1923 *target_bpp = 4;
1924 if (gl_info->supported[NV_TEXTURE_SHADER])
1926 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1927 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1928 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1929 * the needed type and format parameter, so the internal format contains a
1930 * 4th component, which is returned as alpha
1932 } else {
1933 *format = GL_BGRA;
1934 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1936 break;
1938 case WINED3DFMT_R8G8B8A8_SNORM:
1939 if (gl_info->supported[NV_TEXTURE_SHADER]) break;
1940 *convert = CONVERT_Q8W8V8U8;
1941 *format = GL_BGRA;
1942 *type = GL_UNSIGNED_BYTE;
1943 *target_bpp = 4;
1944 break;
1946 case WINED3DFMT_R16G16_SNORM:
1947 if (gl_info->supported[NV_TEXTURE_SHADER]) break;
1948 *convert = CONVERT_V16U16;
1949 *format = GL_BGR;
1950 *type = GL_UNSIGNED_SHORT;
1951 *target_bpp = 6;
1952 break;
1954 case WINED3DFMT_L4A4_UNORM:
1955 /* WINED3DFMT_L4A4_UNORM exists as an internal gl format, but for some reason there is not
1956 * format+type combination to load it. Thus convert it to A8L8, then load it
1957 * with A4L4 internal, but A8L8 format+type
1959 *convert = CONVERT_A4L4;
1960 *format = GL_LUMINANCE_ALPHA;
1961 *type = GL_UNSIGNED_BYTE;
1962 *target_bpp = 2;
1963 break;
1965 case WINED3DFMT_R16G16_UNORM:
1966 *convert = CONVERT_G16R16;
1967 *format = GL_RGB;
1968 *type = GL_UNSIGNED_SHORT;
1969 *target_bpp = 6;
1970 break;
1972 case WINED3DFMT_R16G16_FLOAT:
1973 *convert = CONVERT_R16G16F;
1974 *format = GL_RGB;
1975 *type = GL_HALF_FLOAT_ARB;
1976 *target_bpp = 6;
1977 break;
1979 case WINED3DFMT_R32G32_FLOAT:
1980 *convert = CONVERT_R32G32F;
1981 *format = GL_RGB;
1982 *type = GL_FLOAT;
1983 *target_bpp = 12;
1984 break;
1986 case WINED3DFMT_S1_UINT_D15_UNORM:
1987 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1988 || gl_info->supported[EXT_PACKED_DEPTH_STENCIL])
1990 *convert = CONVERT_D15S1;
1991 *target_bpp = 4;
1993 break;
1995 case WINED3DFMT_S4X4_UINT_D24_UNORM:
1996 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1997 || gl_info->supported[EXT_PACKED_DEPTH_STENCIL])
1999 *convert = CONVERT_D24X4S4;
2001 break;
2003 case WINED3DFMT_S8_UINT_D24_FLOAT:
2004 if (gl_info->supported[ARB_DEPTH_BUFFER_FLOAT])
2006 *convert = CONVERT_D24FS8;
2007 *target_bpp = 8;
2009 break;
2011 default:
2012 break;
2015 return WINED3D_OK;
2018 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey)
2020 IWineD3DDeviceImpl *device = This->resource.device;
2021 IWineD3DPaletteImpl *pal = This->palette;
2022 BOOL index_in_alpha = FALSE;
2023 unsigned int i;
2025 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2026 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2027 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2028 * duplicate entries. Store the color key in the unused alpha component to speed the
2029 * download up and to make conversion unneeded. */
2030 index_in_alpha = primary_render_target_is_p8(device);
2032 if (!pal)
2034 UINT dxVersion = ((IWineD3DImpl *)device->wined3d)->dxVersion;
2036 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2037 if (dxVersion <= 7)
2039 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2040 if (index_in_alpha)
2042 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
2043 * there's no palette at this time. */
2044 for (i = 0; i < 256; i++) table[i][3] = i;
2047 else
2049 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2050 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2051 * capability flag is present (wine does advertise this capability) */
2052 for (i = 0; i < 256; ++i)
2054 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2055 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2056 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2057 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2061 else
2063 TRACE("Using surface palette %p\n", pal);
2064 /* Get the surface's palette */
2065 for (i = 0; i < 256; ++i)
2067 table[i][0] = pal->palents[i].peRed;
2068 table[i][1] = pal->palents[i].peGreen;
2069 table[i][2] = pal->palents[i].peBlue;
2071 /* When index_in_alpha is set the palette index is stored in the
2072 * alpha component. In case of a readback we can then read
2073 * GL_ALPHA. Color keying is handled in BltOverride using a
2074 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
2075 * color key itself is passed to glAlphaFunc in other cases the
2076 * alpha component of pixels that should be masked away is set to 0. */
2077 if (index_in_alpha)
2079 table[i][3] = i;
2081 else if (colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue)
2082 && (i <= This->SrcBltCKey.dwColorSpaceHighValue))
2084 table[i][3] = 0x00;
2086 else if(pal->Flags & WINEDDPCAPS_ALPHA)
2088 table[i][3] = pal->palents[i].peFlags;
2090 else
2092 table[i][3] = 0xFF;
2098 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
2099 UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This)
2101 IWineD3DDeviceImpl *device = This->resource.device;
2102 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
2103 const BYTE *source;
2104 BYTE *dest;
2105 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
2107 switch (convert) {
2108 case NO_CONVERSION:
2110 memcpy(dst, src, pitch * height);
2111 break;
2113 case CONVERT_PALETTED:
2114 case CONVERT_PALETTED_CK:
2116 IWineD3DPaletteImpl* pal = This->palette;
2117 BYTE table[256][4];
2118 unsigned int x, y;
2120 if( pal == NULL) {
2121 /* TODO: If we are a sublevel, try to get the palette from level 0 */
2124 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2126 for (y = 0; y < height; y++)
2128 source = src + pitch * y;
2129 dest = dst + outpitch * y;
2130 /* This is an 1 bpp format, using the width here is fine */
2131 for (x = 0; x < width; x++) {
2132 BYTE color = *source++;
2133 *dest++ = table[color][0];
2134 *dest++ = table[color][1];
2135 *dest++ = table[color][2];
2136 *dest++ = table[color][3];
2140 break;
2142 case CONVERT_CK_565:
2144 /* Converting the 565 format in 5551 packed to emulate color-keying.
2146 Note : in all these conversion, it would be best to average the averaging
2147 pixels to get the color of the pixel that will be color-keyed to
2148 prevent 'color bleeding'. This will be done later on if ever it is
2149 too visible.
2151 Note2: Nvidia documents say that their driver does not support alpha + color keying
2152 on the same surface and disables color keying in such a case
2154 unsigned int x, y;
2155 const WORD *Source;
2156 WORD *Dest;
2158 TRACE("Color keyed 565\n");
2160 for (y = 0; y < height; y++) {
2161 Source = (const WORD *)(src + y * pitch);
2162 Dest = (WORD *) (dst + y * outpitch);
2163 for (x = 0; x < width; x++ ) {
2164 WORD color = *Source++;
2165 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
2166 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2167 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2168 *Dest |= 0x0001;
2170 Dest++;
2174 break;
2176 case CONVERT_CK_5551:
2178 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
2179 unsigned int x, y;
2180 const WORD *Source;
2181 WORD *Dest;
2182 TRACE("Color keyed 5551\n");
2183 for (y = 0; y < height; y++) {
2184 Source = (const WORD *)(src + y * pitch);
2185 Dest = (WORD *) (dst + y * outpitch);
2186 for (x = 0; x < width; x++ ) {
2187 WORD color = *Source++;
2188 *Dest = color;
2189 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2190 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2191 *Dest |= (1 << 15);
2193 else {
2194 *Dest &= ~(1 << 15);
2196 Dest++;
2200 break;
2202 case CONVERT_CK_RGB24:
2204 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
2205 unsigned int x, y;
2206 for (y = 0; y < height; y++)
2208 source = src + pitch * y;
2209 dest = dst + outpitch * y;
2210 for (x = 0; x < width; x++) {
2211 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
2212 DWORD dstcolor = color << 8;
2213 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2214 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2215 dstcolor |= 0xff;
2217 *(DWORD*)dest = dstcolor;
2218 source += 3;
2219 dest += 4;
2223 break;
2225 case CONVERT_RGB32_888:
2227 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
2228 unsigned int x, y;
2229 for (y = 0; y < height; y++)
2231 source = src + pitch * y;
2232 dest = dst + outpitch * y;
2233 for (x = 0; x < width; x++) {
2234 DWORD color = 0xffffff & *(const DWORD*)source;
2235 DWORD dstcolor = color << 8;
2236 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2237 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2238 dstcolor |= 0xff;
2240 *(DWORD*)dest = dstcolor;
2241 source += 4;
2242 dest += 4;
2246 break;
2248 case CONVERT_V8U8:
2250 unsigned int x, y;
2251 const short *Source;
2252 unsigned char *Dest;
2253 for(y = 0; y < height; y++) {
2254 Source = (const short *)(src + y * pitch);
2255 Dest = dst + y * outpitch;
2256 for (x = 0; x < width; x++ ) {
2257 long color = (*Source++);
2258 /* B */ Dest[0] = 0xff;
2259 /* G */ Dest[1] = (color >> 8) + 128; /* V */
2260 /* R */ Dest[2] = (color) + 128; /* U */
2261 Dest += 3;
2264 break;
2267 case CONVERT_V16U16:
2269 unsigned int x, y;
2270 const DWORD *Source;
2271 unsigned short *Dest;
2272 for(y = 0; y < height; y++) {
2273 Source = (const DWORD *)(src + y * pitch);
2274 Dest = (unsigned short *) (dst + y * outpitch);
2275 for (x = 0; x < width; x++ ) {
2276 DWORD color = (*Source++);
2277 /* B */ Dest[0] = 0xffff;
2278 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
2279 /* R */ Dest[2] = (color ) + 32768; /* U */
2280 Dest += 3;
2283 break;
2286 case CONVERT_Q8W8V8U8:
2288 unsigned int x, y;
2289 const DWORD *Source;
2290 unsigned char *Dest;
2291 for(y = 0; y < height; y++) {
2292 Source = (const DWORD *)(src + y * pitch);
2293 Dest = dst + y * outpitch;
2294 for (x = 0; x < width; x++ ) {
2295 long color = (*Source++);
2296 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
2297 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2298 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2299 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
2300 Dest += 4;
2303 break;
2306 case CONVERT_L6V5U5:
2308 unsigned int x, y;
2309 const WORD *Source;
2310 unsigned char *Dest;
2312 if (gl_info->supported[NV_TEXTURE_SHADER])
2314 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
2315 * fixed function and shaders without further conversion once the surface is
2316 * loaded
2318 for(y = 0; y < height; y++) {
2319 Source = (const WORD *)(src + y * pitch);
2320 Dest = dst + y * outpitch;
2321 for (x = 0; x < width; x++ ) {
2322 short color = (*Source++);
2323 unsigned char l = ((color >> 10) & 0xfc);
2324 char v = ((color >> 5) & 0x3e);
2325 char u = ((color ) & 0x1f);
2327 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
2328 * and doubles the positive range. Thus shift left only once, gl does the 2nd
2329 * shift. GL reads a signed value and converts it into an unsigned value.
2331 /* M */ Dest[2] = l << 1;
2333 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
2334 * from 5 bit values to 8 bit values.
2336 /* V */ Dest[1] = v << 3;
2337 /* U */ Dest[0] = u << 3;
2338 Dest += 3;
2341 } else {
2342 for(y = 0; y < height; y++) {
2343 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
2344 Source = (const WORD *)(src + y * pitch);
2345 for (x = 0; x < width; x++ ) {
2346 short color = (*Source++);
2347 unsigned char l = ((color >> 10) & 0xfc);
2348 short v = ((color >> 5) & 0x3e);
2349 short u = ((color ) & 0x1f);
2350 short v_conv = v + 16;
2351 short u_conv = u + 16;
2353 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
2354 Dest_s += 1;
2358 break;
2361 case CONVERT_X8L8V8U8:
2363 unsigned int x, y;
2364 const DWORD *Source;
2365 unsigned char *Dest;
2367 if (gl_info->supported[NV_TEXTURE_SHADER])
2369 /* This implementation works with the fixed function pipeline and shaders
2370 * without further modification after converting the surface.
2372 for(y = 0; y < height; y++) {
2373 Source = (const DWORD *)(src + y * pitch);
2374 Dest = dst + y * outpitch;
2375 for (x = 0; x < width; x++ ) {
2376 long color = (*Source++);
2377 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
2378 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
2379 /* U */ Dest[0] = (color & 0xff); /* U */
2380 /* I */ Dest[3] = 255; /* X */
2381 Dest += 4;
2384 } else {
2385 /* Doesn't work correctly with the fixed function pipeline, but can work in
2386 * shaders if the shader is adjusted. (There's no use for this format in gl's
2387 * standard fixed function pipeline anyway).
2389 for(y = 0; y < height; y++) {
2390 Source = (const DWORD *)(src + y * pitch);
2391 Dest = dst + y * outpitch;
2392 for (x = 0; x < width; x++ ) {
2393 long color = (*Source++);
2394 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
2395 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2396 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2397 Dest += 4;
2401 break;
2404 case CONVERT_A4L4:
2406 unsigned int x, y;
2407 const unsigned char *Source;
2408 unsigned char *Dest;
2409 for(y = 0; y < height; y++) {
2410 Source = src + y * pitch;
2411 Dest = dst + y * outpitch;
2412 for (x = 0; x < width; x++ ) {
2413 unsigned char color = (*Source++);
2414 /* A */ Dest[1] = (color & 0xf0) << 0;
2415 /* L */ Dest[0] = (color & 0x0f) << 4;
2416 Dest += 2;
2419 break;
2422 case CONVERT_G16R16:
2423 case CONVERT_R16G16F:
2425 unsigned int x, y;
2426 const WORD *Source;
2427 WORD *Dest;
2429 for(y = 0; y < height; y++) {
2430 Source = (const WORD *)(src + y * pitch);
2431 Dest = (WORD *) (dst + y * outpitch);
2432 for (x = 0; x < width; x++ ) {
2433 WORD green = (*Source++);
2434 WORD red = (*Source++);
2435 Dest[0] = green;
2436 Dest[1] = red;
2437 /* Strictly speaking not correct for R16G16F, but it doesn't matter because the
2438 * shader overwrites it anyway
2440 Dest[2] = 0xffff;
2441 Dest += 3;
2444 break;
2447 case CONVERT_R32G32F:
2449 unsigned int x, y;
2450 const float *Source;
2451 float *Dest;
2452 for(y = 0; y < height; y++) {
2453 Source = (const float *)(src + y * pitch);
2454 Dest = (float *) (dst + y * outpitch);
2455 for (x = 0; x < width; x++ ) {
2456 float green = (*Source++);
2457 float red = (*Source++);
2458 Dest[0] = green;
2459 Dest[1] = red;
2460 Dest[2] = 1.0f;
2461 Dest += 3;
2464 break;
2467 case CONVERT_D15S1:
2469 unsigned int x, y;
2471 for (y = 0; y < height; ++y)
2473 const WORD *source = (const WORD *)(src + y * pitch);
2474 DWORD *dest = (DWORD *)(dst + y * outpitch);
2476 for (x = 0; x < width; ++x)
2478 /* The depth data is normalized, so needs to be scaled,
2479 * the stencil data isn't. Scale depth data by
2480 * (2^24-1)/(2^15-1) ~~ (2^9 + 2^-6). */
2481 WORD d15 = source[x] >> 1;
2482 DWORD d24 = (d15 << 9) + (d15 >> 6);
2483 dest[x] = (d24 << 8) | (source[x] & 0x1);
2486 break;
2489 case CONVERT_D24X4S4:
2491 unsigned int x, y;
2493 for (y = 0; y < height; ++y)
2495 const DWORD *source = (const DWORD *)(src + y * pitch);
2496 DWORD *dest = (DWORD *)(dst + y * outpitch);
2498 for (x = 0; x < width; ++x)
2500 /* Just need to clear out the X4 part. */
2501 dest[x] = source[x] & ~0xf0;
2504 break;
2507 case CONVERT_D24FS8:
2509 unsigned int x, y;
2511 for (y = 0; y < height; ++y)
2513 const DWORD *source = (const DWORD *)(src + y * pitch);
2514 float *dest_f = (float *)(dst + y * outpitch);
2515 DWORD *dest_s = (DWORD *)(dst + y * outpitch);
2517 for (x = 0; x < width; ++x)
2519 dest_f[x * 2] = float_24_to_32((source[x] & 0xffffff00) >> 8);
2520 dest_s[x * 2 + 1] = source[x] & 0xff;
2523 break;
2526 default:
2527 ERR("Unsupported conversion type %#x.\n", convert);
2529 return WINED3D_OK;
2532 /* This function is used in case of 8bit paletted textures to upload the palette.
2533 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2534 extensions like ATI_fragment_shaders is possible.
2536 /* Context activation is done by the caller. */
2537 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2538 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2539 BYTE table[256][4];
2540 IWineD3DDeviceImpl *device = This->resource.device;
2541 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
2543 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2545 /* Try to use the paletted texture extension */
2546 if (gl_info->supported[EXT_PALETTED_TEXTURE])
2548 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2549 ENTER_GL();
2550 GL_EXTCALL(glColorTableEXT(This->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
2551 LEAVE_GL();
2553 else
2555 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2556 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2557 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2559 ENTER_GL();
2561 /* Create the fragment program if we don't have it */
2562 if(!device->paletteConversionShader)
2564 const char *fragment_palette_conversion =
2565 "!!ARBfp1.0\n"
2566 "TEMP index;\n"
2567 /* { 255/256, 0.5/255*255/256, 0, 0 } */
2568 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n"
2569 /* The alpha-component contains the palette index */
2570 "TEX index, fragment.texcoord[0], texture[0], 2D;\n"
2571 /* Scale the index by 255/256 and add a bias of '0.5' in order to sample in the middle */
2572 "MAD index.a, index.a, constants.x, constants.y;\n"
2573 /* Use the alpha-component as an index in the palette to get the final color */
2574 "TEX result.color, index.a, texture[1], 1D;\n"
2575 "END";
2577 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2578 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2579 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2580 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), fragment_palette_conversion));
2581 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2584 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2585 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2587 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2588 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2590 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2591 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2592 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2593 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2595 /* Switch back to unit 0 in which the 2D texture will be stored. */
2596 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2598 /* Rebind the texture because it isn't bound anymore */
2599 glBindTexture(This->texture_target, This->texture_name);
2601 LEAVE_GL();
2605 BOOL palette9_changed(IWineD3DSurfaceImpl *This)
2607 IWineD3DDeviceImpl *device = This->resource.device;
2609 if (This->palette || (This->resource.format_desc->format != WINED3DFMT_P8_UINT
2610 && This->resource.format_desc->format != WINED3DFMT_P8_UINT_A8_UNORM))
2612 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2613 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2615 return FALSE;
2618 if (This->palette9)
2620 if (!memcmp(This->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256))
2622 return FALSE;
2624 } else {
2625 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2627 memcpy(This->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2628 return TRUE;
2631 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2632 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2633 DWORD flag = srgb_mode ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2635 if (!(This->Flags & flag)) {
2636 TRACE("Reloading because surface is dirty\n");
2637 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2638 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2639 /* Reload: vice versa OR */
2640 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2641 /* Also reload: Color key is active AND the color key has changed */
2642 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2643 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2644 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2645 TRACE("Reloading because of color keying\n");
2646 /* To perform the color key conversion we need a sysmem copy of
2647 * the surface. Make sure we have it
2650 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2651 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2652 /* TODO: This is not necessarily needed with hw palettized texture support */
2653 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2654 } else {
2655 TRACE("surface is already in texture\n");
2656 return WINED3D_OK;
2659 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2660 * These resources are not bound by device size or format restrictions. Because of this,
2661 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2662 * However, these resources can always be created, locked, and copied.
2664 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2666 FIXME("(%p) Operation not supported for scratch textures\n",This);
2667 return WINED3DERR_INVALIDCALL;
2670 IWineD3DSurface_LoadLocation(iface, flag, NULL /* no partial locking for textures yet */);
2672 #if 0
2674 static unsigned int gen = 0;
2675 char buffer[4096];
2676 ++gen;
2677 if ((gen % 10) == 0) {
2678 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm",
2679 This, This->texture_target, This->texture_level, gen);
2680 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2683 * debugging crash code
2684 if (gen == 250) {
2685 void** test = NULL;
2686 *test = 0;
2690 #endif
2692 if (!(This->Flags & SFLAG_DONOTFREE)) {
2693 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2694 This->resource.allocatedMemory = NULL;
2695 This->resource.heapMemory = NULL;
2696 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2699 return WINED3D_OK;
2702 /* Context activation is done by the caller. */
2703 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface, BOOL srgb) {
2704 /* TODO: check for locks */
2705 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2706 IWineD3DDeviceImpl *device = This->resource.device;
2707 IWineD3DBaseTexture *baseTexture = NULL;
2709 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2710 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2711 TRACE("Passing to container\n");
2712 IWineD3DBaseTexture_BindTexture(baseTexture, srgb);
2713 IWineD3DBaseTexture_Release(baseTexture);
2715 else
2717 struct wined3d_context *context = NULL;
2718 GLuint *name;
2720 TRACE("(%p) : Binding surface\n", This);
2722 name = srgb ? &This->texture_name_srgb : &This->texture_name;
2723 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
2725 ENTER_GL();
2727 if (!This->texture_level)
2729 if (!*name) {
2730 glGenTextures(1, name);
2731 checkGLcall("glGenTextures");
2732 TRACE("Surface %p given name %d\n", This, *name);
2734 glBindTexture(This->texture_target, *name);
2735 checkGLcall("glBindTexture");
2736 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2737 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2738 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2739 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2740 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2741 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2742 glTexParameteri(This->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2743 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2744 glTexParameteri(This->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2745 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2747 /* This is where we should be reducing the amount of GLMemoryUsed */
2748 } else if (*name) {
2749 /* Mipmap surfaces should have a base texture container */
2750 ERR("Mipmap surface has a glTexture bound to it!\n");
2753 glBindTexture(This->texture_target, *name);
2754 checkGLcall("glBindTexture");
2756 LEAVE_GL();
2758 if (context) context_release(context);
2760 return;
2763 #include <errno.h>
2764 #include <stdio.h>
2765 static HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename)
2767 FILE* f = NULL;
2768 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2769 char *allocatedMemory;
2770 const char *textureRow;
2771 IWineD3DSwapChain *swapChain = NULL;
2772 int width, height, i, y;
2773 GLuint tmpTexture = 0;
2774 DWORD color;
2775 /*FIXME:
2776 Textures may not be stored in ->allocatedgMemory and a GlTexture
2777 so we should lock the surface before saving a snapshot, or at least check that
2779 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2780 by calling GetTexImage and in compressed form by calling
2781 GetCompressedTexImageARB. Queried compressed images can be saved and
2782 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2783 texture images do not need to be processed by the GL and should
2784 significantly improve texture loading performance relative to uncompressed
2785 images. */
2787 /* Setup the width and height to be the internal texture width and height. */
2788 width = This->pow2Width;
2789 height = This->pow2Height;
2790 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2791 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2793 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2794 /* if were not a real texture then read the back buffer into a real texture */
2795 /* we don't want to interfere with the back buffer so read the data into a temporary
2796 * texture and then save the data out of the temporary texture
2798 GLint prevRead;
2799 ENTER_GL();
2800 TRACE("(%p) Reading render target into texture\n", This);
2802 glGenTextures(1, &tmpTexture);
2803 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2805 glTexImage2D(GL_TEXTURE_2D,
2807 GL_RGBA,
2808 width,
2809 height,
2810 0/*border*/,
2811 GL_RGBA,
2812 GL_UNSIGNED_INT_8_8_8_8_REV,
2813 NULL);
2815 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2816 checkGLcall("glGetIntegerv");
2817 glReadBuffer(swapChain ? GL_BACK : This->resource.device->offscreenBuffer);
2818 checkGLcall("glReadBuffer");
2819 glCopyTexImage2D(GL_TEXTURE_2D,
2821 GL_RGBA,
2824 width,
2825 height,
2828 checkGLcall("glCopyTexImage2D");
2829 glReadBuffer(prevRead);
2830 LEAVE_GL();
2832 } else { /* bind the real texture, and make sure it up to date */
2833 surface_internal_preload(iface, SRGB_RGB);
2834 surface_bind_and_dirtify(This, FALSE);
2836 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2837 ENTER_GL();
2838 FIXME("Saving texture level %d width %d height %d\n", This->texture_level, width, height);
2839 glGetTexImage(GL_TEXTURE_2D, This->texture_level, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, allocatedMemory);
2840 checkGLcall("glGetTexImage");
2841 if (tmpTexture) {
2842 glBindTexture(GL_TEXTURE_2D, 0);
2843 glDeleteTextures(1, &tmpTexture);
2845 LEAVE_GL();
2847 f = fopen(filename, "w+");
2848 if (NULL == f) {
2849 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2850 return WINED3DERR_INVALIDCALL;
2852 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2853 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format_desc->format));
2854 /* TGA header */
2855 fputc(0,f);
2856 fputc(0,f);
2857 fputc(2,f);
2858 fputc(0,f);
2859 fputc(0,f);
2860 fputc(0,f);
2861 fputc(0,f);
2862 fputc(0,f);
2863 fputc(0,f);
2864 fputc(0,f);
2865 fputc(0,f);
2866 fputc(0,f);
2867 /* short width*/
2868 fwrite(&width,2,1,f);
2869 /* short height */
2870 fwrite(&height,2,1,f);
2871 /* format rgba */
2872 fputc(0x20,f);
2873 fputc(0x28,f);
2874 /* raw data */
2875 /* 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 */
2876 if(swapChain)
2877 textureRow = allocatedMemory + (width * (height - 1) *4);
2878 else
2879 textureRow = allocatedMemory;
2880 for (y = 0 ; y < height; y++) {
2881 for (i = 0; i < width; i++) {
2882 color = *((const DWORD*)textureRow);
2883 fputc((color >> 16) & 0xFF, f); /* B */
2884 fputc((color >> 8) & 0xFF, f); /* G */
2885 fputc((color >> 0) & 0xFF, f); /* R */
2886 fputc((color >> 24) & 0xFF, f); /* A */
2887 textureRow += 4;
2889 /* take two rows of the pointer to the texture memory */
2890 if(swapChain)
2891 (textureRow-= width << 3);
2894 TRACE("Closing file\n");
2895 fclose(f);
2897 if(swapChain) {
2898 IWineD3DSwapChain_Release(swapChain);
2900 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2901 return WINED3D_OK;
2904 static HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2905 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2906 HRESULT hr;
2908 TRACE("(%p) : Calling base function first\n", This);
2909 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2910 if(SUCCEEDED(hr)) {
2911 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2912 TRACE("(%p) : glFormat %d, glFormatInternal %d, glType %d\n", This, This->resource.format_desc->glFormat,
2913 This->resource.format_desc->glInternal, This->resource.format_desc->glType);
2915 return hr;
2918 static HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2919 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2921 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2922 WARN("Surface is locked or the HDC is in use\n");
2923 return WINED3DERR_INVALIDCALL;
2926 if(Mem && Mem != This->resource.allocatedMemory) {
2927 void *release = NULL;
2929 /* Do I have to copy the old surface content? */
2930 if(This->Flags & SFLAG_DIBSECTION) {
2931 /* Release the DC. No need to hold the critical section for the update
2932 * Thread because this thread runs only on front buffers, but this method
2933 * fails for render targets in the check above.
2935 SelectObject(This->hDC, This->dib.holdbitmap);
2936 DeleteDC(This->hDC);
2937 /* Release the DIB section */
2938 DeleteObject(This->dib.DIBsection);
2939 This->dib.bitmap_data = NULL;
2940 This->resource.allocatedMemory = NULL;
2941 This->hDC = NULL;
2942 This->Flags &= ~SFLAG_DIBSECTION;
2943 } else if(!(This->Flags & SFLAG_USERPTR)) {
2944 release = This->resource.heapMemory;
2945 This->resource.heapMemory = NULL;
2947 This->resource.allocatedMemory = Mem;
2948 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2950 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2951 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2953 /* For client textures opengl has to be notified */
2954 if(This->Flags & SFLAG_CLIENT) {
2955 surface_release_client_storage(iface);
2958 /* Now free the old memory if any */
2959 HeapFree(GetProcessHeap(), 0, release);
2960 } else if(This->Flags & SFLAG_USERPTR) {
2961 /* LockRect and GetDC will re-create the dib section and allocated memory */
2962 This->resource.allocatedMemory = NULL;
2963 /* HeapMemory should be NULL already */
2964 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2965 This->Flags &= ~SFLAG_USERPTR;
2967 if(This->Flags & SFLAG_CLIENT) {
2968 surface_release_client_storage(iface);
2971 return WINED3D_OK;
2974 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
2976 /* Flip the surface contents */
2977 /* Flip the DC */
2979 HDC tmp;
2980 tmp = front->hDC;
2981 front->hDC = back->hDC;
2982 back->hDC = tmp;
2985 /* Flip the DIBsection */
2987 HBITMAP tmp;
2988 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
2989 tmp = front->dib.DIBsection;
2990 front->dib.DIBsection = back->dib.DIBsection;
2991 back->dib.DIBsection = tmp;
2993 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
2994 else front->Flags &= ~SFLAG_DIBSECTION;
2995 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
2996 else back->Flags &= ~SFLAG_DIBSECTION;
2999 /* Flip the surface data */
3001 void* tmp;
3003 tmp = front->dib.bitmap_data;
3004 front->dib.bitmap_data = back->dib.bitmap_data;
3005 back->dib.bitmap_data = tmp;
3007 tmp = front->resource.allocatedMemory;
3008 front->resource.allocatedMemory = back->resource.allocatedMemory;
3009 back->resource.allocatedMemory = tmp;
3011 tmp = front->resource.heapMemory;
3012 front->resource.heapMemory = back->resource.heapMemory;
3013 back->resource.heapMemory = tmp;
3016 /* Flip the PBO */
3018 GLuint tmp_pbo = front->pbo;
3019 front->pbo = back->pbo;
3020 back->pbo = tmp_pbo;
3023 /* client_memory should not be different, but just in case */
3025 BOOL tmp;
3026 tmp = front->dib.client_memory;
3027 front->dib.client_memory = back->dib.client_memory;
3028 back->dib.client_memory = tmp;
3031 /* Flip the opengl texture */
3033 GLuint tmp;
3035 tmp = back->texture_name;
3036 back->texture_name = front->texture_name;
3037 front->texture_name = tmp;
3039 tmp = back->texture_name_srgb;
3040 back->texture_name_srgb = front->texture_name_srgb;
3041 front->texture_name_srgb = tmp;
3045 DWORD tmp_flags = back->Flags;
3046 back->Flags = front->Flags;
3047 front->Flags = tmp_flags;
3051 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
3052 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3053 IWineD3DSwapChainImpl *swapchain = NULL;
3054 HRESULT hr;
3055 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
3057 /* Flipping is only supported on RenderTargets and overlays*/
3058 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
3059 WARN("Tried to flip a non-render target, non-overlay surface\n");
3060 return WINEDDERR_NOTFLIPPABLE;
3063 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
3064 flip_surface(This, (IWineD3DSurfaceImpl *) override);
3066 /* Update the overlay if it is visible */
3067 if(This->overlay_dest) {
3068 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
3069 } else {
3070 return WINED3D_OK;
3074 if(override) {
3075 /* DDraw sets this for the X11 surfaces, so don't confuse the user
3076 * FIXME("(%p) Target override is not supported by now\n", This);
3077 * Additionally, it isn't really possible to support triple-buffering
3078 * properly on opengl at all
3082 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
3083 if(!swapchain) {
3084 ERR("Flipped surface is not on a swapchain\n");
3085 return WINEDDERR_NOTFLIPPABLE;
3088 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
3089 * and only d3d8 and d3d9 apps specify the presentation interval
3091 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
3092 /* Most common case first to avoid wasting time on all the other cases */
3093 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
3094 } else if(Flags & WINEDDFLIP_NOVSYNC) {
3095 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3096 } else if(Flags & WINEDDFLIP_INTERVAL2) {
3097 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
3098 } else if(Flags & WINEDDFLIP_INTERVAL3) {
3099 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
3100 } else {
3101 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
3104 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
3105 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
3106 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
3107 return hr;
3110 /* Does a direct frame buffer -> texture copy. Stretching is done
3111 * with single pixel copy calls
3113 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3114 const WINED3DRECT *srect, const WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
3116 IWineD3DDeviceImpl *myDevice = This->resource.device;
3117 float xrel, yrel;
3118 UINT row;
3119 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3120 struct wined3d_context *context;
3123 context = context_acquire(myDevice, SrcSurface, CTXUSAGE_BLIT);
3124 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3125 ENTER_GL();
3127 /* Bind the target texture */
3128 glBindTexture(This->texture_target, This->texture_name);
3129 checkGLcall("glBindTexture");
3130 if(surface_is_offscreen(SrcSurface)) {
3131 TRACE("Reading from an offscreen target\n");
3132 upsidedown = !upsidedown;
3133 glReadBuffer(myDevice->offscreenBuffer);
3135 else
3137 glReadBuffer(surface_get_gl_buffer(SrcSurface));
3139 checkGLcall("glReadBuffer");
3141 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
3142 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
3144 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3146 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3148 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
3149 ERR("Texture filtering not supported in direct blit\n");
3152 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
3153 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3155 ERR("Texture filtering not supported in direct blit\n");
3158 if (upsidedown
3159 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3160 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3162 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
3164 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3165 drect->x1 /*xoffset */, drect->y1 /* y offset */,
3166 srect->x1, Src->currentDesc.Height - srect->y2,
3167 drect->x2 - drect->x1, drect->y2 - drect->y1);
3168 } else {
3169 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
3170 /* I have to process this row by row to swap the image,
3171 * otherwise it would be upside down, so stretching in y direction
3172 * doesn't cost extra time
3174 * However, stretching in x direction can be avoided if not necessary
3176 for(row = drect->y1; row < drect->y2; row++) {
3177 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3179 /* Well, that stuff works, but it's very slow.
3180 * find a better way instead
3182 UINT col;
3184 for(col = drect->x1; col < drect->x2; col++) {
3185 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3186 drect->x1 + col /* x offset */, row /* y offset */,
3187 srect->x1 + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3189 } else {
3190 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3191 drect->x1 /* x offset */, row /* y offset */,
3192 srect->x1, yoffset - (int) (row * yrel), drect->x2-drect->x1, 1);
3196 checkGLcall("glCopyTexSubImage2D");
3198 LEAVE_GL();
3199 context_release(context);
3201 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3202 * path is never entered
3204 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3207 /* Uses the hardware to stretch and flip the image */
3208 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3209 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
3210 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
3212 IWineD3DDeviceImpl *myDevice = This->resource.device;
3213 GLuint src, backup = 0;
3214 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3215 float left, right, top, bottom; /* Texture coordinates */
3216 UINT fbwidth = Src->currentDesc.Width;
3217 UINT fbheight = Src->currentDesc.Height;
3218 struct wined3d_context *context;
3219 GLenum drawBuffer = GL_BACK;
3220 GLenum texture_target;
3221 BOOL noBackBufferBackup;
3222 BOOL src_offscreen;
3224 TRACE("Using hwstretch blit\n");
3225 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3226 context = context_acquire(myDevice, SrcSurface, CTXUSAGE_BLIT);
3227 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3229 src_offscreen = surface_is_offscreen(SrcSurface);
3230 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3231 if (!noBackBufferBackup && !Src->texture_name)
3233 /* Get it a description */
3234 surface_internal_preload(SrcSurface, SRGB_RGB);
3236 ENTER_GL();
3238 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3239 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3241 if (context->aux_buffers >= 2)
3243 /* Got more than one aux buffer? Use the 2nd aux buffer */
3244 drawBuffer = GL_AUX1;
3246 else if ((!src_offscreen || myDevice->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3248 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3249 drawBuffer = GL_AUX0;
3252 if(noBackBufferBackup) {
3253 glGenTextures(1, &backup);
3254 checkGLcall("glGenTextures");
3255 glBindTexture(GL_TEXTURE_2D, backup);
3256 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3257 texture_target = GL_TEXTURE_2D;
3258 } else {
3259 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3260 * we are reading from the back buffer, the backup can be used as source texture
3262 texture_target = Src->texture_target;
3263 glBindTexture(texture_target, Src->texture_name);
3264 checkGLcall("glBindTexture(texture_target, Src->texture_name)");
3265 glEnable(texture_target);
3266 checkGLcall("glEnable(texture_target)");
3268 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3269 Src->Flags &= ~SFLAG_INTEXTURE;
3272 if (src_offscreen)
3274 TRACE("Reading from an offscreen target\n");
3275 upsidedown = !upsidedown;
3276 glReadBuffer(myDevice->offscreenBuffer);
3278 else
3280 glReadBuffer(surface_get_gl_buffer(SrcSurface));
3283 /* TODO: Only back up the part that will be overwritten */
3284 glCopyTexSubImage2D(texture_target, 0,
3285 0, 0 /* read offsets */,
3286 0, 0,
3287 fbwidth,
3288 fbheight);
3290 checkGLcall("glCopyTexSubImage2D");
3292 /* No issue with overriding these - the sampler is dirty due to blit usage */
3293 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3294 wined3d_gl_mag_filter(magLookup, Filter));
3295 checkGLcall("glTexParameteri");
3296 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3297 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
3298 checkGLcall("glTexParameteri");
3300 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
3301 src = backup ? backup : Src->texture_name;
3302 } else {
3303 glReadBuffer(GL_FRONT);
3304 checkGLcall("glReadBuffer(GL_FRONT)");
3306 glGenTextures(1, &src);
3307 checkGLcall("glGenTextures(1, &src)");
3308 glBindTexture(GL_TEXTURE_2D, src);
3309 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
3311 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
3312 * out for power of 2 sizes
3314 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
3315 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3316 checkGLcall("glTexImage2D");
3317 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
3318 0, 0 /* read offsets */,
3319 0, 0,
3320 fbwidth,
3321 fbheight);
3323 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3324 checkGLcall("glTexParameteri");
3325 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3326 checkGLcall("glTexParameteri");
3328 glReadBuffer(GL_BACK);
3329 checkGLcall("glReadBuffer(GL_BACK)");
3331 if(texture_target != GL_TEXTURE_2D) {
3332 glDisable(texture_target);
3333 glEnable(GL_TEXTURE_2D);
3334 texture_target = GL_TEXTURE_2D;
3337 checkGLcall("glEnd and previous");
3339 left = srect->x1;
3340 right = srect->x2;
3342 if(upsidedown) {
3343 top = Src->currentDesc.Height - srect->y1;
3344 bottom = Src->currentDesc.Height - srect->y2;
3345 } else {
3346 top = Src->currentDesc.Height - srect->y2;
3347 bottom = Src->currentDesc.Height - srect->y1;
3350 if(Src->Flags & SFLAG_NORMCOORD) {
3351 left /= Src->pow2Width;
3352 right /= Src->pow2Width;
3353 top /= Src->pow2Height;
3354 bottom /= Src->pow2Height;
3357 /* draw the source texture stretched and upside down. The correct surface is bound already */
3358 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3359 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3361 context_set_draw_buffer(context, drawBuffer);
3362 glReadBuffer(drawBuffer);
3364 glBegin(GL_QUADS);
3365 /* bottom left */
3366 glTexCoord2f(left, bottom);
3367 glVertex2i(0, fbheight);
3369 /* top left */
3370 glTexCoord2f(left, top);
3371 glVertex2i(0, fbheight - drect->y2 - drect->y1);
3373 /* top right */
3374 glTexCoord2f(right, top);
3375 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
3377 /* bottom right */
3378 glTexCoord2f(right, bottom);
3379 glVertex2i(drect->x2 - drect->x1, fbheight);
3380 glEnd();
3381 checkGLcall("glEnd and previous");
3383 if (texture_target != This->texture_target)
3385 glDisable(texture_target);
3386 glEnable(This->texture_target);
3387 texture_target = This->texture_target;
3390 /* Now read the stretched and upside down image into the destination texture */
3391 glBindTexture(texture_target, This->texture_name);
3392 checkGLcall("glBindTexture");
3393 glCopyTexSubImage2D(texture_target,
3395 drect->x1, drect->y1, /* xoffset, yoffset */
3396 0, 0, /* We blitted the image to the origin */
3397 drect->x2 - drect->x1, drect->y2 - drect->y1);
3398 checkGLcall("glCopyTexSubImage2D");
3400 if(drawBuffer == GL_BACK) {
3401 /* Write the back buffer backup back */
3402 if(backup) {
3403 if(texture_target != GL_TEXTURE_2D) {
3404 glDisable(texture_target);
3405 glEnable(GL_TEXTURE_2D);
3406 texture_target = GL_TEXTURE_2D;
3408 glBindTexture(GL_TEXTURE_2D, backup);
3409 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3410 } else {
3411 if (texture_target != Src->texture_target)
3413 glDisable(texture_target);
3414 glEnable(Src->texture_target);
3415 texture_target = Src->texture_target;
3417 glBindTexture(Src->texture_target, Src->texture_name);
3418 checkGLcall("glBindTexture(Src->texture_target, Src->texture_name)");
3421 glBegin(GL_QUADS);
3422 /* top left */
3423 glTexCoord2f(0.0f, (float)fbheight / (float)Src->pow2Height);
3424 glVertex2i(0, 0);
3426 /* bottom left */
3427 glTexCoord2f(0.0f, 0.0f);
3428 glVertex2i(0, fbheight);
3430 /* bottom right */
3431 glTexCoord2f((float)fbwidth / (float)Src->pow2Width, 0.0f);
3432 glVertex2i(fbwidth, Src->currentDesc.Height);
3434 /* top right */
3435 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
3436 glVertex2i(fbwidth, 0);
3437 glEnd();
3439 glDisable(texture_target);
3440 checkGLcall("glDisable(texture_target)");
3442 /* Cleanup */
3443 if (src != Src->texture_name && src != backup)
3445 glDeleteTextures(1, &src);
3446 checkGLcall("glDeleteTextures(1, &src)");
3448 if(backup) {
3449 glDeleteTextures(1, &backup);
3450 checkGLcall("glDeleteTextures(1, &backup)");
3453 LEAVE_GL();
3454 context_release(context);
3456 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3457 * path is never entered
3459 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3462 /* Not called from the VTable */
3463 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3464 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx,
3465 WINED3DTEXTUREFILTERTYPE Filter)
3467 IWineD3DDeviceImpl *myDevice = This->resource.device;
3468 WINED3DRECT rect;
3469 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3470 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3472 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3474 /* Get the swapchain. One of the surfaces has to be a primary surface */
3475 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3476 WARN("Destination is in sysmem, rejecting gl blt\n");
3477 return WINED3DERR_INVALIDCALL;
3479 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3480 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3481 if(Src) {
3482 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3483 WARN("Src is in sysmem, rejecting gl blt\n");
3484 return WINED3DERR_INVALIDCALL;
3486 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3487 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3490 /* Early sort out of cases where no render target is used */
3491 if(!dstSwapchain && !srcSwapchain &&
3492 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3493 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3494 return WINED3DERR_INVALIDCALL;
3497 /* No destination color keying supported */
3498 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3499 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3500 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3501 return WINED3DERR_INVALIDCALL;
3504 if (DestRect) {
3505 rect.x1 = DestRect->left;
3506 rect.y1 = DestRect->top;
3507 rect.x2 = DestRect->right;
3508 rect.y2 = DestRect->bottom;
3509 } else {
3510 rect.x1 = 0;
3511 rect.y1 = 0;
3512 rect.x2 = This->currentDesc.Width;
3513 rect.y2 = This->currentDesc.Height;
3516 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3517 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3518 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3519 /* Half-life does a Blt from the back buffer to the front buffer,
3520 * Full surface size, no flags... Use present instead
3522 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3525 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3526 while(1)
3528 RECT mySrcRect;
3529 TRACE("Looking if a Present can be done...\n");
3530 /* Source Rectangle must be full surface */
3531 if( SrcRect ) {
3532 if(SrcRect->left != 0 || SrcRect->top != 0 ||
3533 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
3534 TRACE("No, Source rectangle doesn't match\n");
3535 break;
3538 mySrcRect.left = 0;
3539 mySrcRect.top = 0;
3540 mySrcRect.right = Src->currentDesc.Width;
3541 mySrcRect.bottom = Src->currentDesc.Height;
3543 /* No stretching may occur */
3544 if(mySrcRect.right != rect.x2 - rect.x1 ||
3545 mySrcRect.bottom != rect.y2 - rect.y1) {
3546 TRACE("No, stretching is done\n");
3547 break;
3550 /* Destination must be full surface or match the clipping rectangle */
3551 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3553 RECT cliprect;
3554 POINT pos[2];
3555 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3556 pos[0].x = rect.x1;
3557 pos[0].y = rect.y1;
3558 pos[1].x = rect.x2;
3559 pos[1].y = rect.y2;
3560 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3561 pos, 2);
3563 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3564 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3566 TRACE("No, dest rectangle doesn't match(clipper)\n");
3567 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3568 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3569 break;
3572 else
3574 if(rect.x1 != 0 || rect.y1 != 0 ||
3575 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3576 TRACE("No, dest rectangle doesn't match(surface size)\n");
3577 break;
3581 TRACE("Yes\n");
3583 /* These flags are unimportant for the flag check, remove them */
3584 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3585 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3587 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3588 * take very long, while a flip is fast.
3589 * This applies to Half-Life, which does such Blts every time it finished
3590 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3591 * menu. This is also used by all apps when they do windowed rendering
3593 * The problem is that flipping is not really the same as copying. After a
3594 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3595 * untouched. Therefore it's necessary to override the swap effect
3596 * and to set it back after the flip.
3598 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3599 * testcases.
3602 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3603 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3605 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3606 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3608 dstSwapchain->presentParms.SwapEffect = orig_swap;
3610 return WINED3D_OK;
3612 break;
3615 TRACE("Unsupported blit between buffers on the same swapchain\n");
3616 return WINED3DERR_INVALIDCALL;
3617 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3618 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3619 return WINED3DERR_INVALIDCALL;
3620 } else if(dstSwapchain && srcSwapchain) {
3621 FIXME("Implement hardware blit between two different swapchains\n");
3622 return WINED3DERR_INVALIDCALL;
3623 } else if(dstSwapchain) {
3624 if(SrcSurface == myDevice->render_targets[0]) {
3625 TRACE("Blit from active render target to a swapchain\n");
3626 /* Handled with regular texture -> swapchain blit */
3628 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3629 FIXME("Implement blit from a swapchain to the active render target\n");
3630 return WINED3DERR_INVALIDCALL;
3633 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3634 /* Blit from render target to texture */
3635 WINED3DRECT srect;
3636 BOOL upsideDown, stretchx;
3637 BOOL paletteOverride = FALSE;
3639 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3640 TRACE("Color keying not supported by frame buffer to texture blit\n");
3641 return WINED3DERR_INVALIDCALL;
3642 /* Destination color key is checked above */
3645 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3646 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3648 if(SrcRect) {
3649 if(SrcRect->top < SrcRect->bottom) {
3650 srect.y1 = SrcRect->top;
3651 srect.y2 = SrcRect->bottom;
3652 upsideDown = FALSE;
3653 } else {
3654 srect.y1 = SrcRect->bottom;
3655 srect.y2 = SrcRect->top;
3656 upsideDown = TRUE;
3658 srect.x1 = SrcRect->left;
3659 srect.x2 = SrcRect->right;
3660 } else {
3661 srect.x1 = 0;
3662 srect.y1 = 0;
3663 srect.x2 = Src->currentDesc.Width;
3664 srect.y2 = Src->currentDesc.Height;
3665 upsideDown = FALSE;
3667 if(rect.x1 > rect.x2) {
3668 UINT tmp = rect.x2;
3669 rect.x2 = rect.x1;
3670 rect.x1 = tmp;
3671 upsideDown = !upsideDown;
3674 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3675 stretchx = TRUE;
3676 } else {
3677 stretchx = FALSE;
3680 /* When blitting from a render target a texture, the texture isn't required to have a palette.
3681 * In this case grab the palette from the render target. */
3682 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT && !This->palette)
3684 paletteOverride = TRUE;
3685 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3686 This->palette = Src->palette;
3689 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3690 * flip the image nor scale it.
3692 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3693 * -> If the app wants a image width an unscaled width, copy it line per line
3694 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3695 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3696 * back buffer. This is slower than reading line per line, thus not used for flipping
3697 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3698 * pixel by pixel
3700 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3701 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3702 * backends.
3704 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
3705 && myDevice->adapter->gl_info.fbo_ops.glBlitFramebuffer
3706 && surface_can_stretch_rect(Src, This))
3708 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3709 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3710 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3711 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3712 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3713 fb_copy_to_texture_direct(This, SrcSurface, &srect, &rect, upsideDown, Filter);
3714 } else {
3715 TRACE("Using hardware stretching to flip / stretch the texture\n");
3716 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3719 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3720 if(paletteOverride)
3721 This->palette = NULL;
3723 if(!(This->Flags & SFLAG_DONOTFREE)) {
3724 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3725 This->resource.allocatedMemory = NULL;
3726 This->resource.heapMemory = NULL;
3727 } else {
3728 This->Flags &= ~SFLAG_INSYSMEM;
3731 return WINED3D_OK;
3732 } else if(Src) {
3733 /* Blit from offscreen surface to render target */
3734 float glTexCoord[4];
3735 DWORD oldCKeyFlags = Src->CKeyFlags;
3736 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3737 struct wined3d_context *context;
3738 RECT SourceRectangle;
3739 BOOL paletteOverride = FALSE;
3741 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3743 if(SrcRect) {
3744 SourceRectangle.left = SrcRect->left;
3745 SourceRectangle.right = SrcRect->right;
3746 SourceRectangle.top = SrcRect->top;
3747 SourceRectangle.bottom = SrcRect->bottom;
3748 } else {
3749 SourceRectangle.left = 0;
3750 SourceRectangle.right = Src->currentDesc.Width;
3751 SourceRectangle.top = 0;
3752 SourceRectangle.bottom = Src->currentDesc.Height;
3755 /* When blitting from an offscreen surface to a rendertarget, the source
3756 * surface is not required to have a palette. Our rendering / conversion
3757 * code further down the road retrieves the palette from the surface, so
3758 * it must have a palette set. */
3759 if (Src->resource.format_desc->format == WINED3DFMT_P8_UINT && !Src->palette)
3761 paletteOverride = TRUE;
3762 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3763 Src->palette = This->palette;
3766 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
3767 && myDevice->adapter->gl_info.fbo_ops.glBlitFramebuffer
3768 && !(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3769 && surface_can_stretch_rect(Src, This))
3771 TRACE("Using stretch_rect_fbo\n");
3772 /* The source is always a texture, but never the currently active render target, and the texture
3773 * contents are never upside down
3775 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3776 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3778 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3779 if(paletteOverride)
3780 Src->palette = NULL;
3781 return WINED3D_OK;
3784 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3785 /* Fall back to software */
3786 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3787 SourceRectangle.left, SourceRectangle.top,
3788 SourceRectangle.right, SourceRectangle.bottom);
3789 return WINED3DERR_INVALIDCALL;
3792 /* Color keying: Check if we have to do a color keyed blt,
3793 * and if not check if a color key is activated.
3795 * Just modify the color keying parameters in the surface and restore them afterwards
3796 * The surface keeps track of the color key last used to load the opengl surface.
3797 * PreLoad will catch the change to the flags and color key and reload if necessary.
3799 if(Flags & WINEDDBLT_KEYSRC) {
3800 /* Use color key from surface */
3801 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3802 /* Use color key from DDBltFx */
3803 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3804 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3805 } else {
3806 /* Do not use color key */
3807 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3810 /* Now load the surface */
3811 surface_internal_preload((IWineD3DSurface *) Src, SRGB_RGB);
3813 /* Activate the destination context, set it up for blitting */
3814 context = context_acquire(myDevice, (IWineD3DSurface *)This, CTXUSAGE_BLIT);
3816 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3817 * while OpenGL coordinates are window relative.
3818 * Also beware of the origin difference(top left vs bottom left).
3819 * Also beware that the front buffer's surface size is screen width x screen height,
3820 * whereas the real gl drawable size is the size of the window.
3822 if (dstSwapchain && (IWineD3DSurface *)This == dstSwapchain->frontBuffer) {
3823 RECT windowsize;
3824 POINT offset = {0,0};
3825 UINT h;
3826 ClientToScreen(dstSwapchain->win_handle, &offset);
3827 GetClientRect(dstSwapchain->win_handle, &windowsize);
3828 h = windowsize.bottom - windowsize.top;
3829 rect.x1 -= offset.x; rect.x2 -=offset.x;
3830 rect.y1 -= offset.y; rect.y2 -=offset.y;
3831 rect.y1 += This->currentDesc.Height - h; rect.y2 += This->currentDesc.Height - h;
3834 if (!is_identity_fixup(This->resource.format_desc->color_fixup))
3836 FIXME("Destination format %s has a fixup, this is not supported.\n",
3837 debug_d3dformat(This->resource.format_desc->format));
3838 dump_color_fixup_desc(This->resource.format_desc->color_fixup);
3841 if (!myDevice->blitter->color_fixup_supported(Src->resource.format_desc->color_fixup))
3843 FIXME("Source format %s has an unsupported fixup:\n",
3844 debug_d3dformat(Src->resource.format_desc->format));
3845 dump_color_fixup_desc(Src->resource.format_desc->color_fixup);
3848 myDevice->blitter->set_shader((IWineD3DDevice *) myDevice, Src->resource.format_desc,
3849 Src->texture_target, Src->pow2Width, Src->pow2Height);
3851 ENTER_GL();
3853 /* Bind the texture */
3854 glBindTexture(Src->texture_target, Src->texture_name);
3855 checkGLcall("glBindTexture");
3857 /* Filtering for StretchRect */
3858 glTexParameteri(Src->texture_target, GL_TEXTURE_MAG_FILTER,
3859 wined3d_gl_mag_filter(magLookup, Filter));
3860 checkGLcall("glTexParameteri");
3861 glTexParameteri(Src->texture_target, GL_TEXTURE_MIN_FILTER,
3862 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
3863 checkGLcall("glTexParameteri");
3864 glTexParameteri(Src->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3865 glTexParameteri(Src->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3866 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3867 checkGLcall("glTexEnvi");
3869 /* This is for color keying */
3870 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3871 glEnable(GL_ALPHA_TEST);
3872 checkGLcall("glEnable(GL_ALPHA_TEST)");
3874 /* When the primary render target uses P8, the alpha component contains the palette index.
3875 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3876 * should be masked away have alpha set to 0. */
3877 if(primary_render_target_is_p8(myDevice))
3878 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
3879 else
3880 glAlphaFunc(GL_NOTEQUAL, 0.0f);
3881 checkGLcall("glAlphaFunc");
3882 } else {
3883 glDisable(GL_ALPHA_TEST);
3884 checkGLcall("glDisable(GL_ALPHA_TEST)");
3887 /* Draw a textured quad
3889 glBegin(GL_QUADS);
3891 glColor3f(1.0f, 1.0f, 1.0f);
3892 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3893 glVertex3f(rect.x1, rect.y1, 0.0f);
3895 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3896 glVertex3f(rect.x1, rect.y2, 0.0f);
3898 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3899 glVertex3f(rect.x2, rect.y2, 0.0f);
3901 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3902 glVertex3f(rect.x2, rect.y1, 0.0f);
3904 glEnd();
3905 checkGLcall("glEnd");
3907 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3908 glDisable(GL_ALPHA_TEST);
3909 checkGLcall("glDisable(GL_ALPHA_TEST)");
3912 glBindTexture(Src->texture_target, 0);
3913 checkGLcall("glBindTexture(Src->texture_target, 0)");
3915 /* Restore the color key parameters */
3916 Src->CKeyFlags = oldCKeyFlags;
3917 Src->SrcBltCKey = oldBltCKey;
3919 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3920 if(paletteOverride)
3921 Src->palette = NULL;
3923 LEAVE_GL();
3925 /* Leave the opengl state valid for blitting */
3926 myDevice->blitter->unset_shader((IWineD3DDevice *) myDevice);
3928 /* Flush in case the drawable is used by multiple GL contexts */
3929 if(dstSwapchain && (This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer || dstSwapchain->num_contexts >= 2))
3930 wglFlush();
3932 context_release(context);
3934 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3935 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3936 * is outdated now
3938 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3940 return WINED3D_OK;
3941 } else {
3942 /* Source-Less Blit to render target */
3943 if (Flags & WINEDDBLT_COLORFILL) {
3944 /* This is easy to handle for the D3D Device... */
3945 DWORD color;
3947 TRACE("Colorfill\n");
3949 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3950 must be true if we are here */
3951 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3952 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3953 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3954 TRACE("Surface is higher back buffer, falling back to software\n");
3955 return WINED3DERR_INVALIDCALL;
3958 /* The color as given in the Blt function is in the format of the frame-buffer...
3959 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3961 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT)
3963 DWORD alpha;
3965 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3966 else alpha = 0xFF000000;
3968 if (This->palette) {
3969 color = (alpha |
3970 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3971 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3972 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3973 } else {
3974 color = alpha;
3977 else if (This->resource.format_desc->format == WINED3DFMT_B5G6R5_UNORM)
3979 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3980 color = 0xFFFFFFFF;
3981 } else {
3982 color = ((0xFF000000) |
3983 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3984 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3985 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3988 else if (This->resource.format_desc->format == WINED3DFMT_B8G8R8_UNORM
3989 || This->resource.format_desc->format == WINED3DFMT_B8G8R8X8_UNORM)
3991 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3993 else if (This->resource.format_desc->format == WINED3DFMT_B8G8R8A8_UNORM)
3995 color = DDBltFx->u5.dwFillColor;
3997 else {
3998 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3999 return WINED3DERR_INVALIDCALL;
4002 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
4003 IWineD3DDeviceImpl_ClearSurface(myDevice, This, 1 /* Number of rectangles */,
4004 &rect, WINED3DCLEAR_TARGET, color, 0.0f /* Z */, 0 /* Stencil */);
4005 return WINED3D_OK;
4009 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
4010 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
4011 return WINED3DERR_INVALIDCALL;
4014 static HRESULT IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, const RECT *DestRect,
4015 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx)
4017 IWineD3DDeviceImpl *myDevice = This->resource.device;
4018 float depth;
4020 if (Flags & WINEDDBLT_DEPTHFILL) {
4021 switch(This->resource.format_desc->format)
4023 case WINED3DFMT_D16_UNORM:
4024 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
4025 break;
4026 case WINED3DFMT_S1_UINT_D15_UNORM:
4027 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
4028 break;
4029 case WINED3DFMT_D24_UNORM_S8_UINT:
4030 case WINED3DFMT_X8D24_UNORM:
4031 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
4032 break;
4033 case WINED3DFMT_D32_UNORM:
4034 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
4035 break;
4036 default:
4037 depth = 0.0f;
4038 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format_desc->format));
4041 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
4042 DestRect == NULL ? 0 : 1,
4043 (const WINED3DRECT *)DestRect,
4044 WINED3DCLEAR_ZBUFFER,
4045 0x00000000,
4046 depth,
4047 0x00000000);
4050 FIXME("(%p): Unsupp depthstencil blit\n", This);
4051 return WINED3DERR_INVALIDCALL;
4054 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
4055 const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
4056 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4057 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
4058 IWineD3DDeviceImpl *myDevice = This->resource.device;
4060 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
4061 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
4063 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
4065 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
4066 return WINEDDERR_SURFACEBUSY;
4069 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
4070 * except depth blits, which seem to work
4072 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
4073 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
4074 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
4075 return WINED3DERR_INVALIDCALL;
4076 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
4077 TRACE("Z Blit override handled the blit\n");
4078 return WINED3D_OK;
4082 /* Special cases for RenderTargets */
4083 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
4084 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
4085 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
4088 /* For the rest call the X11 surface implementation.
4089 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
4090 * other Blts are rather rare
4092 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
4095 static HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
4096 IWineD3DSurface *Source, const RECT *rsrc, DWORD trans)
4098 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4099 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
4100 IWineD3DDeviceImpl *myDevice = This->resource.device;
4102 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
4104 if ( (This->Flags & SFLAG_LOCKED) || (srcImpl->Flags & SFLAG_LOCKED))
4106 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
4107 return WINEDDERR_SURFACEBUSY;
4110 if(myDevice->inScene &&
4111 (iface == myDevice->stencilBufferTarget ||
4112 (Source == myDevice->stencilBufferTarget))) {
4113 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
4114 return WINED3DERR_INVALIDCALL;
4117 /* Special cases for RenderTargets */
4118 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
4119 (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) ) {
4121 RECT SrcRect, DstRect;
4122 DWORD Flags=0;
4124 if(rsrc) {
4125 SrcRect.left = rsrc->left;
4126 SrcRect.top= rsrc->top;
4127 SrcRect.bottom = rsrc->bottom;
4128 SrcRect.right = rsrc->right;
4129 } else {
4130 SrcRect.left = 0;
4131 SrcRect.top = 0;
4132 SrcRect.right = srcImpl->currentDesc.Width;
4133 SrcRect.bottom = srcImpl->currentDesc.Height;
4136 DstRect.left = dstx;
4137 DstRect.top=dsty;
4138 DstRect.right = dstx + SrcRect.right - SrcRect.left;
4139 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
4141 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
4142 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
4143 Flags |= WINEDDBLT_KEYSRC;
4144 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
4145 Flags |= WINEDDBLT_KEYDEST;
4146 if(trans & WINEDDBLTFAST_WAIT)
4147 Flags |= WINEDDBLT_WAIT;
4148 if(trans & WINEDDBLTFAST_DONOTWAIT)
4149 Flags |= WINEDDBLT_DONOTWAIT;
4151 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
4155 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
4158 static HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface)
4160 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4161 RGBQUAD col[256];
4162 IWineD3DPaletteImpl *pal = This->palette;
4163 unsigned int n;
4164 TRACE("(%p)\n", This);
4166 if (!pal) return WINED3D_OK;
4168 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
4169 || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
4171 int bpp;
4172 GLenum format, internal, type;
4173 CONVERT_TYPES convert;
4175 /* Check if we are using a RTL mode which uses texturing for uploads */
4176 BOOL use_texture = (wined3d_settings.rendertargetlock_mode == RTL_READTEX);
4178 /* Check if we have hardware palette conversion if we have convert is set to NO_CONVERSION */
4179 d3dfmt_get_conv(This, TRUE, use_texture, &format, &internal, &type, &convert, &bpp, FALSE);
4181 if((This->resource.usage & WINED3DUSAGE_RENDERTARGET) && (convert == NO_CONVERSION))
4183 IWineD3DDeviceImpl *device = This->resource.device;
4184 struct wined3d_context *context;
4186 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
4187 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
4189 /* We want to force a palette refresh, so mark the drawable as not being up to date */
4190 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
4192 /* Re-upload the palette */
4193 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4194 d3dfmt_p8_upload_palette(iface, convert);
4195 context_release(context);
4196 } else {
4197 if(!(This->Flags & SFLAG_INSYSMEM)) {
4198 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
4199 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
4201 TRACE("Dirtifying surface\n");
4202 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
4206 if(This->Flags & SFLAG_DIBSECTION) {
4207 TRACE("(%p): Updating the hdc's palette\n", This);
4208 for (n=0; n<256; n++) {
4209 col[n].rgbRed = pal->palents[n].peRed;
4210 col[n].rgbGreen = pal->palents[n].peGreen;
4211 col[n].rgbBlue = pal->palents[n].peBlue;
4212 col[n].rgbReserved = 0;
4214 SetDIBColorTable(This->hDC, 0, 256, col);
4217 /* Propagate the changes to the drawable when we have a palette. */
4218 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
4219 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
4221 return WINED3D_OK;
4224 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
4225 /** Check against the maximum texture sizes supported by the video card **/
4226 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4227 const struct wined3d_gl_info *gl_info = &This->resource.device->adapter->gl_info;
4228 unsigned int pow2Width, pow2Height;
4230 This->texture_name = 0;
4231 This->texture_target = GL_TEXTURE_2D;
4233 /* Non-power2 support */
4234 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINE_NORMALIZED_TEXRECT])
4236 pow2Width = This->currentDesc.Width;
4237 pow2Height = This->currentDesc.Height;
4239 else
4241 /* Find the nearest pow2 match */
4242 pow2Width = pow2Height = 1;
4243 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
4244 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
4246 This->pow2Width = pow2Width;
4247 This->pow2Height = pow2Height;
4249 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
4250 /** TODO: add support for non power two compressed textures **/
4251 if (This->resource.format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
4253 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
4254 This, This->currentDesc.Width, This->currentDesc.Height);
4255 return WINED3DERR_NOTAVAILABLE;
4259 if(pow2Width != This->currentDesc.Width ||
4260 pow2Height != This->currentDesc.Height) {
4261 This->Flags |= SFLAG_NONPOW2;
4264 TRACE("%p\n", This);
4265 if ((This->pow2Width > gl_info->limits.texture_size || This->pow2Height > gl_info->limits.texture_size)
4266 && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
4268 /* one of three options
4269 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)
4270 2: Set the texture to the maximum size (bad idea)
4271 3: WARN and return WINED3DERR_NOTAVAILABLE;
4272 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.
4274 WARN("(%p) Creating an oversized surface: %ux%u (texture is %ux%u)\n",
4275 This, This->pow2Width, This->pow2Height, This->currentDesc.Width, This->currentDesc.Height);
4276 This->Flags |= SFLAG_OVERSIZE;
4278 /* This will be initialized on the first blt */
4279 This->glRect.left = 0;
4280 This->glRect.top = 0;
4281 This->glRect.right = 0;
4282 This->glRect.bottom = 0;
4283 } else {
4284 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
4285 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
4286 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
4287 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
4289 if (This->Flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
4290 && !(This->resource.format_desc->format == WINED3DFMT_P8_UINT
4291 && gl_info->supported[EXT_PALETTED_TEXTURE]
4292 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
4294 This->texture_target = GL_TEXTURE_RECTANGLE_ARB;
4295 This->pow2Width = This->currentDesc.Width;
4296 This->pow2Height = This->currentDesc.Height;
4297 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
4300 /* No oversize, gl rect is the full texture size */
4301 This->Flags &= ~SFLAG_OVERSIZE;
4302 This->glRect.left = 0;
4303 This->glRect.top = 0;
4304 This->glRect.right = This->pow2Width;
4305 This->glRect.bottom = This->pow2Height;
4308 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4309 switch(wined3d_settings.offscreen_rendering_mode) {
4310 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4311 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4312 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4316 This->Flags |= SFLAG_INSYSMEM;
4318 return WINED3D_OK;
4321 struct depth_blt_info
4323 GLenum binding;
4324 GLenum bind_target;
4325 enum tex_types tex_type;
4326 GLfloat coords[4][3];
4329 static void surface_get_depth_blt_info(GLenum target, GLsizei w, GLsizei h, struct depth_blt_info *info)
4331 GLfloat (*coords)[3] = info->coords;
4333 switch (target)
4335 default:
4336 FIXME("Unsupported texture target %#x\n", target);
4337 /* Fall back to GL_TEXTURE_2D */
4338 case GL_TEXTURE_2D:
4339 info->binding = GL_TEXTURE_BINDING_2D;
4340 info->bind_target = GL_TEXTURE_2D;
4341 info->tex_type = tex_2d;
4342 coords[0][0] = 0.0f; coords[0][1] = 1.0f; coords[0][2] = 0.0f;
4343 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 0.0f;
4344 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4345 coords[3][0] = 1.0f; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4346 break;
4348 case GL_TEXTURE_RECTANGLE_ARB:
4349 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
4350 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
4351 info->tex_type = tex_rect;
4352 coords[0][0] = 0.0f; coords[0][1] = h; coords[0][2] = 0.0f;
4353 coords[1][0] = w; coords[1][1] = h; coords[1][2] = 0.0f;
4354 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4355 coords[3][0] = w; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4356 break;
4358 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4359 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4360 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4361 info->tex_type = tex_cube;
4362 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4363 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4364 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4365 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4367 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4368 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4369 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4370 info->tex_type = tex_cube;
4371 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4372 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4373 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4374 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4376 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4377 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4378 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4379 info->tex_type = tex_cube;
4380 coords[0][0] = -1.0f; coords[0][1] = 1.0f; coords[0][2] = 1.0f;
4381 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 1.0f;
4382 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4383 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4385 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4386 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4387 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4388 info->tex_type = tex_cube;
4389 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4390 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4391 coords[2][0] = -1.0f; coords[2][1] = -1.0f; coords[2][2] = 1.0f;
4392 coords[3][0] = 1.0f; coords[3][1] = -1.0f; coords[3][2] = 1.0f;
4394 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4395 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4396 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4397 info->tex_type = tex_cube;
4398 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4399 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4400 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4401 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4403 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4404 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4405 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4406 info->tex_type = tex_cube;
4407 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4408 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4409 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4410 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4414 /* GL locking is done by the caller */
4415 static void surface_depth_blt(IWineD3DSurfaceImpl *This, GLuint texture, GLsizei w, GLsizei h, GLenum target)
4417 IWineD3DDeviceImpl *device = This->resource.device;
4418 struct depth_blt_info info;
4419 GLint old_binding = 0;
4421 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4423 glDisable(GL_CULL_FACE);
4424 glDisable(GL_BLEND);
4425 glDisable(GL_ALPHA_TEST);
4426 glDisable(GL_SCISSOR_TEST);
4427 glDisable(GL_STENCIL_TEST);
4428 glEnable(GL_DEPTH_TEST);
4429 glDepthFunc(GL_ALWAYS);
4430 glDepthMask(GL_TRUE);
4431 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4432 glViewport(0, 0, w, h);
4434 surface_get_depth_blt_info(target, w, h, &info);
4435 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4436 glGetIntegerv(info.binding, &old_binding);
4437 glBindTexture(info.bind_target, texture);
4439 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4441 glBegin(GL_TRIANGLE_STRIP);
4442 glTexCoord3fv(info.coords[0]);
4443 glVertex2f(-1.0f, -1.0f);
4444 glTexCoord3fv(info.coords[1]);
4445 glVertex2f(1.0f, -1.0f);
4446 glTexCoord3fv(info.coords[2]);
4447 glVertex2f(-1.0f, 1.0f);
4448 glTexCoord3fv(info.coords[3]);
4449 glVertex2f(1.0f, 1.0f);
4450 glEnd();
4452 glBindTexture(info.bind_target, old_binding);
4454 glPopAttrib();
4456 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4459 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
4460 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4462 TRACE("(%p) New location %#x\n", This, location);
4464 if (location & ~SFLAG_DS_LOCATIONS) {
4465 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
4468 This->Flags &= ~SFLAG_DS_LOCATIONS;
4469 This->Flags |= location;
4472 /* Context activation is done by the caller. */
4473 void surface_load_ds_location(IWineD3DSurface *iface, struct wined3d_context *context, DWORD location)
4475 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4476 IWineD3DDeviceImpl *device = This->resource.device;
4477 const struct wined3d_gl_info *gl_info = context->gl_info;
4479 TRACE("(%p) New location %#x\n", This, location);
4481 /* TODO: Make this work for modes other than FBO */
4482 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4484 if (This->Flags & location) {
4485 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
4486 return;
4489 if (This->current_renderbuffer) {
4490 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
4491 return;
4494 if (location == SFLAG_DS_OFFSCREEN) {
4495 if (This->Flags & SFLAG_DS_ONSCREEN) {
4496 GLint old_binding = 0;
4497 GLenum bind_target;
4499 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
4501 ENTER_GL();
4503 if (!device->depth_blt_texture) {
4504 glGenTextures(1, &device->depth_blt_texture);
4507 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4508 * directly on the FBO texture. That's because we need to flip. */
4509 context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4510 if (This->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4512 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4513 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4514 } else {
4515 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4516 bind_target = GL_TEXTURE_2D;
4518 glBindTexture(bind_target, device->depth_blt_texture);
4519 glCopyTexImage2D(bind_target, This->texture_level, This->resource.format_desc->glInternal,
4520 0, 0, This->currentDesc.Width, This->currentDesc.Height, 0);
4521 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4522 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4523 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4524 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4525 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4526 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4527 glBindTexture(bind_target, old_binding);
4529 /* Setup the destination */
4530 if (!device->depth_blt_rb) {
4531 gl_info->fbo_ops.glGenRenderbuffers(1, &device->depth_blt_rb);
4532 checkGLcall("glGenRenderbuffersEXT");
4534 if (device->depth_blt_rb_w != This->currentDesc.Width
4535 || device->depth_blt_rb_h != This->currentDesc.Height) {
4536 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, device->depth_blt_rb);
4537 checkGLcall("glBindRenderbufferEXT");
4538 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8,
4539 This->currentDesc.Width, This->currentDesc.Height);
4540 checkGLcall("glRenderbufferStorageEXT");
4541 device->depth_blt_rb_w = This->currentDesc.Width;
4542 device->depth_blt_rb_h = This->currentDesc.Height;
4545 context_bind_fbo(context, GL_FRAMEBUFFER, &context->dst_fbo);
4546 gl_info->fbo_ops.glFramebufferRenderbuffer(GL_FRAMEBUFFER,
4547 GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, device->depth_blt_rb);
4548 checkGLcall("glFramebufferRenderbufferEXT");
4549 context_attach_depth_stencil_fbo(context, GL_FRAMEBUFFER, iface, FALSE);
4551 /* Do the actual blit */
4552 surface_depth_blt(This, device->depth_blt_texture, This->currentDesc.Width, This->currentDesc.Height, bind_target);
4553 checkGLcall("depth_blt");
4555 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER, &context->current_fbo->id);
4556 else context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4558 LEAVE_GL();
4559 } else {
4560 FIXME("No up to date depth stencil location\n");
4562 } else if (location == SFLAG_DS_ONSCREEN) {
4563 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4564 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4566 ENTER_GL();
4568 context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4569 surface_depth_blt(This, This->texture_name, This->currentDesc.Width,
4570 This->currentDesc.Height, This->texture_target);
4571 checkGLcall("depth_blt");
4573 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER, &context->current_fbo->id);
4575 LEAVE_GL();
4576 } else {
4577 FIXME("No up to date depth stencil location\n");
4579 } else {
4580 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4583 This->Flags |= location;
4586 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4587 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4588 IWineD3DBaseTexture *texture;
4589 IWineD3DSurfaceImpl *overlay;
4591 TRACE("(%p)->(%s, %s)\n", iface, debug_surflocation(flag),
4592 persistent ? "TRUE" : "FALSE");
4594 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4595 if (surface_is_offscreen(iface))
4597 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4598 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4600 else
4602 TRACE("Surface %p is an onscreen surface\n", iface);
4606 if(persistent) {
4607 if(((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) ||
4608 ((This->Flags & SFLAG_INSRGBTEX) && !(flag & SFLAG_INSRGBTEX))) {
4609 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4610 TRACE("Passing to container\n");
4611 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4612 IWineD3DBaseTexture_Release(texture);
4615 This->Flags &= ~SFLAG_LOCATIONS;
4616 This->Flags |= flag;
4618 /* Redraw emulated overlays, if any */
4619 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4620 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4621 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4624 } else {
4625 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))) {
4626 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4627 TRACE("Passing to container\n");
4628 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4629 IWineD3DBaseTexture_Release(texture);
4632 This->Flags &= ~flag;
4635 if(!(This->Flags & SFLAG_LOCATIONS)) {
4636 ERR("%p: Surface does not have any up to date location\n", This);
4640 struct coords {
4641 GLfloat x, y, z;
4644 struct float_rect
4646 float l;
4647 float t;
4648 float r;
4649 float b;
4652 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
4654 f->l = ((r->left * 2.0f) / w) - 1.0f;
4655 f->t = ((r->top * 2.0f) / h) - 1.0f;
4656 f->r = ((r->right * 2.0f) / w) - 1.0f;
4657 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
4660 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in)
4662 IWineD3DDeviceImpl *device = This->resource.device;
4663 struct wined3d_context *context;
4664 struct coords coords[4];
4665 RECT rect;
4666 IWineD3DSwapChain *swapchain;
4667 IWineD3DBaseTexture *texture;
4668 GLenum bind_target;
4669 struct float_rect f;
4671 if(rect_in) {
4672 rect = *rect_in;
4673 } else {
4674 rect.left = 0;
4675 rect.top = 0;
4676 rect.right = This->currentDesc.Width;
4677 rect.bottom = This->currentDesc.Height;
4680 switch (This->texture_target)
4682 case GL_TEXTURE_2D:
4683 bind_target = GL_TEXTURE_2D;
4685 coords[0].x = (float)rect.left / This->pow2Width;
4686 coords[0].y = (float)rect.top / This->pow2Height;
4687 coords[0].z = 0;
4689 coords[1].x = (float)rect.left / This->pow2Width;
4690 coords[1].y = (float)rect.bottom / This->pow2Height;
4691 coords[1].z = 0;
4693 coords[2].x = (float)rect.right / This->pow2Width;
4694 coords[2].y = (float)rect.bottom / This->pow2Height;
4695 coords[2].z = 0;
4697 coords[3].x = (float)rect.right / This->pow2Width;
4698 coords[3].y = (float)rect.top / This->pow2Height;
4699 coords[3].z = 0;
4700 break;
4702 case GL_TEXTURE_RECTANGLE_ARB:
4703 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4704 coords[0].x = rect.left; coords[0].y = rect.top; coords[0].z = 0;
4705 coords[1].x = rect.left; coords[1].y = rect.bottom; coords[1].z = 0;
4706 coords[2].x = rect.right; coords[2].y = rect.bottom; coords[2].z = 0;
4707 coords[3].x = rect.right; coords[3].y = rect.top; coords[3].z = 0;
4708 break;
4710 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4711 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4712 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4713 coords[0].x = 1; coords[0].y = -f.t; coords[0].z = -f.l;
4714 coords[1].x = 1; coords[1].y = -f.b; coords[1].z = -f.l;
4715 coords[2].x = 1; coords[2].y = -f.b; coords[2].z = -f.r;
4716 coords[3].x = 1; coords[3].y = -f.t; coords[3].z = -f.r;
4717 break;
4719 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4720 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4721 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4722 coords[0].x = -1; coords[0].y = -f.t; coords[0].z = f.l;
4723 coords[1].x = -1; coords[1].y = -f.b; coords[1].z = f.l;
4724 coords[2].x = -1; coords[2].y = -f.b; coords[2].z = f.r;
4725 coords[3].x = -1; coords[3].y = -f.t; coords[3].z = f.r;
4726 break;
4728 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4729 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4730 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4731 coords[0].x = f.l; coords[0].y = 1; coords[0].z = f.t;
4732 coords[1].x = f.l; coords[1].y = 1; coords[1].z = f.b;
4733 coords[2].x = f.r; coords[2].y = 1; coords[2].z = f.b;
4734 coords[3].x = f.r; coords[3].y = 1; coords[3].z = f.t;
4735 break;
4737 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4738 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4739 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4740 coords[0].x = f.l; coords[0].y = -1; coords[0].z = -f.t;
4741 coords[1].x = f.l; coords[1].y = -1; coords[1].z = -f.b;
4742 coords[2].x = f.r; coords[2].y = -1; coords[2].z = -f.b;
4743 coords[3].x = f.r; coords[3].y = -1; coords[3].z = -f.t;
4744 break;
4746 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4747 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4748 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4749 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1;
4750 coords[1].x = f.l; coords[1].y = -f.b; coords[1].z = 1;
4751 coords[2].x = f.r; coords[2].y = -f.b; coords[2].z = 1;
4752 coords[3].x = f.r; coords[3].y = -f.t; coords[3].z = 1;
4753 break;
4755 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4756 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4757 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4758 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1;
4759 coords[1].x = -f.l; coords[1].y = -f.b; coords[1].z = -1;
4760 coords[2].x = -f.r; coords[2].y = -f.b; coords[2].z = -1;
4761 coords[3].x = -f.r; coords[3].y = -f.t; coords[3].z = -1;
4762 break;
4764 default:
4765 ERR("Unexpected texture target %#x\n", This->texture_target);
4766 return;
4769 context = context_acquire(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4771 ENTER_GL();
4773 glEnable(bind_target);
4774 checkGLcall("glEnable(bind_target)");
4775 glBindTexture(bind_target, This->texture_name);
4776 checkGLcall("glBindTexture(bind_target, This->texture_name)");
4777 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4778 checkGLcall("glTexParameteri");
4779 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4780 checkGLcall("glTexParameteri");
4782 if (context->render_offscreen)
4784 LONG tmp = rect.top;
4785 rect.top = rect.bottom;
4786 rect.bottom = tmp;
4789 glBegin(GL_QUADS);
4790 glTexCoord3fv(&coords[0].x);
4791 glVertex2i(rect.left, rect.top);
4793 glTexCoord3fv(&coords[1].x);
4794 glVertex2i(rect.left, rect.bottom);
4796 glTexCoord3fv(&coords[2].x);
4797 glVertex2i(rect.right, rect.bottom);
4799 glTexCoord3fv(&coords[3].x);
4800 glVertex2i(rect.right, rect.top);
4801 glEnd();
4802 checkGLcall("glEnd");
4804 glDisable(bind_target);
4805 checkGLcall("glDisable(bind_target)");
4807 LEAVE_GL();
4809 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain)))
4811 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
4812 if(((IWineD3DSwapChainImpl*)swapchain)->frontBuffer == (IWineD3DSurface*)This ||
4813 ((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
4814 wglFlush();
4816 IWineD3DSwapChain_Release(swapchain);
4817 } else {
4818 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
4819 * reset properly next draw
4821 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture)))
4823 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
4824 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
4825 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
4826 IWineD3DBaseTexture_Release(texture);
4830 context_release(context);
4833 /*****************************************************************************
4834 * IWineD3DSurface::LoadLocation
4836 * Copies the current surface data from wherever it is to the requested
4837 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4838 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4839 * multiple locations, the gl texture is preferred over the drawable, which is
4840 * preferred over system memory. The PBO counts as system memory. If rect is
4841 * not NULL, only the specified rectangle is copied (only supported for
4842 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4843 * location is marked up to date after the copy.
4845 * Parameters:
4846 * flag: Surface location flag to be updated
4847 * rect: rectangle to be copied
4849 * Returns:
4850 * WINED3D_OK on success
4851 * WINED3DERR_DEVICELOST on an internal error
4853 *****************************************************************************/
4854 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4855 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4856 IWineD3DDeviceImpl *device = This->resource.device;
4857 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4858 GLenum format, internal, type;
4859 CONVERT_TYPES convert;
4860 int bpp;
4861 int width, pitch, outpitch;
4862 BYTE *mem;
4863 BOOL drawable_read_ok = TRUE;
4864 BOOL in_fbo = FALSE;
4866 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4867 if (surface_is_offscreen(iface))
4869 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4870 * Prefer SFLAG_INTEXTURE. */
4871 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4872 drawable_read_ok = FALSE;
4873 in_fbo = TRUE;
4875 else
4877 TRACE("Surface %p is an onscreen surface\n", iface);
4881 TRACE("(%p)->(%s, %p)\n", iface, debug_surflocation(flag), rect);
4882 if(rect) {
4883 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4886 if(This->Flags & flag) {
4887 TRACE("Location already up to date\n");
4888 return WINED3D_OK;
4891 if(!(This->Flags & SFLAG_LOCATIONS)) {
4892 ERR("%p: Surface does not have any up to date location\n", This);
4893 This->Flags |= SFLAG_LOST;
4894 return WINED3DERR_DEVICELOST;
4897 if(flag == SFLAG_INSYSMEM) {
4898 surface_prepare_system_memory(This);
4900 /* Download the surface to system memory */
4901 if (This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
4903 struct wined3d_context *context = NULL;
4905 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4907 surface_bind_and_dirtify(This, !(This->Flags & SFLAG_INTEXTURE));
4908 surface_download_data(This);
4910 if (context) context_release(context);
4912 else
4914 /* Note: It might be faster to download into a texture first. */
4915 read_from_framebuffer(This, rect,
4916 This->resource.allocatedMemory,
4917 IWineD3DSurface_GetPitch(iface));
4919 } else if(flag == SFLAG_INDRAWABLE) {
4920 if(This->Flags & SFLAG_INTEXTURE) {
4921 surface_blt_to_drawable(This, rect);
4922 } else {
4923 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4924 /* This needs a shader to convert the srgb data sampled from the GL texture into RGB
4925 * values, otherwise we get incorrect values in the target. For now go the slow way
4926 * via a system memory copy
4928 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4931 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, FALSE);
4933 /* The width is in 'length' not in bytes */
4934 width = This->currentDesc.Width;
4935 pitch = IWineD3DSurface_GetPitch(iface);
4937 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4938 * but it isn't set (yet) in all cases it is getting called. */
4939 if ((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO))
4941 struct wined3d_context *context = NULL;
4943 TRACE("Removing the pbo attached to surface %p\n", This);
4945 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4946 surface_remove_pbo(This);
4947 if (context) context_release(context);
4950 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4951 int height = This->currentDesc.Height;
4953 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4954 outpitch = width * bpp;
4955 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4957 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4958 if(!mem) {
4959 ERR("Out of memory %d, %d!\n", outpitch, height);
4960 return WINED3DERR_OUTOFVIDEOMEMORY;
4962 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4964 This->Flags |= SFLAG_CONVERTED;
4965 } else {
4966 This->Flags &= ~SFLAG_CONVERTED;
4967 mem = This->resource.allocatedMemory;
4970 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4972 /* Don't delete PBO memory */
4973 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4974 HeapFree(GetProcessHeap(), 0, mem);
4976 } else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */ {
4977 if (drawable_read_ok && (This->Flags & SFLAG_INDRAWABLE)) {
4978 read_from_framebuffer_texture(This, flag == SFLAG_INSRGBTEX);
4980 else
4982 /* Upload from system memory */
4983 BOOL srgb = flag == SFLAG_INSRGBTEX;
4984 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4985 struct wined3d_context *context = NULL;
4987 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */,
4988 &format, &internal, &type, &convert, &bpp, srgb);
4990 if(srgb) {
4991 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE) {
4992 /* Performance warning ... */
4993 FIXME("%p: Downloading rgb texture to reload it as srgb\n", This);
4994 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4996 } else {
4997 if((This->Flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX) {
4998 /* Performance warning ... */
4999 FIXME("%p: Downloading srgb texture to reload it as rgb\n", This);
5000 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
5003 if(!(This->Flags & SFLAG_INSYSMEM)) {
5004 /* Should not happen */
5005 ERR("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set\n");
5006 /* Lets hope we get it from somewhere... */
5007 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
5010 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
5011 surface_bind_and_dirtify(This, srgb);
5013 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
5014 This->Flags |= SFLAG_GLCKEY;
5015 This->glCKey = This->SrcBltCKey;
5017 else This->Flags &= ~SFLAG_GLCKEY;
5019 /* The width is in 'length' not in bytes */
5020 width = This->currentDesc.Width;
5021 pitch = IWineD3DSurface_GetPitch(iface);
5023 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
5024 * but it isn't set (yet) in all cases it is getting called. */
5025 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
5026 TRACE("Removing the pbo attached to surface %p\n", This);
5027 surface_remove_pbo(This);
5030 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
5031 int height = This->currentDesc.Height;
5033 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
5034 outpitch = width * bpp;
5035 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
5037 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
5038 if(!mem) {
5039 ERR("Out of memory %d, %d!\n", outpitch, height);
5040 if (context) context_release(context);
5041 return WINED3DERR_OUTOFVIDEOMEMORY;
5043 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
5045 This->Flags |= SFLAG_CONVERTED;
5047 else if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
5048 && (gl_info->supported[EXT_PALETTED_TEXTURE] || gl_info->supported[ARB_FRAGMENT_PROGRAM]))
5050 d3dfmt_p8_upload_palette(iface, convert);
5051 This->Flags &= ~SFLAG_CONVERTED;
5052 mem = This->resource.allocatedMemory;
5053 } else {
5054 This->Flags &= ~SFLAG_CONVERTED;
5055 mem = This->resource.allocatedMemory;
5058 /* Make sure the correct pitch is used */
5059 ENTER_GL();
5060 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
5061 LEAVE_GL();
5063 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
5064 TRACE("non power of two support\n");
5065 if(!(This->Flags & alloc_flag)) {
5066 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
5067 This->Flags |= alloc_flag;
5069 if (mem || (This->Flags & SFLAG_PBO)) {
5070 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
5072 } else {
5073 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
5074 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
5076 if(!(This->Flags & alloc_flag)) {
5077 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
5078 This->Flags |= alloc_flag;
5080 if (mem || (This->Flags & SFLAG_PBO)) {
5081 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
5085 /* Restore the default pitch */
5086 ENTER_GL();
5087 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
5088 LEAVE_GL();
5090 if (context) context_release(context);
5092 /* Don't delete PBO memory */
5093 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
5094 HeapFree(GetProcessHeap(), 0, mem);
5098 if(rect == NULL) {
5099 This->Flags |= flag;
5102 if (in_fbo && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
5103 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
5104 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
5107 return WINED3D_OK;
5110 static HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container)
5112 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
5113 IWineD3DSwapChain *swapchain = NULL;
5115 /* Update the drawable size method */
5116 if(container) {
5117 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
5119 if(swapchain) {
5120 This->get_drawable_size = get_drawable_size_swapchain;
5121 IWineD3DSwapChain_Release(swapchain);
5122 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
5123 switch(wined3d_settings.offscreen_rendering_mode) {
5124 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
5125 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
5126 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
5130 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
5133 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
5134 return SURFACE_OPENGL;
5137 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
5138 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
5139 HRESULT hr;
5141 /* If there's no destination surface there is nothing to do */
5142 if(!This->overlay_dest) return WINED3D_OK;
5144 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
5145 * update the overlay. Prevent an endless recursion
5147 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
5148 return WINED3D_OK;
5150 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
5151 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
5152 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
5153 NULL, WINED3DTEXF_LINEAR);
5154 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
5156 return hr;
5159 BOOL surface_is_offscreen(IWineD3DSurface *iface)
5161 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
5162 IWineD3DSwapChainImpl *swapchain = (IWineD3DSwapChainImpl *) This->container;
5164 /* Not on a swapchain - must be offscreen */
5165 if (!(This->Flags & SFLAG_SWAPCHAIN)) return TRUE;
5167 /* The front buffer is always onscreen */
5168 if(iface == swapchain->frontBuffer) return FALSE;
5170 /* If the swapchain is rendered to an FBO, the backbuffer is
5171 * offscreen, otherwise onscreen */
5172 return swapchain->render_to_fbo;
5175 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
5177 /* IUnknown */
5178 IWineD3DBaseSurfaceImpl_QueryInterface,
5179 IWineD3DBaseSurfaceImpl_AddRef,
5180 IWineD3DSurfaceImpl_Release,
5181 /* IWineD3DResource */
5182 IWineD3DBaseSurfaceImpl_GetParent,
5183 IWineD3DBaseSurfaceImpl_SetPrivateData,
5184 IWineD3DBaseSurfaceImpl_GetPrivateData,
5185 IWineD3DBaseSurfaceImpl_FreePrivateData,
5186 IWineD3DBaseSurfaceImpl_SetPriority,
5187 IWineD3DBaseSurfaceImpl_GetPriority,
5188 IWineD3DSurfaceImpl_PreLoad,
5189 IWineD3DSurfaceImpl_UnLoad,
5190 IWineD3DBaseSurfaceImpl_GetType,
5191 /* IWineD3DSurface */
5192 IWineD3DBaseSurfaceImpl_GetContainer,
5193 IWineD3DBaseSurfaceImpl_GetDesc,
5194 IWineD3DSurfaceImpl_LockRect,
5195 IWineD3DSurfaceImpl_UnlockRect,
5196 IWineD3DSurfaceImpl_GetDC,
5197 IWineD3DSurfaceImpl_ReleaseDC,
5198 IWineD3DSurfaceImpl_Flip,
5199 IWineD3DSurfaceImpl_Blt,
5200 IWineD3DBaseSurfaceImpl_GetBltStatus,
5201 IWineD3DBaseSurfaceImpl_GetFlipStatus,
5202 IWineD3DBaseSurfaceImpl_IsLost,
5203 IWineD3DBaseSurfaceImpl_Restore,
5204 IWineD3DSurfaceImpl_BltFast,
5205 IWineD3DBaseSurfaceImpl_GetPalette,
5206 IWineD3DBaseSurfaceImpl_SetPalette,
5207 IWineD3DSurfaceImpl_RealizePalette,
5208 IWineD3DBaseSurfaceImpl_SetColorKey,
5209 IWineD3DBaseSurfaceImpl_GetPitch,
5210 IWineD3DSurfaceImpl_SetMem,
5211 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
5212 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
5213 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
5214 IWineD3DBaseSurfaceImpl_UpdateOverlay,
5215 IWineD3DBaseSurfaceImpl_SetClipper,
5216 IWineD3DBaseSurfaceImpl_GetClipper,
5217 /* Internal use: */
5218 IWineD3DSurfaceImpl_LoadTexture,
5219 IWineD3DSurfaceImpl_BindTexture,
5220 IWineD3DSurfaceImpl_SaveSnapshot,
5221 IWineD3DSurfaceImpl_SetContainer,
5222 IWineD3DBaseSurfaceImpl_GetData,
5223 IWineD3DSurfaceImpl_SetFormat,
5224 IWineD3DSurfaceImpl_PrivateSetup,
5225 IWineD3DSurfaceImpl_ModifyLocation,
5226 IWineD3DSurfaceImpl_LoadLocation,
5227 IWineD3DSurfaceImpl_GetImplType,
5228 IWineD3DSurfaceImpl_DrawOverlay
5230 #undef GLINFO_LOCATION
5232 #define GLINFO_LOCATION device->adapter->gl_info
5233 static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
5234 /* Context activation is done by the caller. */
5235 static void ffp_blit_free(IWineD3DDevice *iface) { }
5237 /* Context activation is done by the caller. */
5238 static HRESULT ffp_blit_set(IWineD3DDevice *iface, const struct GlPixelFormatDesc *format_desc,
5239 GLenum textype, UINT width, UINT height)
5241 ENTER_GL();
5242 glEnable(textype);
5243 checkGLcall("glEnable(textype)");
5244 LEAVE_GL();
5245 return WINED3D_OK;
5248 /* Context activation is done by the caller. */
5249 static void ffp_blit_unset(IWineD3DDevice *iface)
5251 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
5252 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5254 ENTER_GL();
5255 glDisable(GL_TEXTURE_2D);
5256 checkGLcall("glDisable(GL_TEXTURE_2D)");
5257 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
5259 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
5260 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
5262 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
5264 glDisable(GL_TEXTURE_RECTANGLE_ARB);
5265 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
5267 LEAVE_GL();
5270 static BOOL ffp_blit_color_fixup_supported(struct color_fixup_desc fixup)
5272 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
5274 TRACE("Checking support for fixup:\n");
5275 dump_color_fixup_desc(fixup);
5278 /* We only support identity conversions. */
5279 if (is_identity_fixup(fixup))
5281 TRACE("[OK]\n");
5282 return TRUE;
5285 TRACE("[FAILED]\n");
5286 return FALSE;
5289 const struct blit_shader ffp_blit = {
5290 ffp_blit_alloc,
5291 ffp_blit_free,
5292 ffp_blit_set,
5293 ffp_blit_unset,
5294 ffp_blit_color_fixup_supported