wined3d: Revert 4ba16b84a9e60ffcc98fc4b4c303c975589b59e2.
[wine.git] / dlls / wined3d / surface.c
blob6a5e66d8dd3588b6ba18187bc3c72df7308675da
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.wineD3DDevice;
42 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
43 renderbuffer_entry_t *entry, *entry2;
45 TRACE("(%p) : Cleaning up.\n", This);
47 /* Need a context to destroy the texture. Use the currently active render
48 * target, but only if the primary render target exists. Otherwise
49 * lastActiveRenderTarget is garbage. When destroying the primary render
50 * target, Uninit3D() will activate a context before doing anything. */
51 if (device->render_targets && device->render_targets[0])
53 ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
56 ENTER_GL();
58 if (This->texture_name)
60 /* Release the OpenGL texture. */
61 TRACE("Deleting texture %u.\n", This->texture_name);
62 glDeleteTextures(1, &This->texture_name);
65 if (This->Flags & SFLAG_PBO)
67 /* Delete the PBO. */
68 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
71 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry)
73 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
74 HeapFree(GetProcessHeap(), 0, entry);
77 LEAVE_GL();
79 if (This->Flags & SFLAG_DIBSECTION)
81 /* Release the DC. */
82 SelectObject(This->hDC, This->dib.holdbitmap);
83 DeleteDC(This->hDC);
84 /* Release the DIB section. */
85 DeleteObject(This->dib.DIBsection);
86 This->dib.bitmap_data = NULL;
87 This->resource.allocatedMemory = NULL;
90 if (This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem((IWineD3DSurface *)This, NULL);
91 if (This->overlay_dest) list_remove(&This->overlay_entry);
93 HeapFree(GetProcessHeap(), 0, This->palette9);
95 resource_cleanup((IWineD3DResource *)This);
98 UINT surface_calculate_size(const struct GlPixelFormatDesc *format_desc, UINT alignment, UINT width, UINT height)
100 UINT size;
102 if (format_desc->format == WINED3DFMT_UNKNOWN)
104 size = 0;
106 else if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
108 UINT row_block_count = (width + format_desc->block_width - 1) / format_desc->block_width;
109 UINT row_count = (height + format_desc->block_height - 1) / format_desc->block_height;
110 size = row_count * row_block_count * format_desc->block_byte_count;
112 else
114 /* The pitch is a multiple of 4 bytes. */
115 size = height * (((width * format_desc->byte_count) + alignment - 1) & ~(alignment - 1));
118 if (format_desc->heightscale != 0.0f) size *= format_desc->heightscale;
120 return size;
123 HRESULT surface_init(IWineD3DSurfaceImpl *surface, WINED3DSURFTYPE surface_type, UINT alignment,
124 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
125 UINT multisample_quality, IWineD3DDeviceImpl *device, DWORD usage, WINED3DFORMAT format,
126 WINED3DPOOL pool, IUnknown *parent, const struct wined3d_parent_ops *parent_ops)
128 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
129 const struct GlPixelFormatDesc *format_desc = getFormatDescEntry(format, &GLINFO_LOCATION);
130 void (*cleanup)(IWineD3DSurfaceImpl *This);
131 unsigned int resource_size;
132 HRESULT hr;
134 if (multisample_quality > 0)
136 FIXME("multisample_quality set to %u, substituting 0\n", multisample_quality);
137 multisample_quality = 0;
140 /* FIXME: Check that the format is supported by the device. */
142 resource_size = surface_calculate_size(format_desc, alignment, width, height);
144 /* Look at the implementation and set the correct Vtable. */
145 switch (surface_type)
147 case SURFACE_OPENGL:
148 surface->lpVtbl = &IWineD3DSurface_Vtbl;
149 cleanup = surface_cleanup;
150 break;
152 case SURFACE_GDI:
153 surface->lpVtbl = &IWineGDISurface_Vtbl;
154 cleanup = surface_gdi_cleanup;
155 break;
157 default:
158 ERR("Requested unknown surface implementation %#x.\n", surface_type);
159 return WINED3DERR_INVALIDCALL;
162 hr = resource_init((IWineD3DResource *)surface, WINED3DRTYPE_SURFACE,
163 device, resource_size, usage, format_desc, pool, parent, parent_ops);
164 if (FAILED(hr))
166 WARN("Failed to initialize resource, returning %#x.\n", hr);
167 return hr;
170 /* "Standalone" surface. */
171 IWineD3DSurface_SetContainer((IWineD3DSurface *)surface, NULL);
173 surface->currentDesc.Width = width;
174 surface->currentDesc.Height = height;
175 surface->currentDesc.MultiSampleType = multisample_type;
176 surface->currentDesc.MultiSampleQuality = multisample_quality;
177 surface->texture_level = level;
178 list_init(&surface->overlays);
180 /* Flags */
181 surface->Flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
182 if (discard) surface->Flags |= SFLAG_DISCARD;
183 if (lockable || format == WINED3DFMT_D16_LOCKABLE) surface->Flags |= SFLAG_LOCKABLE;
185 /* Quick lockable sanity check.
186 * TODO: remove this after surfaces, usage and lockability have been debugged properly
187 * this function is too deep to need to care about things like this.
188 * Levels need to be checked too, since they all affect what can be done. */
189 switch (pool)
191 case WINED3DPOOL_SCRATCH:
192 if(!lockable)
194 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
195 "which are mutually exclusive, setting lockable to TRUE.\n");
196 lockable = TRUE;
198 break;
200 case WINED3DPOOL_SYSTEMMEM:
201 if (!lockable)
202 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
203 break;
205 case WINED3DPOOL_MANAGED:
206 if (usage & WINED3DUSAGE_DYNAMIC)
207 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
208 break;
210 case WINED3DPOOL_DEFAULT:
211 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
212 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
213 break;
215 default:
216 FIXME("Unknown pool %#x.\n", pool);
217 break;
220 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
222 FIXME("Trying to create a render target that isn't in the default pool.\n");
225 /* Mark the texture as dirty so that it gets loaded first time around. */
226 surface_add_dirty_rect((IWineD3DSurface *)surface, NULL);
227 list_init(&surface->renderbuffers);
229 TRACE("surface %p, memory %p, size %u\n", surface, surface->resource.allocatedMemory, surface->resource.size);
231 /* Call the private setup routine */
232 hr = IWineD3DSurface_PrivateSetup((IWineD3DSurface *)surface);
233 if (FAILED(hr))
235 ERR("Private setup failed, returning %#x\n", hr);
236 cleanup(surface);
237 return hr;
240 return hr;
243 static void surface_force_reload(IWineD3DSurface *iface)
245 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
247 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
250 void surface_set_texture_name(IWineD3DSurface *iface, GLuint new_name, BOOL srgb)
252 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
253 GLuint *name;
254 DWORD flag;
256 if(srgb)
258 name = &This->texture_name_srgb;
259 flag = SFLAG_INSRGBTEX;
261 else
263 name = &This->texture_name;
264 flag = SFLAG_INTEXTURE;
267 TRACE("(%p) : setting texture name %u\n", This, new_name);
269 if (!*name && new_name)
271 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
272 * surface has no texture name yet. See if we can get rid of this. */
273 if (This->Flags & flag)
274 ERR("Surface has SFLAG_INTEXTURE set, but no texture name\n");
275 IWineD3DSurface_ModifyLocation(iface, flag, FALSE);
278 *name = new_name;
279 surface_force_reload(iface);
282 void surface_set_texture_target(IWineD3DSurface *iface, GLenum target)
284 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
286 TRACE("(%p) : setting target %#x\n", This, target);
288 if (This->texture_target != target)
290 if (target == GL_TEXTURE_RECTANGLE_ARB)
292 This->Flags &= ~SFLAG_NORMCOORD;
294 else if (This->texture_target == GL_TEXTURE_RECTANGLE_ARB)
296 This->Flags |= SFLAG_NORMCOORD;
299 This->texture_target = target;
300 surface_force_reload(iface);
303 /* Context activation is done by the caller. */
304 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This, BOOL srgb) {
305 DWORD active_sampler;
307 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
308 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
309 * gl states. The current texture unit should always be a valid one.
311 * To be more specific, this is tricky because we can implicitly be called
312 * from sampler() in state.c. This means we can't touch anything other than
313 * whatever happens to be the currently active texture, or we would risk
314 * marking already applied sampler states dirty again.
316 * TODO: Track the current active texture per GL context instead of using glGet
318 GLint active_texture;
319 ENTER_GL();
320 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
321 LEAVE_GL();
322 active_sampler = This->resource.wineD3DDevice->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
324 if (active_sampler != WINED3D_UNMAPPED_STAGE)
326 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(active_sampler));
328 IWineD3DSurface_BindTexture((IWineD3DSurface *)This, srgb);
331 /* This function checks if the primary render target uses the 8bit paletted format. */
332 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
334 if (device->render_targets && device->render_targets[0]) {
335 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
336 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
337 && (render_target->resource.format_desc->format == WINED3DFMT_P8))
338 return TRUE;
340 return FALSE;
343 #undef GLINFO_LOCATION
345 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
347 /* This call just downloads data, the caller is responsible for binding the
348 * correct texture. */
349 /* Context activation is done by the caller. */
350 static void surface_download_data(IWineD3DSurfaceImpl *This) {
351 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
353 /* Only support read back of converted P8 surfaces */
354 if (This->Flags & SFLAG_CONVERTED && format_desc->format != WINED3DFMT_P8)
356 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(format_desc->format));
357 return;
360 ENTER_GL();
362 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
364 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
365 This, This->texture_level, format_desc->glFormat, format_desc->glType,
366 This->resource.allocatedMemory);
368 if (This->Flags & SFLAG_PBO)
370 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
371 checkGLcall("glBindBufferARB");
372 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target, This->texture_level, NULL));
373 checkGLcall("glGetCompressedTexImageARB");
374 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
375 checkGLcall("glBindBufferARB");
377 else
379 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target,
380 This->texture_level, This->resource.allocatedMemory));
381 checkGLcall("glGetCompressedTexImageARB");
384 LEAVE_GL();
385 } else {
386 void *mem;
387 GLenum format = format_desc->glFormat;
388 GLenum type = format_desc->glType;
389 int src_pitch = 0;
390 int dst_pitch = 0;
392 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
393 if (format_desc->format == WINED3DFMT_P8 && primary_render_target_is_p8(This->resource.wineD3DDevice))
395 format = GL_ALPHA;
396 type = GL_UNSIGNED_BYTE;
399 if (This->Flags & SFLAG_NONPOW2) {
400 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
401 src_pitch = format_desc->byte_count * This->pow2Width;
402 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
403 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
404 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
405 } else {
406 mem = This->resource.allocatedMemory;
409 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
410 This, This->texture_level, format, type, mem);
412 if(This->Flags & SFLAG_PBO) {
413 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
414 checkGLcall("glBindBufferARB");
416 glGetTexImage(This->texture_target, This->texture_level, format, type, NULL);
417 checkGLcall("glGetTexImage");
419 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
420 checkGLcall("glBindBufferARB");
421 } else {
422 glGetTexImage(This->texture_target, This->texture_level, format, type, mem);
423 checkGLcall("glGetTexImage");
425 LEAVE_GL();
427 if (This->Flags & SFLAG_NONPOW2) {
428 const BYTE *src_data;
429 BYTE *dst_data;
430 UINT y;
432 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
433 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
434 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
436 * We're doing this...
438 * instead of boxing the texture :
439 * |<-texture width ->| -->pow2width| /\
440 * |111111111111111111| | |
441 * |222 Texture 222222| boxed empty | texture height
442 * |3333 Data 33333333| | |
443 * |444444444444444444| | \/
444 * ----------------------------------- |
445 * | boxed empty | boxed empty | pow2height
446 * | | | \/
447 * -----------------------------------
450 * we're repacking the data to the expected texture width
452 * |<-texture width ->| -->pow2width| /\
453 * |111111111111111111222222222222222| |
454 * |222333333333333333333444444444444| texture height
455 * |444444 | |
456 * | | \/
457 * | | |
458 * | empty | pow2height
459 * | | \/
460 * -----------------------------------
462 * == is the same as
464 * |<-texture width ->| /\
465 * |111111111111111111|
466 * |222222222222222222|texture height
467 * |333333333333333333|
468 * |444444444444444444| \/
469 * --------------------
471 * this also means that any references to allocatedMemory should work with the data as if were a
472 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
474 * internally the texture is still stored in a boxed format so any references to textureName will
475 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
477 * Performance should not be an issue, because applications normally do not lock the surfaces when
478 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
479 * and doesn't have to be re-read.
481 src_data = mem;
482 dst_data = This->resource.allocatedMemory;
483 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
484 for (y = 1 ; y < This->currentDesc.Height; y++) {
485 /* skip the first row */
486 src_data += src_pitch;
487 dst_data += dst_pitch;
488 memcpy(dst_data, src_data, dst_pitch);
491 HeapFree(GetProcessHeap(), 0, mem);
495 /* Surface has now been downloaded */
496 This->Flags |= SFLAG_INSYSMEM;
499 /* This call just uploads data, the caller is responsible for binding the
500 * correct texture. */
501 /* Context activation is done by the caller. */
502 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
503 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
505 TRACE("This %p, internal %#x, width %d, height %d, format %#x, type %#x, data %p.\n",
506 This, internal, width, height, format, type, data);
507 TRACE("target %#x, level %u, resource size %u.\n",
508 This->texture_target, This->texture_level, This->resource.size);
510 if (format_desc->heightscale != 1.0f && format_desc->heightscale != 0.0f) height *= format_desc->heightscale;
512 ENTER_GL();
514 if (This->Flags & SFLAG_PBO)
516 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
517 checkGLcall("glBindBufferARB");
519 TRACE("(%p) pbo: %#x, data: %p.\n", This, This->pbo, data);
520 data = NULL;
523 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
525 TRACE("Calling glCompressedTexSubImage2DARB.\n");
527 GL_EXTCALL(glCompressedTexSubImage2DARB(This->texture_target, This->texture_level,
528 0, 0, width, height, internal, This->resource.size, data));
529 checkGLcall("glCompressedTexSubImage2DARB");
531 else
533 TRACE("Calling glTexSubImage2D.\n");
535 glTexSubImage2D(This->texture_target, This->texture_level,
536 0, 0, width, height, format, type, data);
537 checkGLcall("glTexSubImage2D");
540 if (This->Flags & SFLAG_PBO)
542 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
543 checkGLcall("glBindBufferARB");
546 LEAVE_GL();
549 /* This call just allocates the texture, the caller is responsible for binding
550 * the correct texture. */
551 /* Context activation is done by the caller. */
552 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
553 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
554 BOOL enable_client_storage = FALSE;
555 const BYTE *mem = NULL;
557 if (format_desc->heightscale != 1.0f && format_desc->heightscale != 0.0f) height *= format_desc->heightscale;
559 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",
560 This, This->texture_target, This->texture_level, debug_d3dformat(format_desc->format),
561 internal, width, height, format, type);
563 ENTER_GL();
565 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
566 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
567 /* In some cases we want to disable client storage.
568 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
569 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
570 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
571 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
572 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
574 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
575 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
576 This->Flags &= ~SFLAG_CLIENT;
577 enable_client_storage = TRUE;
578 } else {
579 This->Flags |= SFLAG_CLIENT;
581 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
582 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
584 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
588 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED && mem)
590 GL_EXTCALL(glCompressedTexImage2DARB(This->texture_target, This->texture_level,
591 internal, width, height, 0, This->resource.size, mem));
593 else
595 glTexImage2D(This->texture_target, This->texture_level,
596 internal, width, height, 0, format, type, mem);
597 checkGLcall("glTexImage2D");
600 if(enable_client_storage) {
601 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
602 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
604 LEAVE_GL();
607 /* In D3D the depth stencil dimensions have to be greater than or equal to the
608 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
609 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
610 /* GL locking is done by the caller */
611 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
612 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
613 renderbuffer_entry_t *entry;
614 GLuint renderbuffer = 0;
615 unsigned int src_width, src_height;
617 src_width = This->pow2Width;
618 src_height = This->pow2Height;
620 /* A depth stencil smaller than the render target is not valid */
621 if (width > src_width || height > src_height) return;
623 /* Remove any renderbuffer set if the sizes match */
624 if (width == src_width && height == src_height) {
625 This->current_renderbuffer = NULL;
626 return;
629 /* Look if we've already got a renderbuffer of the correct dimensions */
630 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
631 if (entry->width == width && entry->height == height) {
632 renderbuffer = entry->id;
633 This->current_renderbuffer = entry;
634 break;
638 if (!renderbuffer) {
639 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
640 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
641 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
642 This->resource.format_desc->glInternal, width, height));
644 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
645 entry->width = width;
646 entry->height = height;
647 entry->id = renderbuffer;
648 list_add_head(&This->renderbuffers, &entry->entry);
650 This->current_renderbuffer = entry;
653 checkGLcall("set_compatible_renderbuffer");
656 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
657 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
658 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
660 TRACE("(%p) : swapchain %p\n", This, swapchain);
662 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
663 TRACE("Returning GL_BACK\n");
664 return GL_BACK;
665 } else if (swapchain_impl->frontBuffer == iface) {
666 TRACE("Returning GL_FRONT\n");
667 return GL_FRONT;
670 FIXME("Higher back buffer, returning GL_BACK\n");
671 return GL_BACK;
674 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
675 void surface_add_dirty_rect(IWineD3DSurface *iface, const RECT *dirty_rect)
677 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
678 IWineD3DBaseTexture *baseTexture = NULL;
680 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
681 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
683 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
684 if (dirty_rect)
686 This->dirtyRect.left = min(This->dirtyRect.left, dirty_rect->left);
687 This->dirtyRect.top = min(This->dirtyRect.top, dirty_rect->top);
688 This->dirtyRect.right = max(This->dirtyRect.right, dirty_rect->right);
689 This->dirtyRect.bottom = max(This->dirtyRect.bottom, dirty_rect->bottom);
691 else
693 This->dirtyRect.left = 0;
694 This->dirtyRect.top = 0;
695 This->dirtyRect.right = This->currentDesc.Width;
696 This->dirtyRect.bottom = This->currentDesc.Height;
699 TRACE("(%p) : Dirty: yes, Rect:(%d, %d, %d, %d)\n", This, This->dirtyRect.left,
700 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
702 /* if the container is a basetexture then mark it dirty. */
703 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture)))
705 TRACE("Passing to container\n");
706 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
707 IWineD3DBaseTexture_Release(baseTexture);
711 static inline BOOL surface_can_stretch_rect(IWineD3DSurfaceImpl *src, IWineD3DSurfaceImpl *dst)
713 return ((src->resource.format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE)
714 || (src->resource.usage & WINED3DUSAGE_RENDERTARGET))
715 && ((dst->resource.format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE)
716 || (dst->resource.usage & WINED3DUSAGE_RENDERTARGET))
717 && (src->resource.format_desc->format == dst->resource.format_desc->format
718 || (is_identity_fixup(src->resource.format_desc->color_fixup)
719 && is_identity_fixup(dst->resource.format_desc->color_fixup)));
722 static ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface)
724 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
725 ULONG ref = InterlockedDecrement(&This->resource.ref);
726 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
728 if (!ref)
730 surface_cleanup(This);
731 This->resource.parent_ops->wined3d_object_destroyed(This->resource.parent);
733 TRACE("(%p) Released.\n", This);
734 HeapFree(GetProcessHeap(), 0, This);
737 return ref;
740 /* ****************************************************
741 IWineD3DSurface IWineD3DResource parts follow
742 **************************************************** */
744 void surface_internal_preload(IWineD3DSurface *iface, enum WINED3DSRGB srgb)
746 /* TODO: check for locks */
747 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
748 IWineD3DBaseTexture *baseTexture = NULL;
749 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
751 TRACE("(%p)Checking to see if the container is a base texture\n", This);
752 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
753 IWineD3DBaseTextureImpl *tex_impl = (IWineD3DBaseTextureImpl *) baseTexture;
754 TRACE("Passing to container\n");
755 tex_impl->baseTexture.internal_preload(baseTexture, srgb);
756 IWineD3DBaseTexture_Release(baseTexture);
757 } else {
758 TRACE("(%p) : About to load surface\n", This);
760 if(!device->isInDraw) {
761 ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
764 if (This->resource.format_desc->format == WINED3DFMT_P8
765 || This->resource.format_desc->format == WINED3DFMT_A8P8)
767 if(palette9_changed(This)) {
768 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
769 /* TODO: This is not necessarily needed with hw palettized texture support */
770 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
771 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
772 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
776 IWineD3DSurface_LoadTexture(iface, srgb == SRGB_SRGB ? TRUE : FALSE);
778 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
779 /* Tell opengl to try and keep this texture in video ram (well mostly) */
780 GLclampf tmp;
781 tmp = 0.9f;
782 ENTER_GL();
783 glPrioritizeTextures(1, &This->texture_name, &tmp);
784 LEAVE_GL();
787 return;
790 static void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
791 surface_internal_preload(iface, SRGB_ANY);
794 /* Context activation is done by the caller. */
795 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
796 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
797 This->resource.allocatedMemory =
798 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
800 ENTER_GL();
801 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
802 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
803 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
804 checkGLcall("glGetBufferSubDataARB");
805 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
806 checkGLcall("glDeleteBuffersARB");
807 LEAVE_GL();
809 This->pbo = 0;
810 This->Flags &= ~SFLAG_PBO;
813 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
814 IWineD3DBaseTexture *texture = NULL;
815 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
816 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
817 renderbuffer_entry_t *entry, *entry2;
818 TRACE("(%p)\n", iface);
820 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
821 /* Default pool resources are supposed to be destroyed before Reset is called.
822 * Implicit resources stay however. So this means we have an implicit render target
823 * or depth stencil. The content may be destroyed, but we still have to tear down
824 * opengl resources, so we cannot leave early.
826 * Put the most up to date surface location into the drawable. D3D-wise this content
827 * is undefined, so it would be nowhere, but that would make the location management
828 * more complicated. The drawable is a sane location, because if we mark sysmem or
829 * texture up to date, drawPrim will copy the uninitialized texture or sysmem to the
830 * uninitialized drawable. That's pointless and we'd have to allocate the texture /
831 * sysmem copy here.
833 if (This->resource.usage & WINED3DUSAGE_DEPTHSTENCIL) {
834 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
835 } else {
836 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, TRUE);
838 } else {
839 /* Load the surface into system memory */
840 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
841 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
843 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
844 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSRGBTEX, FALSE);
845 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
847 ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
849 /* Destroy PBOs, but load them into real sysmem before */
850 if(This->Flags & SFLAG_PBO) {
851 surface_remove_pbo(This);
854 /* Destroy fbo render buffers. This is needed for implicit render targets, for
855 * all application-created targets the application has to release the surface
856 * before calling _Reset
858 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
859 ENTER_GL();
860 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
861 LEAVE_GL();
862 list_remove(&entry->entry);
863 HeapFree(GetProcessHeap(), 0, entry);
865 list_init(&This->renderbuffers);
866 This->current_renderbuffer = NULL;
868 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
869 * destroy it
871 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
872 if(!texture) {
873 ENTER_GL();
874 glDeleteTextures(1, &This->texture_name);
875 This->texture_name = 0;
876 glDeleteTextures(1, &This->texture_name_srgb);
877 This->texture_name_srgb = 0;
878 LEAVE_GL();
879 } else {
880 IWineD3DBaseTexture_Release(texture);
882 return;
885 /* ******************************************************
886 IWineD3DSurface IWineD3DSurface parts follow
887 ****************************************************** */
889 /* Read the framebuffer back into the surface */
890 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
891 IWineD3DSwapChainImpl *swapchain;
892 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
893 BYTE *mem;
894 GLint fmt;
895 GLint type;
896 BYTE *row, *top, *bottom;
897 int i;
898 BOOL bpp;
899 RECT local_rect;
900 BOOL srcIsUpsideDown;
901 GLint rowLen = 0;
902 GLint skipPix = 0;
903 GLint skipRow = 0;
905 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
906 static BOOL warned = FALSE;
907 if(!warned) {
908 ERR("The application tries to lock the render target, but render target locking is disabled\n");
909 warned = TRUE;
911 return;
914 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
915 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
916 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
917 * context->last_was_blit set on the unlock.
919 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
920 ENTER_GL();
922 /* Select the correct read buffer, and give some debug output.
923 * There is no need to keep track of the current read buffer or reset it, every part of the code
924 * that reads sets the read buffer as desired.
926 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
928 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
929 TRACE("Locking %#x buffer\n", buffer);
930 glReadBuffer(buffer);
931 checkGLcall("glReadBuffer");
933 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
934 srcIsUpsideDown = FALSE;
935 } else {
936 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
937 * Read from the back buffer
939 TRACE("Locking offscreen render target\n");
940 glReadBuffer(myDevice->offscreenBuffer);
941 srcIsUpsideDown = TRUE;
944 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
945 if(!rect) {
946 local_rect.left = 0;
947 local_rect.top = 0;
948 local_rect.right = This->currentDesc.Width;
949 local_rect.bottom = This->currentDesc.Height;
950 } else {
951 local_rect = *rect;
953 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
955 switch(This->resource.format_desc->format)
957 case WINED3DFMT_P8:
959 if(primary_render_target_is_p8(myDevice)) {
960 /* In case of P8 render targets the index is stored in the alpha component */
961 fmt = GL_ALPHA;
962 type = GL_UNSIGNED_BYTE;
963 mem = dest;
964 bpp = This->resource.format_desc->byte_count;
965 } else {
966 /* GL can't return palettized data, so read ARGB pixels into a
967 * separate block of memory and convert them into palettized format
968 * in software. Slow, but if the app means to use palettized render
969 * targets and locks it...
971 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
972 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
973 * for the color channels when palettizing the colors.
975 fmt = GL_RGB;
976 type = GL_UNSIGNED_BYTE;
977 pitch *= 3;
978 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
979 if(!mem) {
980 ERR("Out of memory\n");
981 LEAVE_GL();
982 return;
984 bpp = This->resource.format_desc->byte_count * 3;
987 break;
989 default:
990 mem = dest;
991 fmt = This->resource.format_desc->glFormat;
992 type = This->resource.format_desc->glType;
993 bpp = This->resource.format_desc->byte_count;
996 if(This->Flags & SFLAG_PBO) {
997 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
998 checkGLcall("glBindBufferARB");
999 if(mem != NULL) {
1000 ERR("mem not null for pbo -- unexpected\n");
1001 mem = NULL;
1005 /* Save old pixel store pack state */
1006 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
1007 checkGLcall("glGetIntegerv");
1008 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
1009 checkGLcall("glGetIntegerv");
1010 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
1011 checkGLcall("glGetIntegerv");
1013 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1014 glPixelStorei(GL_PACK_ROW_LENGTH, This->currentDesc.Width);
1015 checkGLcall("glPixelStorei");
1016 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
1017 checkGLcall("glPixelStorei");
1018 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
1019 checkGLcall("glPixelStorei");
1021 glReadPixels(local_rect.left, (!srcIsUpsideDown) ? (This->currentDesc.Height - local_rect.bottom) : local_rect.top ,
1022 local_rect.right - local_rect.left,
1023 local_rect.bottom - local_rect.top,
1024 fmt, type, mem);
1025 checkGLcall("glReadPixels");
1027 /* Reset previous pixel store pack state */
1028 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
1029 checkGLcall("glPixelStorei");
1030 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
1031 checkGLcall("glPixelStorei");
1032 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
1033 checkGLcall("glPixelStorei");
1035 if(This->Flags & SFLAG_PBO) {
1036 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1037 checkGLcall("glBindBufferARB");
1039 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
1040 * to get a pointer to it and perform the flipping in software. This is a lot
1041 * faster than calling glReadPixels for each line. In case we want more speed
1042 * we should rerender it flipped in a FBO and read the data back from the FBO. */
1043 if(!srcIsUpsideDown) {
1044 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1045 checkGLcall("glBindBufferARB");
1047 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1048 checkGLcall("glMapBufferARB");
1052 /* TODO: Merge this with the palettization loop below for P8 targets */
1053 if(!srcIsUpsideDown) {
1054 UINT len, off;
1055 /* glReadPixels returns the image upside down, and there is no way to prevent this.
1056 Flip the lines in software */
1057 len = (local_rect.right - local_rect.left) * bpp;
1058 off = local_rect.left * bpp;
1060 row = HeapAlloc(GetProcessHeap(), 0, len);
1061 if(!row) {
1062 ERR("Out of memory\n");
1063 if (This->resource.format_desc->format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
1064 LEAVE_GL();
1065 return;
1068 top = mem + pitch * local_rect.top;
1069 bottom = mem + pitch * (local_rect.bottom - 1);
1070 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
1071 memcpy(row, top + off, len);
1072 memcpy(top + off, bottom + off, len);
1073 memcpy(bottom + off, row, len);
1074 top += pitch;
1075 bottom -= pitch;
1077 HeapFree(GetProcessHeap(), 0, row);
1079 /* Unmap the temp PBO buffer */
1080 if(This->Flags & SFLAG_PBO) {
1081 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1082 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1086 LEAVE_GL();
1088 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
1089 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
1090 * the same color but we have no choice.
1091 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
1093 if ((This->resource.format_desc->format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice))
1095 const PALETTEENTRY *pal = NULL;
1096 DWORD width = pitch / 3;
1097 int x, y, c;
1099 if(This->palette) {
1100 pal = This->palette->palents;
1101 } else {
1102 ERR("Palette is missing, cannot perform inverse palette lookup\n");
1103 HeapFree(GetProcessHeap(), 0, mem);
1104 return ;
1107 for(y = local_rect.top; y < local_rect.bottom; y++) {
1108 for(x = local_rect.left; x < local_rect.right; x++) {
1109 /* start lines pixels */
1110 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
1111 const BYTE *green = blue + 1;
1112 const BYTE *red = green + 1;
1114 for(c = 0; c < 256; c++) {
1115 if(*red == pal[c].peRed &&
1116 *green == pal[c].peGreen &&
1117 *blue == pal[c].peBlue)
1119 *((BYTE *) dest + y * width + x) = c;
1120 break;
1125 HeapFree(GetProcessHeap(), 0, mem);
1129 /* Read the framebuffer contents into a texture */
1130 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This, BOOL srgb)
1132 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1133 IWineD3DSwapChainImpl *swapchain;
1134 int bpp;
1135 GLenum format, internal, type;
1136 CONVERT_TYPES convert;
1137 GLint prevRead;
1138 BOOL alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
1140 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
1142 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
1143 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
1144 * states in the stateblock, and no driver was found yet that had bugs in that regard.
1146 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
1147 surface_bind_and_dirtify(This, srgb);
1149 ENTER_GL();
1150 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1151 LEAVE_GL();
1153 /* Select the correct read buffer, and give some debug output.
1154 * There is no need to keep track of the current read buffer or reset it, every part of the code
1155 * that reads sets the read buffer as desired.
1157 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
1159 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1160 TRACE("Locking %#x buffer\n", buffer);
1162 ENTER_GL();
1163 glReadBuffer(buffer);
1164 checkGLcall("glReadBuffer");
1165 LEAVE_GL();
1167 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1168 } else {
1169 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1170 * Read from the back buffer
1172 TRACE("Locking offscreen render target\n");
1173 ENTER_GL();
1174 glReadBuffer(device->offscreenBuffer);
1175 checkGLcall("glReadBuffer");
1176 LEAVE_GL();
1179 if(!(This->Flags & alloc_flag)) {
1180 surface_allocate_surface(This, internal, This->pow2Width,
1181 This->pow2Height, format, type);
1182 This->Flags |= alloc_flag;
1185 ENTER_GL();
1186 /* If !SrcIsUpsideDown we should flip the surface.
1187 * This can be done using glCopyTexSubImage2D but this
1188 * is VERY slow, so don't do that. We should prevent
1189 * this code from getting called in such cases or perhaps
1190 * we can use FBOs */
1192 glCopyTexSubImage2D(This->texture_target, This->texture_level,
1193 0, 0, 0, 0, This->currentDesc.Width, This->currentDesc.Height);
1194 checkGLcall("glCopyTexSubImage2D");
1196 glReadBuffer(prevRead);
1197 checkGLcall("glReadBuffer");
1199 LEAVE_GL();
1200 TRACE("Updated target %d\n", This->texture_target);
1203 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
1204 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
1205 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
1206 * changed
1208 if(!(This->Flags & SFLAG_DYNLOCK)) {
1209 This->lockCount++;
1210 /* MAXLOCKCOUNT is defined in wined3d_private.h */
1211 if(This->lockCount > MAXLOCKCOUNT) {
1212 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
1213 This->Flags |= SFLAG_DYNLOCK;
1217 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
1218 * Also don't create a PBO for systemmem surfaces.
1220 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
1221 GLenum error;
1222 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1224 ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
1225 ENTER_GL();
1227 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
1228 error = glGetError();
1229 if(This->pbo == 0 || error != GL_NO_ERROR) {
1230 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
1233 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
1235 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1236 checkGLcall("glBindBufferARB");
1238 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1239 checkGLcall("glBufferDataARB");
1241 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1242 checkGLcall("glBindBufferARB");
1244 /* We don't need the system memory anymore and we can't even use it for PBOs */
1245 if(!(This->Flags & SFLAG_CLIENT)) {
1246 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1247 This->resource.heapMemory = NULL;
1249 This->resource.allocatedMemory = NULL;
1250 This->Flags |= SFLAG_PBO;
1251 LEAVE_GL();
1252 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
1253 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1254 * or a pbo to map
1256 if(!This->resource.heapMemory) {
1257 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1259 This->resource.allocatedMemory =
1260 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1261 if(This->Flags & SFLAG_INSYSMEM) {
1262 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1267 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1268 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1269 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1270 const RECT *pass_rect = pRect;
1272 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1274 /* This is also done in the base class, but we have to verify this before loading any data from
1275 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1276 * may interfere, and all other bad things may happen
1278 if (This->Flags & SFLAG_LOCKED) {
1279 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1280 return WINED3DERR_INVALIDCALL;
1282 This->Flags |= SFLAG_LOCKED;
1284 if (!(This->Flags & SFLAG_LOCKABLE))
1286 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1289 if (Flags & WINED3DLOCK_DISCARD) {
1290 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1291 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1292 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1293 This->Flags |= SFLAG_INSYSMEM;
1294 goto lock_end;
1297 if (This->Flags & SFLAG_INSYSMEM) {
1298 TRACE("Local copy is up to date, not downloading data\n");
1299 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1300 goto lock_end;
1303 /* IWineD3DSurface_LoadLocation() does not check if the rectangle specifies
1304 * the full surface. Most callers don't need that, so do it here. */
1305 if (pRect && pRect->top == 0 && pRect->left == 0
1306 && pRect->right == This->currentDesc.Width
1307 && pRect->bottom == This->currentDesc.Height)
1309 pass_rect = NULL;
1312 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
1313 && ((This->Flags & SFLAG_SWAPCHAIN) || iface == myDevice->render_targets[0])))
1315 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1318 lock_end:
1319 if(This->Flags & SFLAG_PBO) {
1320 ActivateContext(myDevice, NULL, CTXUSAGE_RESOURCELOAD);
1321 ENTER_GL();
1322 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1323 checkGLcall("glBindBufferARB");
1325 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1326 if(This->resource.allocatedMemory) {
1327 ERR("The surface already has PBO memory allocated!\n");
1330 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1331 checkGLcall("glMapBufferARB");
1333 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1334 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1335 checkGLcall("glBindBufferARB");
1337 LEAVE_GL();
1340 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1341 /* Don't dirtify */
1342 } else {
1343 IWineD3DBaseTexture *pBaseTexture;
1345 * Dirtify on lock
1346 * as seen in msdn docs
1348 surface_add_dirty_rect(iface, pRect);
1350 /** Dirtify Container if needed */
1351 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1352 TRACE("Making container dirty\n");
1353 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1354 IWineD3DBaseTexture_Release(pBaseTexture);
1355 } else {
1356 TRACE("Surface is standalone, no need to dirty the container\n");
1360 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1363 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1364 GLint prev_store;
1365 GLint prev_rasterpos[4];
1366 GLint skipBytes = 0;
1367 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1368 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1369 IWineD3DSwapChainImpl *swapchain;
1371 /* Activate the correct context for the render target */
1372 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1373 ENTER_GL();
1375 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
1376 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1377 TRACE("Unlocking %#x buffer\n", buffer);
1378 glDrawBuffer(buffer);
1379 checkGLcall("glDrawBuffer");
1381 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1382 } else {
1383 /* Primary offscreen render target */
1384 TRACE("Offscreen render target\n");
1385 glDrawBuffer(myDevice->offscreenBuffer);
1386 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1389 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1390 checkGLcall("glGetIntegerv");
1391 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1392 checkGLcall("glGetIntegerv");
1393 glPixelZoom(1.0f, -1.0f);
1394 checkGLcall("glPixelZoom");
1396 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1397 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1398 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1400 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1401 checkGLcall("glRasterPos3i");
1403 /* Some drivers(radeon dri, others?) don't like exceptions during
1404 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1405 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1406 * catch to put the dib section in InSync mode, which leads to a crash
1407 * and a blocked x server on my radeon card.
1409 * The following lines read the dib section so it is put in InSync mode
1410 * before glDrawPixels is called and the crash is prevented. There won't
1411 * be any interfering gdi accesses, because UnlockRect is called from
1412 * ReleaseDC, and the app won't use the dc any more afterwards.
1414 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1415 volatile BYTE read;
1416 read = This->resource.allocatedMemory[0];
1419 if(This->Flags & SFLAG_PBO) {
1420 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1421 checkGLcall("glBindBufferARB");
1424 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1425 if(This->Flags & SFLAG_LOCKED) {
1426 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1427 (This->lockedRect.bottom - This->lockedRect.top)-1,
1428 fmt, type,
1429 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1430 checkGLcall("glDrawPixels");
1431 } else {
1432 glDrawPixels(This->currentDesc.Width,
1433 This->currentDesc.Height,
1434 fmt, type, mem);
1435 checkGLcall("glDrawPixels");
1438 if(This->Flags & SFLAG_PBO) {
1439 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1440 checkGLcall("glBindBufferARB");
1443 glPixelZoom(1.0f, 1.0f);
1444 checkGLcall("glPixelZoom");
1446 glRasterPos3iv(&prev_rasterpos[0]);
1447 checkGLcall("glRasterPos3iv");
1449 /* Reset to previous pack row length */
1450 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1451 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH)");
1453 if(!swapchain) {
1454 glDrawBuffer(myDevice->offscreenBuffer);
1455 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1456 } else if(swapchain->backBuffer) {
1457 glDrawBuffer(GL_BACK);
1458 checkGLcall("glDrawBuffer(GL_BACK)");
1459 } else {
1460 glDrawBuffer(GL_FRONT);
1461 checkGLcall("glDrawBuffer(GL_FRONT)");
1463 LEAVE_GL();
1465 return;
1468 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1469 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1470 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1471 BOOL fullsurface;
1473 if (!(This->Flags & SFLAG_LOCKED)) {
1474 WARN("trying to Unlock an unlocked surf@%p\n", This);
1475 return WINEDDERR_NOTLOCKED;
1478 if (This->Flags & SFLAG_PBO) {
1479 TRACE("Freeing PBO memory\n");
1480 ActivateContext(myDevice, NULL, CTXUSAGE_RESOURCELOAD);
1481 ENTER_GL();
1482 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1483 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1484 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1485 checkGLcall("glUnmapBufferARB");
1486 LEAVE_GL();
1487 This->resource.allocatedMemory = NULL;
1490 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1492 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1493 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1494 goto unlock_end;
1497 if ((This->Flags & SFLAG_SWAPCHAIN) || (myDevice->render_targets && iface == myDevice->render_targets[0]))
1499 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1500 static BOOL warned = FALSE;
1501 if(!warned) {
1502 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1503 warned = TRUE;
1505 goto unlock_end;
1508 if(This->dirtyRect.left == 0 &&
1509 This->dirtyRect.top == 0 &&
1510 This->dirtyRect.right == This->currentDesc.Width &&
1511 This->dirtyRect.bottom == This->currentDesc.Height) {
1512 fullsurface = TRUE;
1513 } else {
1514 /* TODO: Proper partial rectangle tracking */
1515 fullsurface = FALSE;
1516 This->Flags |= SFLAG_INSYSMEM;
1519 switch(wined3d_settings.rendertargetlock_mode) {
1520 case RTL_READTEX:
1521 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1522 /* drop through */
1524 case RTL_READDRAW:
1525 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1526 break;
1529 if(!fullsurface) {
1530 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1531 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1532 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1533 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1534 * not fully up to date because only a subrectangle was read in LockRect.
1536 This->Flags &= ~SFLAG_INSYSMEM;
1537 This->Flags |= SFLAG_INDRAWABLE;
1540 This->dirtyRect.left = This->currentDesc.Width;
1541 This->dirtyRect.top = This->currentDesc.Height;
1542 This->dirtyRect.right = 0;
1543 This->dirtyRect.bottom = 0;
1544 } else if(iface == myDevice->stencilBufferTarget) {
1545 FIXME("Depth Stencil buffer locking is not implemented\n");
1546 } else {
1547 /* The rest should be a normal texture */
1548 IWineD3DBaseTextureImpl *impl;
1549 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1550 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1551 * states need resetting
1553 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1554 if(impl->baseTexture.bindCount) {
1555 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1557 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1561 unlock_end:
1562 This->Flags &= ~SFLAG_LOCKED;
1563 memset(&This->lockedRect, 0, sizeof(RECT));
1565 /* Overlays have to be redrawn manually after changes with the GL implementation */
1566 if(This->overlay_dest) {
1567 IWineD3DSurface_DrawOverlay(iface);
1569 return WINED3D_OK;
1572 static HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC)
1574 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1575 WINED3DLOCKED_RECT lock;
1576 HRESULT hr;
1577 RGBQUAD col[256];
1579 TRACE("(%p)->(%p)\n",This,pHDC);
1581 if(This->Flags & SFLAG_USERPTR) {
1582 ERR("Not supported on surfaces with an application-provided surfaces\n");
1583 return WINEDDERR_NODC;
1586 /* Give more detailed info for ddraw */
1587 if (This->Flags & SFLAG_DCINUSE)
1588 return WINEDDERR_DCALREADYCREATED;
1590 /* Can't GetDC if the surface is locked */
1591 if (This->Flags & SFLAG_LOCKED)
1592 return WINED3DERR_INVALIDCALL;
1594 memset(&lock, 0, sizeof(lock)); /* To be sure */
1596 /* Create a DIB section if there isn't a hdc yet */
1597 if(!This->hDC) {
1598 hr = IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1599 if(FAILED(hr)) return WINED3DERR_INVALIDCALL;
1600 if(This->Flags & SFLAG_CLIENT) {
1601 surface_internal_preload(iface, SRGB_RGB);
1604 /* Use the dib section from now on if we are not using a PBO */
1605 if(!(This->Flags & SFLAG_PBO))
1606 This->resource.allocatedMemory = This->dib.bitmap_data;
1609 /* Lock the surface */
1610 hr = IWineD3DSurface_LockRect(iface,
1611 &lock,
1612 NULL,
1615 if(This->Flags & SFLAG_PBO) {
1616 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1617 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1620 if(FAILED(hr)) {
1621 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1622 /* keep the dib section */
1623 return hr;
1626 if (This->resource.format_desc->format == WINED3DFMT_P8
1627 || This->resource.format_desc->format == WINED3DFMT_A8P8)
1629 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1630 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1631 unsigned int n;
1632 const PALETTEENTRY *pal = NULL;
1634 if(This->palette) {
1635 pal = This->palette->palents;
1636 } else {
1637 IWineD3DSurfaceImpl *dds_primary;
1638 IWineD3DSwapChainImpl *swapchain;
1639 swapchain = (IWineD3DSwapChainImpl *)This->resource.wineD3DDevice->swapchains[0];
1640 dds_primary = (IWineD3DSurfaceImpl *)swapchain->frontBuffer;
1641 if (dds_primary && dds_primary->palette)
1642 pal = dds_primary->palette->palents;
1645 if (pal) {
1646 for (n=0; n<256; n++) {
1647 col[n].rgbRed = pal[n].peRed;
1648 col[n].rgbGreen = pal[n].peGreen;
1649 col[n].rgbBlue = pal[n].peBlue;
1650 col[n].rgbReserved = 0;
1652 SetDIBColorTable(This->hDC, 0, 256, col);
1656 *pHDC = This->hDC;
1657 TRACE("returning %p\n",*pHDC);
1658 This->Flags |= SFLAG_DCINUSE;
1660 return WINED3D_OK;
1663 static HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC)
1665 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1667 TRACE("(%p)->(%p)\n",This,hDC);
1669 if (!(This->Flags & SFLAG_DCINUSE))
1670 return WINEDDERR_NODC;
1672 if (This->hDC !=hDC) {
1673 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1674 return WINEDDERR_NODC;
1677 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1678 /* Copy the contents of the DIB over to the PBO */
1679 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1682 /* we locked first, so unlock now */
1683 IWineD3DSurface_UnlockRect(iface);
1685 This->Flags &= ~SFLAG_DCINUSE;
1687 return WINED3D_OK;
1690 /* ******************************************************
1691 IWineD3DSurface Internal (No mapping to directx api) parts follow
1692 ****************************************************** */
1694 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) {
1695 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1696 const struct GlPixelFormatDesc *glDesc = This->resource.format_desc;
1697 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1699 /* Default values: From the surface */
1700 *format = glDesc->glFormat;
1701 *type = glDesc->glType;
1702 *convert = NO_CONVERSION;
1703 *target_bpp = glDesc->byte_count;
1705 if(srgb_mode) {
1706 *internal = glDesc->glGammaInternal;
1708 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET
1709 && !(This->Flags & SFLAG_SWAPCHAIN))
1711 *internal = glDesc->rtInternal;
1712 } else {
1713 *internal = glDesc->glInternal;
1716 /* Ok, now look if we have to do any conversion */
1717 switch(This->resource.format_desc->format)
1719 case WINED3DFMT_P8:
1720 /* ****************
1721 Paletted Texture
1722 **************** */
1724 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1725 * of the two is available make sure texturing is requested as neither of the two works in
1726 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1727 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1728 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1729 * conflicts with this.
1731 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) ||
1732 (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) &&
1733 device->render_targets &&
1734 This == (IWineD3DSurfaceImpl*)device->render_targets[0])) ||
1735 colorkey_active || !use_texturing ) {
1736 *format = GL_RGBA;
1737 *internal = GL_RGBA;
1738 *type = GL_UNSIGNED_BYTE;
1739 *target_bpp = 4;
1740 if(colorkey_active) {
1741 *convert = CONVERT_PALETTED_CK;
1742 } else {
1743 *convert = CONVERT_PALETTED;
1746 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1747 *format = GL_ALPHA;
1748 *type = GL_UNSIGNED_BYTE;
1749 *target_bpp = 1;
1752 break;
1754 case WINED3DFMT_R3G3B2:
1755 /* **********************
1756 GL_UNSIGNED_BYTE_3_3_2
1757 ********************** */
1758 if (colorkey_active) {
1759 /* This texture format will never be used.. So do not care about color keying
1760 up until the point in time it will be needed :-) */
1761 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1763 break;
1765 case WINED3DFMT_R5G6B5:
1766 if (colorkey_active) {
1767 *convert = CONVERT_CK_565;
1768 *format = GL_RGBA;
1769 *internal = GL_RGB5_A1;
1770 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1772 break;
1774 case WINED3DFMT_X1R5G5B5:
1775 if (colorkey_active) {
1776 *convert = CONVERT_CK_5551;
1777 *format = GL_BGRA;
1778 *internal = GL_RGB5_A1;
1779 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1781 break;
1783 case WINED3DFMT_R8G8B8:
1784 if (colorkey_active) {
1785 *convert = CONVERT_CK_RGB24;
1786 *format = GL_RGBA;
1787 *internal = GL_RGBA8;
1788 *type = GL_UNSIGNED_INT_8_8_8_8;
1789 *target_bpp = 4;
1791 break;
1793 case WINED3DFMT_X8R8G8B8:
1794 if (colorkey_active) {
1795 *convert = CONVERT_RGB32_888;
1796 *format = GL_RGBA;
1797 *internal = GL_RGBA8;
1798 *type = GL_UNSIGNED_INT_8_8_8_8;
1800 break;
1802 case WINED3DFMT_R8G8_SNORM:
1803 if (GL_SUPPORT(NV_TEXTURE_SHADER)) break;
1804 *convert = CONVERT_V8U8;
1805 *format = GL_BGR;
1806 *type = GL_UNSIGNED_BYTE;
1807 *target_bpp = 3;
1808 break;
1810 case WINED3DFMT_L6V5U5:
1811 *convert = CONVERT_L6V5U5;
1812 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1813 *target_bpp = 3;
1814 /* Use format and types from table */
1815 } else {
1816 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1817 *target_bpp = 2;
1818 *format = GL_RGB;
1819 *type = GL_UNSIGNED_SHORT_5_6_5;
1821 break;
1823 case WINED3DFMT_X8L8V8U8:
1824 *convert = CONVERT_X8L8V8U8;
1825 *target_bpp = 4;
1826 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1827 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1828 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1829 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1830 * the needed type and format parameter, so the internal format contains a
1831 * 4th component, which is returned as alpha
1833 } else {
1834 *format = GL_BGRA;
1835 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1837 break;
1839 case WINED3DFMT_R8G8B8A8_SNORM:
1840 if (GL_SUPPORT(NV_TEXTURE_SHADER)) break;
1841 *convert = CONVERT_Q8W8V8U8;
1842 *format = GL_BGRA;
1843 *type = GL_UNSIGNED_BYTE;
1844 *target_bpp = 4;
1845 break;
1847 case WINED3DFMT_R16G16_SNORM:
1848 if (GL_SUPPORT(NV_TEXTURE_SHADER)) break;
1849 *convert = CONVERT_V16U16;
1850 *format = GL_BGR;
1851 *type = GL_UNSIGNED_SHORT;
1852 *target_bpp = 6;
1853 break;
1855 case WINED3DFMT_A4L4:
1856 /* A4L4 exists as an internal gl format, but for some reason there is not
1857 * format+type combination to load it. Thus convert it to A8L8, then load it
1858 * with A4L4 internal, but A8L8 format+type
1860 *convert = CONVERT_A4L4;
1861 *format = GL_LUMINANCE_ALPHA;
1862 *type = GL_UNSIGNED_BYTE;
1863 *target_bpp = 2;
1864 break;
1866 case WINED3DFMT_R16G16_UNORM:
1867 *convert = CONVERT_G16R16;
1868 *format = GL_RGB;
1869 *type = GL_UNSIGNED_SHORT;
1870 *target_bpp = 6;
1871 break;
1873 case WINED3DFMT_R16G16_FLOAT:
1874 *convert = CONVERT_R16G16F;
1875 *format = GL_RGB;
1876 *type = GL_HALF_FLOAT_ARB;
1877 *target_bpp = 6;
1878 break;
1880 case WINED3DFMT_R32G32_FLOAT:
1881 *convert = CONVERT_R32G32F;
1882 *format = GL_RGB;
1883 *type = GL_FLOAT;
1884 *target_bpp = 12;
1885 break;
1887 case WINED3DFMT_D15S1:
1888 if (GL_SUPPORT(EXT_PACKED_DEPTH_STENCIL))
1890 *convert = CONVERT_D15S1;
1891 *target_bpp = 4;
1893 break;
1895 case WINED3DFMT_D24X4S4:
1896 if (GL_SUPPORT(EXT_PACKED_DEPTH_STENCIL))
1898 *convert = CONVERT_D24X4S4;
1900 break;
1902 case WINED3DFMT_D24FS8:
1903 if (GL_SUPPORT(ARB_DEPTH_BUFFER_FLOAT))
1905 *convert = CONVERT_D24FS8;
1906 *target_bpp = 8;
1908 break;
1910 default:
1911 break;
1914 return WINED3D_OK;
1917 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey)
1919 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1920 IWineD3DPaletteImpl *pal = This->palette;
1921 BOOL index_in_alpha = FALSE;
1922 unsigned int i;
1924 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
1925 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
1926 * is slow. Further RGB->P8 conversion is not possible because palettes can have
1927 * duplicate entries. Store the color key in the unused alpha component to speed the
1928 * download up and to make conversion unneeded. */
1929 index_in_alpha = primary_render_target_is_p8(device);
1931 if (!pal)
1933 UINT dxVersion = ((IWineD3DImpl *)device->wineD3D)->dxVersion;
1935 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
1936 if (dxVersion <= 7)
1938 ERR("This code should never get entered for DirectDraw!, expect problems\n");
1939 if (index_in_alpha)
1941 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
1942 * there's no palette at this time. */
1943 for (i = 0; i < 256; i++) table[i][3] = i;
1946 else
1948 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
1949 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
1950 * capability flag is present (wine does advertise this capability) */
1951 for (i = 0; i < 256; ++i)
1953 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1954 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1955 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1956 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
1960 else
1962 TRACE("Using surface palette %p\n", pal);
1963 /* Get the surface's palette */
1964 for (i = 0; i < 256; ++i)
1966 table[i][0] = pal->palents[i].peRed;
1967 table[i][1] = pal->palents[i].peGreen;
1968 table[i][2] = pal->palents[i].peBlue;
1970 /* When index_in_alpha is set the palette index is stored in the
1971 * alpha component. In case of a readback we can then read
1972 * GL_ALPHA. Color keying is handled in BltOverride using a
1973 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
1974 * color key itself is passed to glAlphaFunc in other cases the
1975 * alpha component of pixels that should be masked away is set to 0. */
1976 if (index_in_alpha)
1978 table[i][3] = i;
1980 else if (colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue)
1981 && (i <= This->SrcBltCKey.dwColorSpaceHighValue))
1983 table[i][3] = 0x00;
1985 else if(pal->Flags & WINEDDPCAPS_ALPHA)
1987 table[i][3] = pal->palents[i].peFlags;
1989 else
1991 table[i][3] = 0xFF;
1997 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
1998 UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This)
2000 const BYTE *source;
2001 BYTE *dest;
2002 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
2004 switch (convert) {
2005 case NO_CONVERSION:
2007 memcpy(dst, src, pitch * height);
2008 break;
2010 case CONVERT_PALETTED:
2011 case CONVERT_PALETTED_CK:
2013 IWineD3DPaletteImpl* pal = This->palette;
2014 BYTE table[256][4];
2015 unsigned int x, y;
2017 if( pal == NULL) {
2018 /* TODO: If we are a sublevel, try to get the palette from level 0 */
2021 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2023 for (y = 0; y < height; y++)
2025 source = src + pitch * y;
2026 dest = dst + outpitch * y;
2027 /* This is an 1 bpp format, using the width here is fine */
2028 for (x = 0; x < width; x++) {
2029 BYTE color = *source++;
2030 *dest++ = table[color][0];
2031 *dest++ = table[color][1];
2032 *dest++ = table[color][2];
2033 *dest++ = table[color][3];
2037 break;
2039 case CONVERT_CK_565:
2041 /* Converting the 565 format in 5551 packed to emulate color-keying.
2043 Note : in all these conversion, it would be best to average the averaging
2044 pixels to get the color of the pixel that will be color-keyed to
2045 prevent 'color bleeding'. This will be done later on if ever it is
2046 too visible.
2048 Note2: Nvidia documents say that their driver does not support alpha + color keying
2049 on the same surface and disables color keying in such a case
2051 unsigned int x, y;
2052 const WORD *Source;
2053 WORD *Dest;
2055 TRACE("Color keyed 565\n");
2057 for (y = 0; y < height; y++) {
2058 Source = (const WORD *)(src + y * pitch);
2059 Dest = (WORD *) (dst + y * outpitch);
2060 for (x = 0; x < width; x++ ) {
2061 WORD color = *Source++;
2062 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
2063 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2064 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2065 *Dest |= 0x0001;
2067 Dest++;
2071 break;
2073 case CONVERT_CK_5551:
2075 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
2076 unsigned int x, y;
2077 const WORD *Source;
2078 WORD *Dest;
2079 TRACE("Color keyed 5551\n");
2080 for (y = 0; y < height; y++) {
2081 Source = (const WORD *)(src + y * pitch);
2082 Dest = (WORD *) (dst + y * outpitch);
2083 for (x = 0; x < width; x++ ) {
2084 WORD color = *Source++;
2085 *Dest = color;
2086 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2087 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2088 *Dest |= (1 << 15);
2090 else {
2091 *Dest &= ~(1 << 15);
2093 Dest++;
2097 break;
2099 case CONVERT_CK_RGB24:
2101 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
2102 unsigned int x, y;
2103 for (y = 0; y < height; y++)
2105 source = src + pitch * y;
2106 dest = dst + outpitch * y;
2107 for (x = 0; x < width; x++) {
2108 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
2109 DWORD dstcolor = color << 8;
2110 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2111 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2112 dstcolor |= 0xff;
2114 *(DWORD*)dest = dstcolor;
2115 source += 3;
2116 dest += 4;
2120 break;
2122 case CONVERT_RGB32_888:
2124 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
2125 unsigned int x, y;
2126 for (y = 0; y < height; y++)
2128 source = src + pitch * y;
2129 dest = dst + outpitch * y;
2130 for (x = 0; x < width; x++) {
2131 DWORD color = 0xffffff & *(const DWORD*)source;
2132 DWORD dstcolor = color << 8;
2133 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2134 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2135 dstcolor |= 0xff;
2137 *(DWORD*)dest = dstcolor;
2138 source += 4;
2139 dest += 4;
2143 break;
2145 case CONVERT_V8U8:
2147 unsigned int x, y;
2148 const short *Source;
2149 unsigned char *Dest;
2150 for(y = 0; y < height; y++) {
2151 Source = (const short *)(src + y * pitch);
2152 Dest = dst + y * outpitch;
2153 for (x = 0; x < width; x++ ) {
2154 long color = (*Source++);
2155 /* B */ Dest[0] = 0xff;
2156 /* G */ Dest[1] = (color >> 8) + 128; /* V */
2157 /* R */ Dest[2] = (color) + 128; /* U */
2158 Dest += 3;
2161 break;
2164 case CONVERT_V16U16:
2166 unsigned int x, y;
2167 const DWORD *Source;
2168 unsigned short *Dest;
2169 for(y = 0; y < height; y++) {
2170 Source = (const DWORD *)(src + y * pitch);
2171 Dest = (unsigned short *) (dst + y * outpitch);
2172 for (x = 0; x < width; x++ ) {
2173 DWORD color = (*Source++);
2174 /* B */ Dest[0] = 0xffff;
2175 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
2176 /* R */ Dest[2] = (color ) + 32768; /* U */
2177 Dest += 3;
2180 break;
2183 case CONVERT_Q8W8V8U8:
2185 unsigned int x, y;
2186 const DWORD *Source;
2187 unsigned char *Dest;
2188 for(y = 0; y < height; y++) {
2189 Source = (const DWORD *)(src + y * pitch);
2190 Dest = dst + y * outpitch;
2191 for (x = 0; x < width; x++ ) {
2192 long color = (*Source++);
2193 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
2194 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2195 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2196 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
2197 Dest += 4;
2200 break;
2203 case CONVERT_L6V5U5:
2205 unsigned int x, y;
2206 const WORD *Source;
2207 unsigned char *Dest;
2209 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2210 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
2211 * fixed function and shaders without further conversion once the surface is
2212 * loaded
2214 for(y = 0; y < height; y++) {
2215 Source = (const WORD *)(src + y * pitch);
2216 Dest = dst + y * outpitch;
2217 for (x = 0; x < width; x++ ) {
2218 short color = (*Source++);
2219 unsigned char l = ((color >> 10) & 0xfc);
2220 char v = ((color >> 5) & 0x3e);
2221 char u = ((color ) & 0x1f);
2223 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
2224 * and doubles the positive range. Thus shift left only once, gl does the 2nd
2225 * shift. GL reads a signed value and converts it into an unsigned value.
2227 /* M */ Dest[2] = l << 1;
2229 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
2230 * from 5 bit values to 8 bit values.
2232 /* V */ Dest[1] = v << 3;
2233 /* U */ Dest[0] = u << 3;
2234 Dest += 3;
2237 } else {
2238 for(y = 0; y < height; y++) {
2239 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
2240 Source = (const WORD *)(src + y * pitch);
2241 for (x = 0; x < width; x++ ) {
2242 short color = (*Source++);
2243 unsigned char l = ((color >> 10) & 0xfc);
2244 short v = ((color >> 5) & 0x3e);
2245 short u = ((color ) & 0x1f);
2246 short v_conv = v + 16;
2247 short u_conv = u + 16;
2249 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
2250 Dest_s += 1;
2254 break;
2257 case CONVERT_X8L8V8U8:
2259 unsigned int x, y;
2260 const DWORD *Source;
2261 unsigned char *Dest;
2263 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2264 /* This implementation works with the fixed function pipeline and shaders
2265 * without further modification after converting the surface.
2267 for(y = 0; y < height; y++) {
2268 Source = (const DWORD *)(src + y * pitch);
2269 Dest = dst + y * outpitch;
2270 for (x = 0; x < width; x++ ) {
2271 long color = (*Source++);
2272 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
2273 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
2274 /* U */ Dest[0] = (color & 0xff); /* U */
2275 /* I */ Dest[3] = 255; /* X */
2276 Dest += 4;
2279 } else {
2280 /* Doesn't work correctly with the fixed function pipeline, but can work in
2281 * shaders if the shader is adjusted. (There's no use for this format in gl's
2282 * standard fixed function pipeline anyway).
2284 for(y = 0; y < height; y++) {
2285 Source = (const DWORD *)(src + y * pitch);
2286 Dest = dst + y * outpitch;
2287 for (x = 0; x < width; x++ ) {
2288 long color = (*Source++);
2289 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
2290 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2291 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2292 Dest += 4;
2296 break;
2299 case CONVERT_A4L4:
2301 unsigned int x, y;
2302 const unsigned char *Source;
2303 unsigned char *Dest;
2304 for(y = 0; y < height; y++) {
2305 Source = src + y * pitch;
2306 Dest = dst + y * outpitch;
2307 for (x = 0; x < width; x++ ) {
2308 unsigned char color = (*Source++);
2309 /* A */ Dest[1] = (color & 0xf0) << 0;
2310 /* L */ Dest[0] = (color & 0x0f) << 4;
2311 Dest += 2;
2314 break;
2317 case CONVERT_G16R16:
2318 case CONVERT_R16G16F:
2320 unsigned int x, y;
2321 const WORD *Source;
2322 WORD *Dest;
2324 for(y = 0; y < height; y++) {
2325 Source = (const WORD *)(src + y * pitch);
2326 Dest = (WORD *) (dst + y * outpitch);
2327 for (x = 0; x < width; x++ ) {
2328 WORD green = (*Source++);
2329 WORD red = (*Source++);
2330 Dest[0] = green;
2331 Dest[1] = red;
2332 /* Strictly speaking not correct for R16G16F, but it doesn't matter because the
2333 * shader overwrites it anyway
2335 Dest[2] = 0xffff;
2336 Dest += 3;
2339 break;
2342 case CONVERT_R32G32F:
2344 unsigned int x, y;
2345 const float *Source;
2346 float *Dest;
2347 for(y = 0; y < height; y++) {
2348 Source = (const float *)(src + y * pitch);
2349 Dest = (float *) (dst + y * outpitch);
2350 for (x = 0; x < width; x++ ) {
2351 float green = (*Source++);
2352 float red = (*Source++);
2353 Dest[0] = green;
2354 Dest[1] = red;
2355 Dest[2] = 1.0f;
2356 Dest += 3;
2359 break;
2362 case CONVERT_D15S1:
2364 unsigned int x, y;
2366 for (y = 0; y < height; ++y)
2368 const WORD *source = (const WORD *)(src + y * pitch);
2369 DWORD *dest = (DWORD *)(dst + y * outpitch);
2371 for (x = 0; x < width; ++x)
2373 /* The depth data is normalized, so needs to be scaled,
2374 * the stencil data isn't. Scale depth data by
2375 * (2^24-1)/(2^15-1) ~~ (2^9 + 2^-6). */
2376 WORD d15 = source[x] >> 1;
2377 DWORD d24 = (d15 << 9) + (d15 >> 6);
2378 dest[x] = (d24 << 8) | (source[x] & 0x1);
2381 break;
2384 case CONVERT_D24X4S4:
2386 unsigned int x, y;
2388 for (y = 0; y < height; ++y)
2390 const DWORD *source = (const DWORD *)(src + y * pitch);
2391 DWORD *dest = (DWORD *)(dst + y * outpitch);
2393 for (x = 0; x < width; ++x)
2395 /* Just need to clear out the X4 part. */
2396 dest[x] = source[x] & ~0xf0;
2399 break;
2402 case CONVERT_D24FS8:
2404 unsigned int x, y;
2406 for (y = 0; y < height; ++y)
2408 const DWORD *source = (const DWORD *)(src + y * pitch);
2409 float *dest_f = (float *)(dst + y * outpitch);
2410 DWORD *dest_s = (DWORD *)(dst + y * outpitch);
2412 for (x = 0; x < width; ++x)
2414 dest_f[x * 2] = float_24_to_32((source[x] & 0xffffff00) >> 8);
2415 dest_s[x * 2 + 1] = source[x] & 0xff;
2418 break;
2421 default:
2422 ERR("Unsupported conversion type %#x.\n", convert);
2424 return WINED3D_OK;
2427 /* This function is used in case of 8bit paletted textures to upload the palette.
2428 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2429 extensions like ATI_fragment_shaders is possible.
2431 /* Context activation is done by the caller. */
2432 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2433 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2434 BYTE table[256][4];
2435 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2437 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2439 /* Try to use the paletted texture extension */
2440 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2442 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2443 ENTER_GL();
2444 GL_EXTCALL(glColorTableEXT(This->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
2445 LEAVE_GL();
2447 else
2449 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2450 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2451 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2453 ENTER_GL();
2455 /* Create the fragment program if we don't have it */
2456 if(!device->paletteConversionShader)
2458 const char *fragment_palette_conversion =
2459 "!!ARBfp1.0\n"
2460 "TEMP index;\n"
2461 /* { 255/256, 0.5/255*255/256, 0, 0 } */
2462 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n"
2463 /* The alpha-component contains the palette index */
2464 "TEX index, fragment.texcoord[0], texture[0], 2D;\n"
2465 /* Scale the index by 255/256 and add a bias of '0.5' in order to sample in the middle */
2466 "MAD index.a, index.a, constants.x, constants.y;\n"
2467 /* Use the alpha-component as an index in the palette to get the final color */
2468 "TEX result.color, index.a, texture[1], 1D;\n"
2469 "END";
2471 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2472 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2473 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2474 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), fragment_palette_conversion));
2475 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2478 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2479 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2481 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2482 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2484 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2485 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2486 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2487 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2489 /* Switch back to unit 0 in which the 2D texture will be stored. */
2490 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2492 /* Rebind the texture because it isn't bound anymore */
2493 glBindTexture(This->texture_target, This->texture_name);
2495 LEAVE_GL();
2499 BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2500 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2502 if (This->palette || (This->resource.format_desc->format != WINED3DFMT_P8
2503 && This->resource.format_desc->format != WINED3DFMT_A8P8))
2505 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2506 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2508 return FALSE;
2511 if(This->palette9) {
2512 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2513 return FALSE;
2515 } else {
2516 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2518 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2519 return TRUE;
2522 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2523 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2524 DWORD flag = srgb_mode ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2526 if (!(This->Flags & flag)) {
2527 TRACE("Reloading because surface is dirty\n");
2528 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2529 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2530 /* Reload: vice versa OR */
2531 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2532 /* Also reload: Color key is active AND the color key has changed */
2533 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2534 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2535 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2536 TRACE("Reloading because of color keying\n");
2537 /* To perform the color key conversion we need a sysmem copy of
2538 * the surface. Make sure we have it
2541 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2542 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2543 /* TODO: This is not necessarily needed with hw palettized texture support */
2544 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2545 } else {
2546 TRACE("surface is already in texture\n");
2547 return WINED3D_OK;
2550 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2551 * These resources are not bound by device size or format restrictions. Because of this,
2552 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2553 * However, these resources can always be created, locked, and copied.
2555 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2557 FIXME("(%p) Operation not supported for scratch textures\n",This);
2558 return WINED3DERR_INVALIDCALL;
2561 IWineD3DSurface_LoadLocation(iface, flag, NULL /* no partial locking for textures yet */);
2563 #if 0
2565 static unsigned int gen = 0;
2566 char buffer[4096];
2567 ++gen;
2568 if ((gen % 10) == 0) {
2569 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm",
2570 This, This->texture_target, This->texture_level, gen);
2571 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2574 * debugging crash code
2575 if (gen == 250) {
2576 void** test = NULL;
2577 *test = 0;
2581 #endif
2583 if (!(This->Flags & SFLAG_DONOTFREE)) {
2584 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2585 This->resource.allocatedMemory = NULL;
2586 This->resource.heapMemory = NULL;
2587 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2590 return WINED3D_OK;
2593 /* Context activation is done by the caller. */
2594 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface, BOOL srgb) {
2595 /* TODO: check for locks */
2596 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2597 IWineD3DBaseTexture *baseTexture = NULL;
2598 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2600 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2601 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2602 TRACE("Passing to container\n");
2603 IWineD3DBaseTexture_BindTexture(baseTexture, srgb);
2604 IWineD3DBaseTexture_Release(baseTexture);
2605 } else {
2606 GLuint *name;
2607 TRACE("(%p) : Binding surface\n", This);
2609 name = srgb ? &This->texture_name_srgb : &This->texture_name;
2610 if(!device->isInDraw) {
2611 ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
2614 ENTER_GL();
2616 if (!This->texture_level)
2618 if (!*name) {
2619 glGenTextures(1, name);
2620 checkGLcall("glGenTextures");
2621 TRACE("Surface %p given name %d\n", This, *name);
2623 glBindTexture(This->texture_target, *name);
2624 checkGLcall("glBindTexture");
2625 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2626 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2627 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2628 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2629 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2630 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2631 glTexParameteri(This->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2632 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2633 glTexParameteri(This->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2634 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2636 /* This is where we should be reducing the amount of GLMemoryUsed */
2637 } else if (*name) {
2638 /* Mipmap surfaces should have a base texture container */
2639 ERR("Mipmap surface has a glTexture bound to it!\n");
2642 glBindTexture(This->texture_target, *name);
2643 checkGLcall("glBindTexture");
2645 LEAVE_GL();
2647 return;
2650 #include <errno.h>
2651 #include <stdio.h>
2652 static HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename)
2654 FILE* f = NULL;
2655 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2656 char *allocatedMemory;
2657 const char *textureRow;
2658 IWineD3DSwapChain *swapChain = NULL;
2659 int width, height, i, y;
2660 GLuint tmpTexture = 0;
2661 DWORD color;
2662 /*FIXME:
2663 Textures may not be stored in ->allocatedgMemory and a GlTexture
2664 so we should lock the surface before saving a snapshot, or at least check that
2666 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2667 by calling GetTexImage and in compressed form by calling
2668 GetCompressedTexImageARB. Queried compressed images can be saved and
2669 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2670 texture images do not need to be processed by the GL and should
2671 significantly improve texture loading performance relative to uncompressed
2672 images. */
2674 /* Setup the width and height to be the internal texture width and height. */
2675 width = This->pow2Width;
2676 height = This->pow2Height;
2677 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2678 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2680 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2681 /* if were not a real texture then read the back buffer into a real texture */
2682 /* we don't want to interfere with the back buffer so read the data into a temporary
2683 * texture and then save the data out of the temporary texture
2685 GLint prevRead;
2686 ENTER_GL();
2687 TRACE("(%p) Reading render target into texture\n", This);
2689 glGenTextures(1, &tmpTexture);
2690 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2692 glTexImage2D(GL_TEXTURE_2D,
2694 GL_RGBA,
2695 width,
2696 height,
2697 0/*border*/,
2698 GL_RGBA,
2699 GL_UNSIGNED_INT_8_8_8_8_REV,
2700 NULL);
2702 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2703 checkGLcall("glGetIntegerv");
2704 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2705 checkGLcall("glReadBuffer");
2706 glCopyTexImage2D(GL_TEXTURE_2D,
2708 GL_RGBA,
2711 width,
2712 height,
2715 checkGLcall("glCopyTexImage2D");
2716 glReadBuffer(prevRead);
2717 LEAVE_GL();
2719 } else { /* bind the real texture, and make sure it up to date */
2720 surface_internal_preload(iface, SRGB_RGB);
2721 surface_bind_and_dirtify(This, FALSE);
2723 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2724 ENTER_GL();
2725 FIXME("Saving texture level %d width %d height %d\n", This->texture_level, width, height);
2726 glGetTexImage(GL_TEXTURE_2D, This->texture_level, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, allocatedMemory);
2727 checkGLcall("glGetTexImage");
2728 if (tmpTexture) {
2729 glBindTexture(GL_TEXTURE_2D, 0);
2730 glDeleteTextures(1, &tmpTexture);
2732 LEAVE_GL();
2734 f = fopen(filename, "w+");
2735 if (NULL == f) {
2736 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2737 return WINED3DERR_INVALIDCALL;
2739 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2740 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format_desc->format));
2741 /* TGA header */
2742 fputc(0,f);
2743 fputc(0,f);
2744 fputc(2,f);
2745 fputc(0,f);
2746 fputc(0,f);
2747 fputc(0,f);
2748 fputc(0,f);
2749 fputc(0,f);
2750 fputc(0,f);
2751 fputc(0,f);
2752 fputc(0,f);
2753 fputc(0,f);
2754 /* short width*/
2755 fwrite(&width,2,1,f);
2756 /* short height */
2757 fwrite(&height,2,1,f);
2758 /* format rgba */
2759 fputc(0x20,f);
2760 fputc(0x28,f);
2761 /* raw data */
2762 /* 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 */
2763 if(swapChain)
2764 textureRow = allocatedMemory + (width * (height - 1) *4);
2765 else
2766 textureRow = allocatedMemory;
2767 for (y = 0 ; y < height; y++) {
2768 for (i = 0; i < width; i++) {
2769 color = *((const DWORD*)textureRow);
2770 fputc((color >> 16) & 0xFF, f); /* B */
2771 fputc((color >> 8) & 0xFF, f); /* G */
2772 fputc((color >> 0) & 0xFF, f); /* R */
2773 fputc((color >> 24) & 0xFF, f); /* A */
2774 textureRow += 4;
2776 /* take two rows of the pointer to the texture memory */
2777 if(swapChain)
2778 (textureRow-= width << 3);
2781 TRACE("Closing file\n");
2782 fclose(f);
2784 if(swapChain) {
2785 IWineD3DSwapChain_Release(swapChain);
2787 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2788 return WINED3D_OK;
2791 static HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2792 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2793 HRESULT hr;
2795 TRACE("(%p) : Calling base function first\n", This);
2796 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2797 if(SUCCEEDED(hr)) {
2798 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2799 TRACE("(%p) : glFormat %d, glFormatInternal %d, glType %d\n", This, This->resource.format_desc->glFormat,
2800 This->resource.format_desc->glInternal, This->resource.format_desc->glType);
2802 return hr;
2805 static HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2806 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2808 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2809 WARN("Surface is locked or the HDC is in use\n");
2810 return WINED3DERR_INVALIDCALL;
2813 if(Mem && Mem != This->resource.allocatedMemory) {
2814 void *release = NULL;
2816 /* Do I have to copy the old surface content? */
2817 if(This->Flags & SFLAG_DIBSECTION) {
2818 /* Release the DC. No need to hold the critical section for the update
2819 * Thread because this thread runs only on front buffers, but this method
2820 * fails for render targets in the check above.
2822 SelectObject(This->hDC, This->dib.holdbitmap);
2823 DeleteDC(This->hDC);
2824 /* Release the DIB section */
2825 DeleteObject(This->dib.DIBsection);
2826 This->dib.bitmap_data = NULL;
2827 This->resource.allocatedMemory = NULL;
2828 This->hDC = NULL;
2829 This->Flags &= ~SFLAG_DIBSECTION;
2830 } else if(!(This->Flags & SFLAG_USERPTR)) {
2831 release = This->resource.heapMemory;
2832 This->resource.heapMemory = NULL;
2834 This->resource.allocatedMemory = Mem;
2835 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2837 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2838 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2840 /* For client textures opengl has to be notified */
2841 if(This->Flags & SFLAG_CLIENT) {
2842 DWORD oldFlags = This->Flags;
2843 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2844 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2845 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2846 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2849 /* Now free the old memory if any */
2850 HeapFree(GetProcessHeap(), 0, release);
2851 } else if(This->Flags & SFLAG_USERPTR) {
2852 /* LockRect and GetDC will re-create the dib section and allocated memory */
2853 This->resource.allocatedMemory = NULL;
2854 /* HeapMemory should be NULL already */
2855 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2856 This->Flags &= ~SFLAG_USERPTR;
2858 if(This->Flags & SFLAG_CLIENT) {
2859 DWORD oldFlags = This->Flags;
2860 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2861 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2862 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2863 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2866 return WINED3D_OK;
2869 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
2871 /* Flip the surface contents */
2872 /* Flip the DC */
2874 HDC tmp;
2875 tmp = front->hDC;
2876 front->hDC = back->hDC;
2877 back->hDC = tmp;
2880 /* Flip the DIBsection */
2882 HBITMAP tmp;
2883 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
2884 tmp = front->dib.DIBsection;
2885 front->dib.DIBsection = back->dib.DIBsection;
2886 back->dib.DIBsection = tmp;
2888 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
2889 else front->Flags &= ~SFLAG_DIBSECTION;
2890 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
2891 else back->Flags &= ~SFLAG_DIBSECTION;
2894 /* Flip the surface data */
2896 void* tmp;
2898 tmp = front->dib.bitmap_data;
2899 front->dib.bitmap_data = back->dib.bitmap_data;
2900 back->dib.bitmap_data = tmp;
2902 tmp = front->resource.allocatedMemory;
2903 front->resource.allocatedMemory = back->resource.allocatedMemory;
2904 back->resource.allocatedMemory = tmp;
2906 tmp = front->resource.heapMemory;
2907 front->resource.heapMemory = back->resource.heapMemory;
2908 back->resource.heapMemory = tmp;
2911 /* Flip the PBO */
2913 GLuint tmp_pbo = front->pbo;
2914 front->pbo = back->pbo;
2915 back->pbo = tmp_pbo;
2918 /* client_memory should not be different, but just in case */
2920 BOOL tmp;
2921 tmp = front->dib.client_memory;
2922 front->dib.client_memory = back->dib.client_memory;
2923 back->dib.client_memory = tmp;
2926 /* Flip the opengl texture */
2928 GLuint tmp;
2930 tmp = back->texture_name;
2931 back->texture_name = front->texture_name;
2932 front->texture_name = tmp;
2934 tmp = back->texture_name_srgb;
2935 back->texture_name_srgb = front->texture_name_srgb;
2936 front->texture_name_srgb = tmp;
2940 DWORD tmp_flags = back->Flags;
2941 back->Flags = front->Flags;
2942 front->Flags = tmp_flags;
2946 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2947 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2948 IWineD3DSwapChainImpl *swapchain = NULL;
2949 HRESULT hr;
2950 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2952 /* Flipping is only supported on RenderTargets and overlays*/
2953 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
2954 WARN("Tried to flip a non-render target, non-overlay surface\n");
2955 return WINEDDERR_NOTFLIPPABLE;
2958 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
2959 flip_surface(This, (IWineD3DSurfaceImpl *) override);
2961 /* Update the overlay if it is visible */
2962 if(This->overlay_dest) {
2963 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
2964 } else {
2965 return WINED3D_OK;
2969 if(override) {
2970 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2971 * FIXME("(%p) Target override is not supported by now\n", This);
2972 * Additionally, it isn't really possible to support triple-buffering
2973 * properly on opengl at all
2977 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2978 if(!swapchain) {
2979 ERR("Flipped surface is not on a swapchain\n");
2980 return WINEDDERR_NOTFLIPPABLE;
2983 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2984 * and only d3d8 and d3d9 apps specify the presentation interval
2986 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2987 /* Most common case first to avoid wasting time on all the other cases */
2988 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2989 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2990 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2991 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2992 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2993 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2994 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2995 } else {
2996 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2999 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
3000 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
3001 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
3002 return hr;
3005 /* Does a direct frame buffer -> texture copy. Stretching is done
3006 * with single pixel copy calls
3008 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3009 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
3010 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
3012 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3013 float xrel, yrel;
3014 UINT row;
3015 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3018 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
3019 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3020 ENTER_GL();
3022 /* Bind the target texture */
3023 glBindTexture(This->texture_target, This->texture_name);
3024 checkGLcall("glBindTexture");
3025 if(!swapchain) {
3026 TRACE("Reading from an offscreen target\n");
3027 upsidedown = !upsidedown;
3028 glReadBuffer(myDevice->offscreenBuffer);
3029 } else {
3030 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
3031 glReadBuffer(buffer);
3033 checkGLcall("glReadBuffer");
3035 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
3036 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
3038 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3040 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3042 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
3043 ERR("Texture filtering not supported in direct blit\n");
3046 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
3047 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3049 ERR("Texture filtering not supported in direct blit\n");
3052 if (upsidedown
3053 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3054 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3056 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
3058 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3059 drect->x1 /*xoffset */, drect->y1 /* y offset */,
3060 srect->x1, Src->currentDesc.Height - srect->y2,
3061 drect->x2 - drect->x1, drect->y2 - drect->y1);
3062 } else {
3063 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
3064 /* I have to process this row by row to swap the image,
3065 * otherwise it would be upside down, so stretching in y direction
3066 * doesn't cost extra time
3068 * However, stretching in x direction can be avoided if not necessary
3070 for(row = drect->y1; row < drect->y2; row++) {
3071 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3073 /* Well, that stuff works, but it's very slow.
3074 * find a better way instead
3076 UINT col;
3078 for(col = drect->x1; col < drect->x2; col++) {
3079 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3080 drect->x1 + col /* x offset */, row /* y offset */,
3081 srect->x1 + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3083 } else {
3084 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3085 drect->x1 /* x offset */, row /* y offset */,
3086 srect->x1, yoffset - (int) (row * yrel), drect->x2-drect->x1, 1);
3090 checkGLcall("glCopyTexSubImage2D");
3092 LEAVE_GL();
3095 /* Uses the hardware to stretch and flip the image */
3096 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3097 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
3098 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
3100 GLuint src, backup = 0;
3101 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3102 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3103 float left, right, top, bottom; /* Texture coordinates */
3104 UINT fbwidth = Src->currentDesc.Width;
3105 UINT fbheight = Src->currentDesc.Height;
3106 struct wined3d_context *context;
3107 GLenum drawBuffer = GL_BACK;
3108 GLenum texture_target;
3109 BOOL noBackBufferBackup;
3111 TRACE("Using hwstretch blit\n");
3112 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3113 context = ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
3114 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3116 noBackBufferBackup = !swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3117 if (!noBackBufferBackup && !Src->texture_name)
3119 /* Get it a description */
3120 surface_internal_preload(SrcSurface, SRGB_RGB);
3122 ENTER_GL();
3124 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3125 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3127 if (context->aux_buffers >= 2)
3129 /* Got more than one aux buffer? Use the 2nd aux buffer */
3130 drawBuffer = GL_AUX1;
3132 else if ((swapchain || myDevice->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3134 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3135 drawBuffer = GL_AUX0;
3138 if(noBackBufferBackup) {
3139 glGenTextures(1, &backup);
3140 checkGLcall("glGenTextures");
3141 glBindTexture(GL_TEXTURE_2D, backup);
3142 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3143 texture_target = GL_TEXTURE_2D;
3144 } else {
3145 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3146 * we are reading from the back buffer, the backup can be used as source texture
3148 texture_target = Src->texture_target;
3149 glBindTexture(texture_target, Src->texture_name);
3150 checkGLcall("glBindTexture(texture_target, Src->texture_name)");
3151 glEnable(texture_target);
3152 checkGLcall("glEnable(texture_target)");
3154 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3155 Src->Flags &= ~SFLAG_INTEXTURE;
3158 if(swapchain) {
3159 glReadBuffer(surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain));
3160 } else {
3161 TRACE("Reading from an offscreen target\n");
3162 upsidedown = !upsidedown;
3163 glReadBuffer(myDevice->offscreenBuffer);
3166 /* TODO: Only back up the part that will be overwritten */
3167 glCopyTexSubImage2D(texture_target, 0,
3168 0, 0 /* read offsets */,
3169 0, 0,
3170 fbwidth,
3171 fbheight);
3173 checkGLcall("glCopyTexSubImage2D");
3175 /* No issue with overriding these - the sampler is dirty due to blit usage */
3176 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3177 wined3d_gl_mag_filter(magLookup, Filter));
3178 checkGLcall("glTexParameteri");
3179 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3180 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
3181 checkGLcall("glTexParameteri");
3183 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
3184 src = backup ? backup : Src->texture_name;
3185 } else {
3186 glReadBuffer(GL_FRONT);
3187 checkGLcall("glReadBuffer(GL_FRONT)");
3189 glGenTextures(1, &src);
3190 checkGLcall("glGenTextures(1, &src)");
3191 glBindTexture(GL_TEXTURE_2D, src);
3192 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
3194 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
3195 * out for power of 2 sizes
3197 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
3198 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3199 checkGLcall("glTexImage2D");
3200 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
3201 0, 0 /* read offsets */,
3202 0, 0,
3203 fbwidth,
3204 fbheight);
3206 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3207 checkGLcall("glTexParameteri");
3208 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3209 checkGLcall("glTexParameteri");
3211 glReadBuffer(GL_BACK);
3212 checkGLcall("glReadBuffer(GL_BACK)");
3214 if(texture_target != GL_TEXTURE_2D) {
3215 glDisable(texture_target);
3216 glEnable(GL_TEXTURE_2D);
3217 texture_target = GL_TEXTURE_2D;
3220 checkGLcall("glEnd and previous");
3222 left = srect->x1;
3223 right = srect->x2;
3225 if(upsidedown) {
3226 top = Src->currentDesc.Height - srect->y1;
3227 bottom = Src->currentDesc.Height - srect->y2;
3228 } else {
3229 top = Src->currentDesc.Height - srect->y2;
3230 bottom = Src->currentDesc.Height - srect->y1;
3233 if(Src->Flags & SFLAG_NORMCOORD) {
3234 left /= Src->pow2Width;
3235 right /= Src->pow2Width;
3236 top /= Src->pow2Height;
3237 bottom /= Src->pow2Height;
3240 /* draw the source texture stretched and upside down. The correct surface is bound already */
3241 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3242 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3244 glDrawBuffer(drawBuffer);
3245 glReadBuffer(drawBuffer);
3247 glBegin(GL_QUADS);
3248 /* bottom left */
3249 glTexCoord2f(left, bottom);
3250 glVertex2i(0, fbheight);
3252 /* top left */
3253 glTexCoord2f(left, top);
3254 glVertex2i(0, fbheight - drect->y2 - drect->y1);
3256 /* top right */
3257 glTexCoord2f(right, top);
3258 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
3260 /* bottom right */
3261 glTexCoord2f(right, bottom);
3262 glVertex2i(drect->x2 - drect->x1, fbheight);
3263 glEnd();
3264 checkGLcall("glEnd and previous");
3266 if (texture_target != This->texture_target)
3268 glDisable(texture_target);
3269 glEnable(This->texture_target);
3270 texture_target = This->texture_target;
3273 /* Now read the stretched and upside down image into the destination texture */
3274 glBindTexture(texture_target, This->texture_name);
3275 checkGLcall("glBindTexture");
3276 glCopyTexSubImage2D(texture_target,
3278 drect->x1, drect->y1, /* xoffset, yoffset */
3279 0, 0, /* We blitted the image to the origin */
3280 drect->x2 - drect->x1, drect->y2 - drect->y1);
3281 checkGLcall("glCopyTexSubImage2D");
3283 if(drawBuffer == GL_BACK) {
3284 /* Write the back buffer backup back */
3285 if(backup) {
3286 if(texture_target != GL_TEXTURE_2D) {
3287 glDisable(texture_target);
3288 glEnable(GL_TEXTURE_2D);
3289 texture_target = GL_TEXTURE_2D;
3291 glBindTexture(GL_TEXTURE_2D, backup);
3292 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3293 } else {
3294 if (texture_target != Src->texture_target)
3296 glDisable(texture_target);
3297 glEnable(Src->texture_target);
3298 texture_target = Src->texture_target;
3300 glBindTexture(Src->texture_target, Src->texture_name);
3301 checkGLcall("glBindTexture(Src->texture_target, Src->texture_name)");
3304 glBegin(GL_QUADS);
3305 /* top left */
3306 glTexCoord2f(0.0f, (float)fbheight / (float)Src->pow2Height);
3307 glVertex2i(0, 0);
3309 /* bottom left */
3310 glTexCoord2f(0.0f, 0.0f);
3311 glVertex2i(0, fbheight);
3313 /* bottom right */
3314 glTexCoord2f((float)fbwidth / (float)Src->pow2Width, 0.0f);
3315 glVertex2i(fbwidth, Src->currentDesc.Height);
3317 /* top right */
3318 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
3319 glVertex2i(fbwidth, 0);
3320 glEnd();
3321 } else {
3322 /* Restore the old draw buffer */
3323 glDrawBuffer(GL_BACK);
3325 glDisable(texture_target);
3326 checkGLcall("glDisable(texture_target)");
3328 /* Cleanup */
3329 if (src != Src->texture_name && src != backup)
3331 glDeleteTextures(1, &src);
3332 checkGLcall("glDeleteTextures(1, &src)");
3334 if(backup) {
3335 glDeleteTextures(1, &backup);
3336 checkGLcall("glDeleteTextures(1, &backup)");
3339 LEAVE_GL();
3342 /* Not called from the VTable */
3343 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3344 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx,
3345 WINED3DTEXTUREFILTERTYPE Filter)
3347 WINED3DRECT rect;
3348 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3349 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3350 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3352 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3354 /* Get the swapchain. One of the surfaces has to be a primary surface */
3355 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3356 WARN("Destination is in sysmem, rejecting gl blt\n");
3357 return WINED3DERR_INVALIDCALL;
3359 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3360 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3361 if(Src) {
3362 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3363 WARN("Src is in sysmem, rejecting gl blt\n");
3364 return WINED3DERR_INVALIDCALL;
3366 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3367 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3370 /* Early sort out of cases where no render target is used */
3371 if(!dstSwapchain && !srcSwapchain &&
3372 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3373 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3374 return WINED3DERR_INVALIDCALL;
3377 /* No destination color keying supported */
3378 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3379 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3380 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3381 return WINED3DERR_INVALIDCALL;
3384 if (DestRect) {
3385 rect.x1 = DestRect->left;
3386 rect.y1 = DestRect->top;
3387 rect.x2 = DestRect->right;
3388 rect.y2 = DestRect->bottom;
3389 } else {
3390 rect.x1 = 0;
3391 rect.y1 = 0;
3392 rect.x2 = This->currentDesc.Width;
3393 rect.y2 = This->currentDesc.Height;
3396 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3397 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3398 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3399 /* Half-life does a Blt from the back buffer to the front buffer,
3400 * Full surface size, no flags... Use present instead
3402 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3405 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3406 while(1)
3408 RECT mySrcRect;
3409 TRACE("Looking if a Present can be done...\n");
3410 /* Source Rectangle must be full surface */
3411 if( SrcRect ) {
3412 if(SrcRect->left != 0 || SrcRect->top != 0 ||
3413 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
3414 TRACE("No, Source rectangle doesn't match\n");
3415 break;
3418 mySrcRect.left = 0;
3419 mySrcRect.top = 0;
3420 mySrcRect.right = Src->currentDesc.Width;
3421 mySrcRect.bottom = Src->currentDesc.Height;
3423 /* No stretching may occur */
3424 if(mySrcRect.right != rect.x2 - rect.x1 ||
3425 mySrcRect.bottom != rect.y2 - rect.y1) {
3426 TRACE("No, stretching is done\n");
3427 break;
3430 /* Destination must be full surface or match the clipping rectangle */
3431 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3433 RECT cliprect;
3434 POINT pos[2];
3435 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3436 pos[0].x = rect.x1;
3437 pos[0].y = rect.y1;
3438 pos[1].x = rect.x2;
3439 pos[1].y = rect.y2;
3440 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3441 pos, 2);
3443 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3444 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3446 TRACE("No, dest rectangle doesn't match(clipper)\n");
3447 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3448 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3449 break;
3452 else
3454 if(rect.x1 != 0 || rect.y1 != 0 ||
3455 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3456 TRACE("No, dest rectangle doesn't match(surface size)\n");
3457 break;
3461 TRACE("Yes\n");
3463 /* These flags are unimportant for the flag check, remove them */
3464 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3465 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3467 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3468 * take very long, while a flip is fast.
3469 * This applies to Half-Life, which does such Blts every time it finished
3470 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3471 * menu. This is also used by all apps when they do windowed rendering
3473 * The problem is that flipping is not really the same as copying. After a
3474 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3475 * untouched. Therefore it's necessary to override the swap effect
3476 * and to set it back after the flip.
3478 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3479 * testcases.
3482 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3483 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3485 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3486 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3488 dstSwapchain->presentParms.SwapEffect = orig_swap;
3490 return WINED3D_OK;
3492 break;
3495 TRACE("Unsupported blit between buffers on the same swapchain\n");
3496 return WINED3DERR_INVALIDCALL;
3497 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3498 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3499 return WINED3DERR_INVALIDCALL;
3500 } else if(dstSwapchain && srcSwapchain) {
3501 FIXME("Implement hardware blit between two different swapchains\n");
3502 return WINED3DERR_INVALIDCALL;
3503 } else if(dstSwapchain) {
3504 if(SrcSurface == myDevice->render_targets[0]) {
3505 TRACE("Blit from active render target to a swapchain\n");
3506 /* Handled with regular texture -> swapchain blit */
3508 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3509 FIXME("Implement blit from a swapchain to the active render target\n");
3510 return WINED3DERR_INVALIDCALL;
3513 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3514 /* Blit from render target to texture */
3515 WINED3DRECT srect;
3516 BOOL upsideDown, stretchx;
3517 BOOL paletteOverride = FALSE;
3519 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3520 TRACE("Color keying not supported by frame buffer to texture blit\n");
3521 return WINED3DERR_INVALIDCALL;
3522 /* Destination color key is checked above */
3525 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3526 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3528 if(SrcRect) {
3529 if(SrcRect->top < SrcRect->bottom) {
3530 srect.y1 = SrcRect->top;
3531 srect.y2 = SrcRect->bottom;
3532 upsideDown = FALSE;
3533 } else {
3534 srect.y1 = SrcRect->bottom;
3535 srect.y2 = SrcRect->top;
3536 upsideDown = TRUE;
3538 srect.x1 = SrcRect->left;
3539 srect.x2 = SrcRect->right;
3540 } else {
3541 srect.x1 = 0;
3542 srect.y1 = 0;
3543 srect.x2 = Src->currentDesc.Width;
3544 srect.y2 = Src->currentDesc.Height;
3545 upsideDown = FALSE;
3547 if(rect.x1 > rect.x2) {
3548 UINT tmp = rect.x2;
3549 rect.x2 = rect.x1;
3550 rect.x1 = tmp;
3551 upsideDown = !upsideDown;
3554 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3555 stretchx = TRUE;
3556 } else {
3557 stretchx = FALSE;
3560 /* When blitting from a render target a texture, the texture isn't required to have a palette.
3561 * In this case grab the palette from the render target. */
3562 if ((This->resource.format_desc->format == WINED3DFMT_P8) && (This->palette == NULL))
3564 paletteOverride = TRUE;
3565 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3566 This->palette = Src->palette;
3569 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3570 * flip the image nor scale it.
3572 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3573 * -> If the app wants a image width an unscaled width, copy it line per line
3574 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3575 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3576 * back buffer. This is slower than reading line per line, thus not used for flipping
3577 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3578 * pixel by pixel
3580 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3581 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3582 * backends.
3584 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)
3585 && surface_can_stretch_rect(Src, This))
3587 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3588 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3589 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3590 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3591 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3592 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3593 } else {
3594 TRACE("Using hardware stretching to flip / stretch the texture\n");
3595 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3598 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3599 if(paletteOverride)
3600 This->palette = NULL;
3602 if(!(This->Flags & SFLAG_DONOTFREE)) {
3603 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3604 This->resource.allocatedMemory = NULL;
3605 This->resource.heapMemory = NULL;
3606 } else {
3607 This->Flags &= ~SFLAG_INSYSMEM;
3609 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3610 * path is never entered
3612 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3614 return WINED3D_OK;
3615 } else if(Src) {
3616 /* Blit from offscreen surface to render target */
3617 float glTexCoord[4];
3618 DWORD oldCKeyFlags = Src->CKeyFlags;
3619 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3620 RECT SourceRectangle;
3621 BOOL paletteOverride = FALSE;
3623 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3625 if(SrcRect) {
3626 SourceRectangle.left = SrcRect->left;
3627 SourceRectangle.right = SrcRect->right;
3628 SourceRectangle.top = SrcRect->top;
3629 SourceRectangle.bottom = SrcRect->bottom;
3630 } else {
3631 SourceRectangle.left = 0;
3632 SourceRectangle.right = Src->currentDesc.Width;
3633 SourceRectangle.top = 0;
3634 SourceRectangle.bottom = Src->currentDesc.Height;
3637 /* When blitting from an offscreen surface to a rendertarget, the source
3638 * surface is not required to have a palette. Our rendering / conversion
3639 * code further down the road retrieves the palette from the surface, so
3640 * it must have a palette set. */
3641 if ((Src->resource.format_desc->format == WINED3DFMT_P8) && (Src->palette == NULL))
3643 paletteOverride = TRUE;
3644 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3645 Src->palette = This->palette;
3648 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)
3649 && !(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3650 && surface_can_stretch_rect(Src, This))
3652 TRACE("Using stretch_rect_fbo\n");
3653 /* The source is always a texture, but never the currently active render target, and the texture
3654 * contents are never upside down
3656 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3657 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3659 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3660 if(paletteOverride)
3661 Src->palette = NULL;
3662 return WINED3D_OK;
3665 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3666 /* Fall back to software */
3667 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3668 SourceRectangle.left, SourceRectangle.top,
3669 SourceRectangle.right, SourceRectangle.bottom);
3670 return WINED3DERR_INVALIDCALL;
3673 /* Color keying: Check if we have to do a color keyed blt,
3674 * and if not check if a color key is activated.
3676 * Just modify the color keying parameters in the surface and restore them afterwards
3677 * The surface keeps track of the color key last used to load the opengl surface.
3678 * PreLoad will catch the change to the flags and color key and reload if necessary.
3680 if(Flags & WINEDDBLT_KEYSRC) {
3681 /* Use color key from surface */
3682 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3683 /* Use color key from DDBltFx */
3684 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3685 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3686 } else {
3687 /* Do not use color key */
3688 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3691 /* Now load the surface */
3692 surface_internal_preload((IWineD3DSurface *) Src, SRGB_RGB);
3694 /* Activate the destination context, set it up for blitting */
3695 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3697 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3698 * while OpenGL coordinates are window relative.
3699 * Also beware of the origin difference(top left vs bottom left).
3700 * Also beware that the front buffer's surface size is screen width x screen height,
3701 * whereas the real gl drawable size is the size of the window.
3703 if (dstSwapchain && (IWineD3DSurface *)This == dstSwapchain->frontBuffer) {
3704 RECT windowsize;
3705 POINT offset = {0,0};
3706 UINT h;
3707 ClientToScreen(dstSwapchain->win_handle, &offset);
3708 GetClientRect(dstSwapchain->win_handle, &windowsize);
3709 h = windowsize.bottom - windowsize.top;
3710 rect.x1 -= offset.x; rect.x2 -=offset.x;
3711 rect.y1 -= offset.y; rect.y2 -=offset.y;
3712 rect.y1 += This->currentDesc.Height - h; rect.y2 += This->currentDesc.Height - h;
3715 if (!is_identity_fixup(This->resource.format_desc->color_fixup))
3717 FIXME("Destination format %s has a fixup, this is not supported.\n",
3718 debug_d3dformat(This->resource.format_desc->format));
3719 dump_color_fixup_desc(This->resource.format_desc->color_fixup);
3722 if (!myDevice->blitter->color_fixup_supported(Src->resource.format_desc->color_fixup))
3724 FIXME("Source format %s has an unsupported fixup:\n",
3725 debug_d3dformat(Src->resource.format_desc->format));
3726 dump_color_fixup_desc(Src->resource.format_desc->color_fixup);
3729 myDevice->blitter->set_shader((IWineD3DDevice *) myDevice, Src->resource.format_desc,
3730 Src->texture_target, Src->pow2Width, Src->pow2Height);
3732 ENTER_GL();
3734 /* Bind the texture */
3735 glBindTexture(Src->texture_target, Src->texture_name);
3736 checkGLcall("glBindTexture");
3738 /* Filtering for StretchRect */
3739 glTexParameteri(Src->texture_target, GL_TEXTURE_MAG_FILTER,
3740 wined3d_gl_mag_filter(magLookup, Filter));
3741 checkGLcall("glTexParameteri");
3742 glTexParameteri(Src->texture_target, GL_TEXTURE_MIN_FILTER,
3743 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
3744 checkGLcall("glTexParameteri");
3745 glTexParameteri(Src->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3746 glTexParameteri(Src->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3747 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3748 checkGLcall("glTexEnvi");
3750 /* This is for color keying */
3751 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3752 glEnable(GL_ALPHA_TEST);
3753 checkGLcall("glEnable(GL_ALPHA_TEST)");
3755 /* When the primary render target uses P8, the alpha component contains the palette index.
3756 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3757 * should be masked away have alpha set to 0. */
3758 if(primary_render_target_is_p8(myDevice))
3759 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
3760 else
3761 glAlphaFunc(GL_NOTEQUAL, 0.0f);
3762 checkGLcall("glAlphaFunc");
3763 } else {
3764 glDisable(GL_ALPHA_TEST);
3765 checkGLcall("glDisable(GL_ALPHA_TEST)");
3768 /* Draw a textured quad
3770 glBegin(GL_QUADS);
3772 glColor3f(1.0f, 1.0f, 1.0f);
3773 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3774 glVertex3f(rect.x1, rect.y1, 0.0f);
3776 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3777 glVertex3f(rect.x1, rect.y2, 0.0f);
3779 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3780 glVertex3f(rect.x2, rect.y2, 0.0f);
3782 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3783 glVertex3f(rect.x2, rect.y1, 0.0f);
3785 glEnd();
3786 checkGLcall("glEnd");
3788 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3789 glDisable(GL_ALPHA_TEST);
3790 checkGLcall("glDisable(GL_ALPHA_TEST)");
3793 glBindTexture(Src->texture_target, 0);
3794 checkGLcall("glBindTexture(Src->texture_target, 0)");
3796 /* Restore the color key parameters */
3797 Src->CKeyFlags = oldCKeyFlags;
3798 Src->SrcBltCKey = oldBltCKey;
3800 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3801 if(paletteOverride)
3802 Src->palette = NULL;
3804 LEAVE_GL();
3806 /* Leave the opengl state valid for blitting */
3807 myDevice->blitter->unset_shader((IWineD3DDevice *) myDevice);
3809 /* Flush in case the drawable is used by multiple GL contexts */
3810 if(dstSwapchain && (This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer || dstSwapchain->num_contexts >= 2))
3811 wglFlush();
3813 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3814 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3815 * is outdated now
3817 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3819 return WINED3D_OK;
3820 } else {
3821 /* Source-Less Blit to render target */
3822 if (Flags & WINEDDBLT_COLORFILL) {
3823 /* This is easy to handle for the D3D Device... */
3824 DWORD color;
3826 TRACE("Colorfill\n");
3828 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3829 must be true if we are here */
3830 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3831 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3832 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3833 TRACE("Surface is higher back buffer, falling back to software\n");
3834 return WINED3DERR_INVALIDCALL;
3837 /* The color as given in the Blt function is in the format of the frame-buffer...
3838 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3840 if (This->resource.format_desc->format == WINED3DFMT_P8)
3842 DWORD alpha;
3844 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3845 else alpha = 0xFF000000;
3847 if (This->palette) {
3848 color = (alpha |
3849 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3850 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3851 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3852 } else {
3853 color = alpha;
3856 else if (This->resource.format_desc->format == WINED3DFMT_R5G6B5)
3858 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3859 color = 0xFFFFFFFF;
3860 } else {
3861 color = ((0xFF000000) |
3862 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3863 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3864 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3867 else if ((This->resource.format_desc->format == WINED3DFMT_R8G8B8)
3868 || (This->resource.format_desc->format == WINED3DFMT_X8R8G8B8))
3870 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3872 else if (This->resource.format_desc->format == WINED3DFMT_A8R8G8B8)
3874 color = DDBltFx->u5.dwFillColor;
3876 else {
3877 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3878 return WINED3DERR_INVALIDCALL;
3881 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3882 IWineD3DDeviceImpl_ClearSurface(myDevice, This, 1 /* Number of rectangles */,
3883 &rect, WINED3DCLEAR_TARGET, color, 0.0f /* Z */, 0 /* Stencil */);
3884 return WINED3D_OK;
3888 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3889 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3890 return WINED3DERR_INVALIDCALL;
3893 static HRESULT IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3894 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx)
3896 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3897 float depth;
3899 if (Flags & WINEDDBLT_DEPTHFILL) {
3900 switch(This->resource.format_desc->format)
3902 case WINED3DFMT_D16_UNORM:
3903 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3904 break;
3905 case WINED3DFMT_D15S1:
3906 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3907 break;
3908 case WINED3DFMT_D24S8:
3909 case WINED3DFMT_D24X8:
3910 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3911 break;
3912 case WINED3DFMT_D32:
3913 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3914 break;
3915 default:
3916 depth = 0.0f;
3917 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format_desc->format));
3920 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3921 DestRect == NULL ? 0 : 1,
3922 (const WINED3DRECT *)DestRect,
3923 WINED3DCLEAR_ZBUFFER,
3924 0x00000000,
3925 depth,
3926 0x00000000);
3929 FIXME("(%p): Unsupp depthstencil blit\n", This);
3930 return WINED3DERR_INVALIDCALL;
3933 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
3934 const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3935 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3936 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3937 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3938 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3939 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3941 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3943 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3944 return WINEDDERR_SURFACEBUSY;
3947 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3948 * except depth blits, which seem to work
3950 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3951 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3952 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3953 return WINED3DERR_INVALIDCALL;
3954 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3955 TRACE("Z Blit override handled the blit\n");
3956 return WINED3D_OK;
3960 /* Special cases for RenderTargets */
3961 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3962 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3963 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3966 /* For the rest call the X11 surface implementation.
3967 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3968 * other Blts are rather rare
3970 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3973 static HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
3974 IWineD3DSurface *Source, const RECT *rsrc, DWORD trans)
3976 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3977 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3978 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3979 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3981 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
3983 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3984 return WINEDDERR_SURFACEBUSY;
3987 if(myDevice->inScene &&
3988 (iface == myDevice->stencilBufferTarget ||
3989 (Source && Source == myDevice->stencilBufferTarget))) {
3990 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3991 return WINED3DERR_INVALIDCALL;
3994 /* Special cases for RenderTargets */
3995 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3996 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3998 RECT SrcRect, DstRect;
3999 DWORD Flags=0;
4001 if(rsrc) {
4002 SrcRect.left = rsrc->left;
4003 SrcRect.top= rsrc->top;
4004 SrcRect.bottom = rsrc->bottom;
4005 SrcRect.right = rsrc->right;
4006 } else {
4007 SrcRect.left = 0;
4008 SrcRect.top = 0;
4009 SrcRect.right = srcImpl->currentDesc.Width;
4010 SrcRect.bottom = srcImpl->currentDesc.Height;
4013 DstRect.left = dstx;
4014 DstRect.top=dsty;
4015 DstRect.right = dstx + SrcRect.right - SrcRect.left;
4016 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
4018 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
4019 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
4020 Flags |= WINEDDBLT_KEYSRC;
4021 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
4022 Flags |= WINEDDBLT_KEYDEST;
4023 if(trans & WINEDDBLTFAST_WAIT)
4024 Flags |= WINEDDBLT_WAIT;
4025 if(trans & WINEDDBLTFAST_DONOTWAIT)
4026 Flags |= WINEDDBLT_DONOTWAIT;
4028 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
4032 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
4035 static HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface)
4037 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4038 RGBQUAD col[256];
4039 IWineD3DPaletteImpl *pal = This->palette;
4040 unsigned int n;
4041 TRACE("(%p)\n", This);
4043 if (!pal) return WINED3D_OK;
4045 if (This->resource.format_desc->format == WINED3DFMT_P8
4046 || This->resource.format_desc->format == WINED3DFMT_A8P8)
4048 int bpp;
4049 GLenum format, internal, type;
4050 CONVERT_TYPES convert;
4052 /* Check if we are using a RTL mode which uses texturing for uploads */
4053 BOOL use_texture = (wined3d_settings.rendertargetlock_mode == RTL_READTEX);
4055 /* Check if we have hardware palette conversion if we have convert is set to NO_CONVERSION */
4056 d3dfmt_get_conv(This, TRUE, use_texture, &format, &internal, &type, &convert, &bpp, FALSE);
4058 if((This->resource.usage & WINED3DUSAGE_RENDERTARGET) && (convert == NO_CONVERSION))
4060 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4062 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
4063 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
4065 /* We want to force a palette refresh, so mark the drawable as not being up to date */
4066 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
4068 /* Re-upload the palette */
4069 ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
4070 d3dfmt_p8_upload_palette(iface, convert);
4071 } else {
4072 if(!(This->Flags & SFLAG_INSYSMEM)) {
4073 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
4074 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
4076 TRACE("Dirtifying surface\n");
4077 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
4081 if(This->Flags & SFLAG_DIBSECTION) {
4082 TRACE("(%p): Updating the hdc's palette\n", This);
4083 for (n=0; n<256; n++) {
4084 col[n].rgbRed = pal->palents[n].peRed;
4085 col[n].rgbGreen = pal->palents[n].peGreen;
4086 col[n].rgbBlue = pal->palents[n].peBlue;
4087 col[n].rgbReserved = 0;
4089 SetDIBColorTable(This->hDC, 0, 256, col);
4092 /* Propagate the changes to the drawable when we have a palette. */
4093 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
4094 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
4096 return WINED3D_OK;
4099 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
4100 /** Check against the maximum texture sizes supported by the video card **/
4101 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4102 unsigned int pow2Width, pow2Height;
4104 This->texture_name = 0;
4105 This->texture_target = GL_TEXTURE_2D;
4107 /* Non-power2 support */
4108 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO) || GL_SUPPORT(WINE_NORMALIZED_TEXRECT)) {
4109 pow2Width = This->currentDesc.Width;
4110 pow2Height = This->currentDesc.Height;
4111 } else {
4112 /* Find the nearest pow2 match */
4113 pow2Width = pow2Height = 1;
4114 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
4115 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
4117 This->pow2Width = pow2Width;
4118 This->pow2Height = pow2Height;
4120 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
4121 /** TODO: add support for non power two compressed textures **/
4122 if (This->resource.format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
4124 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
4125 This, This->currentDesc.Width, This->currentDesc.Height);
4126 return WINED3DERR_NOTAVAILABLE;
4130 if(pow2Width != This->currentDesc.Width ||
4131 pow2Height != This->currentDesc.Height) {
4132 This->Flags |= SFLAG_NONPOW2;
4135 TRACE("%p\n", This);
4136 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
4137 /* one of three options
4138 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)
4139 2: Set the texture to the maximum size (bad idea)
4140 3: WARN and return WINED3DERR_NOTAVAILABLE;
4141 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.
4143 WARN("(%p) Creating an oversized surface: %ux%u (texture is %ux%u)\n",
4144 This, This->pow2Width, This->pow2Height, This->currentDesc.Width, This->currentDesc.Height);
4145 This->Flags |= SFLAG_OVERSIZE;
4147 /* This will be initialized on the first blt */
4148 This->glRect.left = 0;
4149 This->glRect.top = 0;
4150 This->glRect.right = 0;
4151 This->glRect.bottom = 0;
4152 } else {
4153 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
4154 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
4155 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
4156 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
4158 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE)
4159 && !((This->resource.format_desc->format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE)
4160 && (wined3d_settings.rendertargetlock_mode == RTL_READTEX)))
4162 This->texture_target = GL_TEXTURE_RECTANGLE_ARB;
4163 This->pow2Width = This->currentDesc.Width;
4164 This->pow2Height = This->currentDesc.Height;
4165 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
4168 /* No oversize, gl rect is the full texture size */
4169 This->Flags &= ~SFLAG_OVERSIZE;
4170 This->glRect.left = 0;
4171 This->glRect.top = 0;
4172 This->glRect.right = This->pow2Width;
4173 This->glRect.bottom = This->pow2Height;
4176 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4177 switch(wined3d_settings.offscreen_rendering_mode) {
4178 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4179 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4180 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4184 This->Flags |= SFLAG_INSYSMEM;
4186 return WINED3D_OK;
4189 struct depth_blt_info
4191 GLenum binding;
4192 GLenum bind_target;
4193 enum tex_types tex_type;
4194 GLfloat coords[4][3];
4197 static void surface_get_depth_blt_info(GLenum target, GLsizei w, GLsizei h, struct depth_blt_info *info)
4199 GLfloat (*coords)[3] = info->coords;
4201 switch (target)
4203 default:
4204 FIXME("Unsupported texture target %#x\n", target);
4205 /* Fall back to GL_TEXTURE_2D */
4206 case GL_TEXTURE_2D:
4207 info->binding = GL_TEXTURE_BINDING_2D;
4208 info->bind_target = GL_TEXTURE_2D;
4209 info->tex_type = tex_2d;
4210 coords[0][0] = 0.0f; coords[0][1] = 1.0f; coords[0][2] = 0.0f;
4211 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 0.0f;
4212 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4213 coords[3][0] = 1.0f; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4214 break;
4216 case GL_TEXTURE_RECTANGLE_ARB:
4217 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
4218 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
4219 info->tex_type = tex_rect;
4220 coords[0][0] = 0.0f; coords[0][1] = h; coords[0][2] = 0.0f;
4221 coords[1][0] = w; coords[1][1] = h; coords[1][2] = 0.0f;
4222 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4223 coords[3][0] = w; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4224 break;
4226 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4227 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4228 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4229 info->tex_type = tex_cube;
4230 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4231 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4232 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4233 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4235 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4236 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4237 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4238 info->tex_type = tex_cube;
4239 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4240 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4241 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4242 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4244 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4245 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4246 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4247 info->tex_type = tex_cube;
4248 coords[0][0] = -1.0f; coords[0][1] = 1.0f; coords[0][2] = 1.0f;
4249 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 1.0f;
4250 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4251 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4253 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4254 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4255 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4256 info->tex_type = tex_cube;
4257 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4258 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4259 coords[2][0] = -1.0f; coords[2][1] = -1.0f; coords[2][2] = 1.0f;
4260 coords[3][0] = 1.0f; coords[3][1] = -1.0f; coords[3][2] = 1.0f;
4262 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4263 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4264 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4265 info->tex_type = tex_cube;
4266 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4267 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4268 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4269 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4271 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4272 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4273 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4274 info->tex_type = tex_cube;
4275 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4276 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4277 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4278 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4282 /* GL locking is done by the caller */
4283 static void surface_depth_blt(IWineD3DSurfaceImpl *This, GLuint texture, GLsizei w, GLsizei h, GLenum target)
4285 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4286 struct depth_blt_info info;
4287 GLint old_binding = 0;
4289 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4291 glDisable(GL_CULL_FACE);
4292 glDisable(GL_BLEND);
4293 glDisable(GL_ALPHA_TEST);
4294 glDisable(GL_SCISSOR_TEST);
4295 glDisable(GL_STENCIL_TEST);
4296 glEnable(GL_DEPTH_TEST);
4297 glDepthFunc(GL_ALWAYS);
4298 glDepthMask(GL_TRUE);
4299 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4300 glViewport(0, 0, w, h);
4302 surface_get_depth_blt_info(target, w, h, &info);
4303 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4304 glGetIntegerv(info.binding, &old_binding);
4305 glBindTexture(info.bind_target, texture);
4307 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4309 glBegin(GL_TRIANGLE_STRIP);
4310 glTexCoord3fv(info.coords[0]);
4311 glVertex2f(-1.0f, -1.0f);
4312 glTexCoord3fv(info.coords[1]);
4313 glVertex2f(1.0f, -1.0f);
4314 glTexCoord3fv(info.coords[2]);
4315 glVertex2f(-1.0f, 1.0f);
4316 glTexCoord3fv(info.coords[3]);
4317 glVertex2f(1.0f, 1.0f);
4318 glEnd();
4320 glBindTexture(info.bind_target, old_binding);
4322 glPopAttrib();
4324 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4327 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
4328 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4330 TRACE("(%p) New location %#x\n", This, location);
4332 if (location & ~SFLAG_DS_LOCATIONS) {
4333 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
4336 This->Flags &= ~SFLAG_DS_LOCATIONS;
4337 This->Flags |= location;
4340 /* Context activation is done by the caller. */
4341 void surface_load_ds_location(IWineD3DSurface *iface, struct wined3d_context *context, DWORD location)
4343 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4344 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4346 TRACE("(%p) New location %#x\n", This, location);
4348 /* TODO: Make this work for modes other than FBO */
4349 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4351 if (This->Flags & location) {
4352 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
4353 return;
4356 if (This->current_renderbuffer) {
4357 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
4358 return;
4361 if (location == SFLAG_DS_OFFSCREEN) {
4362 if (This->Flags & SFLAG_DS_ONSCREEN) {
4363 GLint old_binding = 0;
4364 GLenum bind_target;
4366 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
4368 ENTER_GL();
4370 if (!device->depth_blt_texture) {
4371 glGenTextures(1, &device->depth_blt_texture);
4374 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4375 * directly on the FBO texture. That's because we need to flip. */
4376 context_bind_fbo(context, GL_FRAMEBUFFER_EXT, NULL);
4377 if (This->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4379 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4380 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4381 } else {
4382 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4383 bind_target = GL_TEXTURE_2D;
4385 glBindTexture(bind_target, device->depth_blt_texture);
4386 glCopyTexImage2D(bind_target, This->texture_level, This->resource.format_desc->glInternal,
4387 0, 0, This->currentDesc.Width, This->currentDesc.Height, 0);
4388 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4389 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4390 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4391 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4392 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4393 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4394 glBindTexture(bind_target, old_binding);
4396 /* Setup the destination */
4397 if (!device->depth_blt_rb) {
4398 GL_EXTCALL(glGenRenderbuffersEXT(1, &device->depth_blt_rb));
4399 checkGLcall("glGenRenderbuffersEXT");
4401 if (device->depth_blt_rb_w != This->currentDesc.Width
4402 || device->depth_blt_rb_h != This->currentDesc.Height) {
4403 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4404 checkGLcall("glBindRenderbufferEXT");
4405 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, This->currentDesc.Width, This->currentDesc.Height));
4406 checkGLcall("glRenderbufferStorageEXT");
4407 device->depth_blt_rb_w = This->currentDesc.Width;
4408 device->depth_blt_rb_h = This->currentDesc.Height;
4411 context_bind_fbo(context, GL_FRAMEBUFFER_EXT, &context->dst_fbo);
4412 GL_EXTCALL(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4413 checkGLcall("glFramebufferRenderbufferEXT");
4414 context_attach_depth_stencil_fbo(context, GL_FRAMEBUFFER_EXT, iface, FALSE);
4416 /* Do the actual blit */
4417 surface_depth_blt(This, device->depth_blt_texture, This->currentDesc.Width, This->currentDesc.Height, bind_target);
4418 checkGLcall("depth_blt");
4420 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER_EXT, &context->current_fbo->id);
4421 else context_bind_fbo(context, GL_FRAMEBUFFER_EXT, NULL);
4423 LEAVE_GL();
4424 } else {
4425 FIXME("No up to date depth stencil location\n");
4427 } else if (location == SFLAG_DS_ONSCREEN) {
4428 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4429 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4431 ENTER_GL();
4433 context_bind_fbo(context, GL_FRAMEBUFFER_EXT, NULL);
4434 surface_depth_blt(This, This->texture_name, This->currentDesc.Width,
4435 This->currentDesc.Height, This->texture_target);
4436 checkGLcall("depth_blt");
4438 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER_EXT, &context->current_fbo->id);
4440 LEAVE_GL();
4441 } else {
4442 FIXME("No up to date depth stencil location\n");
4444 } else {
4445 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4448 This->Flags |= location;
4451 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4452 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4453 IWineD3DBaseTexture *texture;
4454 IWineD3DSurfaceImpl *overlay;
4456 TRACE("(%p)->(%s, %s)\n", iface, debug_surflocation(flag),
4457 persistent ? "TRUE" : "FALSE");
4459 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4460 if (This->Flags & SFLAG_SWAPCHAIN)
4462 TRACE("Surface %p is an onscreen surface\n", iface);
4463 } else {
4464 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4465 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4469 if(persistent) {
4470 if(((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) ||
4471 ((This->Flags & SFLAG_INSRGBTEX) && !(flag & SFLAG_INSRGBTEX))) {
4472 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4473 TRACE("Passing to container\n");
4474 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4475 IWineD3DBaseTexture_Release(texture);
4478 This->Flags &= ~SFLAG_LOCATIONS;
4479 This->Flags |= flag;
4481 /* Redraw emulated overlays, if any */
4482 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4483 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4484 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4487 } else {
4488 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))) {
4489 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4490 TRACE("Passing to container\n");
4491 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4492 IWineD3DBaseTexture_Release(texture);
4495 This->Flags &= ~flag;
4498 if(!(This->Flags & SFLAG_LOCATIONS)) {
4499 ERR("%p: Surface does not have any up to date location\n", This);
4503 struct coords {
4504 GLfloat x, y, z;
4507 struct float_rect
4509 float l;
4510 float t;
4511 float r;
4512 float b;
4515 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
4517 f->l = ((r->left * 2.0f) / w) - 1.0f;
4518 f->t = ((r->top * 2.0f) / h) - 1.0f;
4519 f->r = ((r->right * 2.0f) / w) - 1.0f;
4520 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
4523 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in)
4525 const struct wined3d_context *context;
4526 struct coords coords[4];
4527 RECT rect;
4528 IWineD3DSwapChain *swapchain;
4529 IWineD3DBaseTexture *texture;
4530 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4531 GLenum bind_target;
4532 struct float_rect f;
4534 if(rect_in) {
4535 rect = *rect_in;
4536 } else {
4537 rect.left = 0;
4538 rect.top = 0;
4539 rect.right = This->currentDesc.Width;
4540 rect.bottom = This->currentDesc.Height;
4543 switch (This->texture_target)
4545 case GL_TEXTURE_2D:
4546 bind_target = GL_TEXTURE_2D;
4548 coords[0].x = (float)rect.left / This->pow2Width;
4549 coords[0].y = (float)rect.top / This->pow2Height;
4550 coords[0].z = 0;
4552 coords[1].x = (float)rect.left / This->pow2Width;
4553 coords[1].y = (float)rect.bottom / This->pow2Height;
4554 coords[1].z = 0;
4556 coords[2].x = (float)rect.right / This->pow2Width;
4557 coords[2].y = (float)rect.bottom / This->pow2Height;
4558 coords[2].z = 0;
4560 coords[3].x = (float)rect.right / This->pow2Width;
4561 coords[3].y = (float)rect.top / This->pow2Height;
4562 coords[3].z = 0;
4563 break;
4565 case GL_TEXTURE_RECTANGLE_ARB:
4566 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4567 coords[0].x = rect.left; coords[0].y = rect.top; coords[0].z = 0;
4568 coords[1].x = rect.left; coords[1].y = rect.bottom; coords[1].z = 0;
4569 coords[2].x = rect.right; coords[2].y = rect.bottom; coords[2].z = 0;
4570 coords[3].x = rect.right; coords[3].y = rect.top; coords[3].z = 0;
4571 break;
4573 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4574 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4575 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4576 coords[0].x = 1; coords[0].y = -f.t; coords[0].z = -f.l;
4577 coords[1].x = 1; coords[1].y = -f.b; coords[1].z = -f.l;
4578 coords[2].x = 1; coords[2].y = -f.b; coords[2].z = -f.r;
4579 coords[3].x = 1; coords[3].y = -f.t; coords[3].z = -f.r;
4580 break;
4582 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4583 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4584 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4585 coords[0].x = -1; coords[0].y = -f.t; coords[0].z = f.l;
4586 coords[1].x = -1; coords[1].y = -f.b; coords[1].z = f.l;
4587 coords[2].x = -1; coords[2].y = -f.b; coords[2].z = f.r;
4588 coords[3].x = -1; coords[3].y = -f.t; coords[3].z = f.r;
4589 break;
4591 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4592 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4593 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4594 coords[0].x = f.l; coords[0].y = 1; coords[0].z = f.t;
4595 coords[1].x = f.l; coords[1].y = 1; coords[1].z = f.b;
4596 coords[2].x = f.r; coords[2].y = 1; coords[2].z = f.b;
4597 coords[3].x = f.r; coords[3].y = 1; coords[3].z = f.t;
4598 break;
4600 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4601 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4602 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4603 coords[0].x = f.l; coords[0].y = -1; coords[0].z = -f.t;
4604 coords[1].x = f.l; coords[1].y = -1; coords[1].z = -f.b;
4605 coords[2].x = f.r; coords[2].y = -1; coords[2].z = -f.b;
4606 coords[3].x = f.r; coords[3].y = -1; coords[3].z = -f.t;
4607 break;
4609 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4610 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4611 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4612 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1;
4613 coords[1].x = f.l; coords[1].y = -f.b; coords[1].z = 1;
4614 coords[2].x = f.r; coords[2].y = -f.b; coords[2].z = 1;
4615 coords[3].x = f.r; coords[3].y = -f.t; coords[3].z = 1;
4616 break;
4618 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4619 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4620 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4621 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1;
4622 coords[1].x = -f.l; coords[1].y = -f.b; coords[1].z = -1;
4623 coords[2].x = -f.r; coords[2].y = -f.b; coords[2].z = -1;
4624 coords[3].x = -f.r; coords[3].y = -f.t; coords[3].z = -1;
4625 break;
4627 default:
4628 ERR("Unexpected texture target %#x\n", This->texture_target);
4629 return;
4632 context = ActivateContext(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4634 ENTER_GL();
4636 glEnable(bind_target);
4637 checkGLcall("glEnable(bind_target)");
4638 glBindTexture(bind_target, This->texture_name);
4639 checkGLcall("glBindTexture(bind_target, This->texture_name)");
4640 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4641 checkGLcall("glTexParameteri");
4642 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4643 checkGLcall("glTexParameteri");
4645 if (context->render_offscreen)
4647 LONG tmp = rect.top;
4648 rect.top = rect.bottom;
4649 rect.bottom = tmp;
4652 glBegin(GL_QUADS);
4653 glTexCoord3fv(&coords[0].x);
4654 glVertex2i(rect.left, rect.top);
4656 glTexCoord3fv(&coords[1].x);
4657 glVertex2i(rect.left, rect.bottom);
4659 glTexCoord3fv(&coords[2].x);
4660 glVertex2i(rect.right, rect.bottom);
4662 glTexCoord3fv(&coords[3].x);
4663 glVertex2i(rect.right, rect.top);
4664 glEnd();
4665 checkGLcall("glEnd");
4667 glDisable(bind_target);
4668 checkGLcall("glDisable(bind_target)");
4670 LEAVE_GL();
4672 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain)))
4674 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
4675 if(((IWineD3DSwapChainImpl*)swapchain)->frontBuffer == (IWineD3DSurface*)This ||
4676 ((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
4677 wglFlush();
4679 IWineD3DSwapChain_Release(swapchain);
4680 } else {
4681 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
4682 * reset properly next draw
4684 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture)))
4686 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
4687 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
4688 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
4689 IWineD3DBaseTexture_Release(texture);
4694 /*****************************************************************************
4695 * IWineD3DSurface::LoadLocation
4697 * Copies the current surface data from wherever it is to the requested
4698 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4699 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4700 * multiple locations, the gl texture is preferred over the drawable, which is
4701 * preferred over system memory. The PBO counts as system memory. If rect is
4702 * not NULL, only the specified rectangle is copied (only supported for
4703 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4704 * location is marked up to date after the copy.
4706 * Parameters:
4707 * flag: Surface location flag to be updated
4708 * rect: rectangle to be copied
4710 * Returns:
4711 * WINED3D_OK on success
4712 * WINED3DERR_DEVICELOST on an internal error
4714 *****************************************************************************/
4715 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4716 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4717 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4718 GLenum format, internal, type;
4719 CONVERT_TYPES convert;
4720 int bpp;
4721 int width, pitch, outpitch;
4722 BYTE *mem;
4723 BOOL drawable_read_ok = TRUE;
4725 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4726 if (This->Flags & SFLAG_SWAPCHAIN)
4728 TRACE("Surface %p is an onscreen surface\n", iface);
4729 } else {
4730 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4731 * Prefer SFLAG_INTEXTURE. */
4732 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4733 drawable_read_ok = FALSE;
4737 TRACE("(%p)->(%s, %p)\n", iface, debug_surflocation(flag), rect);
4738 if(rect) {
4739 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4742 if(This->Flags & flag) {
4743 TRACE("Location already up to date\n");
4744 return WINED3D_OK;
4747 if(!(This->Flags & SFLAG_LOCATIONS)) {
4748 ERR("%p: Surface does not have any up to date location\n", This);
4749 This->Flags |= SFLAG_LOST;
4750 return WINED3DERR_DEVICELOST;
4753 if(flag == SFLAG_INSYSMEM) {
4754 surface_prepare_system_memory(This);
4756 /* Download the surface to system memory */
4757 if(This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) {
4758 if (!device->isInDraw) ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
4759 surface_bind_and_dirtify(This, !(This->Flags & SFLAG_INTEXTURE));
4761 surface_download_data(This);
4762 } else {
4763 /* Note: It might be faster to download into a texture first. */
4764 read_from_framebuffer(This, rect,
4765 This->resource.allocatedMemory,
4766 IWineD3DSurface_GetPitch(iface));
4768 } else if(flag == SFLAG_INDRAWABLE) {
4769 if(This->Flags & SFLAG_INTEXTURE) {
4770 surface_blt_to_drawable(This, rect);
4771 } else {
4772 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4773 /* This needs a shader to convert the srgb data sampled from the GL texture into RGB
4774 * values, otherwise we get incorrect values in the target. For now go the slow way
4775 * via a system memory copy
4777 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4780 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, FALSE);
4782 /* The width is in 'length' not in bytes */
4783 width = This->currentDesc.Width;
4784 pitch = IWineD3DSurface_GetPitch(iface);
4786 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4787 * but it isn't set (yet) in all cases it is getting called. */
4788 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4789 TRACE("Removing the pbo attached to surface %p\n", This);
4790 if (!device->isInDraw) ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
4791 surface_remove_pbo(This);
4794 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4795 int height = This->currentDesc.Height;
4797 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4798 outpitch = width * bpp;
4799 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4801 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4802 if(!mem) {
4803 ERR("Out of memory %d, %d!\n", outpitch, height);
4804 return WINED3DERR_OUTOFVIDEOMEMORY;
4806 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4808 This->Flags |= SFLAG_CONVERTED;
4809 } else {
4810 This->Flags &= ~SFLAG_CONVERTED;
4811 mem = This->resource.allocatedMemory;
4814 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4816 /* Don't delete PBO memory */
4817 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4818 HeapFree(GetProcessHeap(), 0, mem);
4820 } else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */ {
4821 if (drawable_read_ok && (This->Flags & SFLAG_INDRAWABLE)) {
4822 read_from_framebuffer_texture(This, flag == SFLAG_INSRGBTEX);
4823 } else { /* Upload from system memory */
4824 BOOL srgb = flag == SFLAG_INSRGBTEX;
4825 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4826 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
4828 if(srgb) {
4829 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE) {
4830 /* Performance warning ... */
4831 FIXME("%p: Downloading rgb texture to reload it as srgb\n", This);
4832 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4834 } else {
4835 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4836 /* Performance warning ... */
4837 FIXME("%p: Downloading srgb texture to reload it as rgb\n", This);
4838 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4842 if (!device->isInDraw) ActivateContext(device, NULL, CTXUSAGE_RESOURCELOAD);
4843 surface_bind_and_dirtify(This, srgb);
4845 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4846 This->Flags |= SFLAG_GLCKEY;
4847 This->glCKey = This->SrcBltCKey;
4849 else This->Flags &= ~SFLAG_GLCKEY;
4851 /* The width is in 'length' not in bytes */
4852 width = This->currentDesc.Width;
4853 pitch = IWineD3DSurface_GetPitch(iface);
4855 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4856 * but it isn't set (yet) in all cases it is getting called. */
4857 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4858 TRACE("Removing the pbo attached to surface %p\n", This);
4859 surface_remove_pbo(This);
4862 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4863 int height = This->currentDesc.Height;
4865 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4866 outpitch = width * bpp;
4867 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4869 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4870 if(!mem) {
4871 ERR("Out of memory %d, %d!\n", outpitch, height);
4872 return WINED3DERR_OUTOFVIDEOMEMORY;
4874 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4876 This->Flags |= SFLAG_CONVERTED;
4878 else if ((This->resource.format_desc->format == WINED3DFMT_P8)
4879 && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)))
4881 d3dfmt_p8_upload_palette(iface, convert);
4882 This->Flags &= ~SFLAG_CONVERTED;
4883 mem = This->resource.allocatedMemory;
4884 } else {
4885 This->Flags &= ~SFLAG_CONVERTED;
4886 mem = This->resource.allocatedMemory;
4889 /* Make sure the correct pitch is used */
4890 ENTER_GL();
4891 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4892 LEAVE_GL();
4894 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4895 TRACE("non power of two support\n");
4896 if(!(This->Flags & alloc_flag)) {
4897 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4898 This->Flags |= alloc_flag;
4900 if (mem || (This->Flags & SFLAG_PBO)) {
4901 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4903 } else {
4904 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4905 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4907 if(!(This->Flags & alloc_flag)) {
4908 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4909 This->Flags |= alloc_flag;
4911 if (mem || (This->Flags & SFLAG_PBO)) {
4912 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4916 /* Restore the default pitch */
4917 ENTER_GL();
4918 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4919 LEAVE_GL();
4921 /* Don't delete PBO memory */
4922 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4923 HeapFree(GetProcessHeap(), 0, mem);
4927 if(rect == NULL) {
4928 This->Flags |= flag;
4931 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !(This->Flags & SFLAG_SWAPCHAIN)
4932 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4933 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4934 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4937 return WINED3D_OK;
4940 static HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container)
4942 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4943 IWineD3DSwapChain *swapchain = NULL;
4945 /* Update the drawable size method */
4946 if(container) {
4947 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4949 if(swapchain) {
4950 This->get_drawable_size = get_drawable_size_swapchain;
4951 IWineD3DSwapChain_Release(swapchain);
4952 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4953 switch(wined3d_settings.offscreen_rendering_mode) {
4954 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4955 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4956 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4960 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4963 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4964 return SURFACE_OPENGL;
4967 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
4968 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4969 HRESULT hr;
4971 /* If there's no destination surface there is nothing to do */
4972 if(!This->overlay_dest) return WINED3D_OK;
4974 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
4975 * update the overlay. Prevent an endless recursion
4977 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
4978 return WINED3D_OK;
4980 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
4981 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
4982 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
4983 NULL, WINED3DTEXF_LINEAR);
4984 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
4986 return hr;
4989 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4991 /* IUnknown */
4992 IWineD3DBaseSurfaceImpl_QueryInterface,
4993 IWineD3DBaseSurfaceImpl_AddRef,
4994 IWineD3DSurfaceImpl_Release,
4995 /* IWineD3DResource */
4996 IWineD3DBaseSurfaceImpl_GetParent,
4997 IWineD3DBaseSurfaceImpl_GetDevice,
4998 IWineD3DBaseSurfaceImpl_SetPrivateData,
4999 IWineD3DBaseSurfaceImpl_GetPrivateData,
5000 IWineD3DBaseSurfaceImpl_FreePrivateData,
5001 IWineD3DBaseSurfaceImpl_SetPriority,
5002 IWineD3DBaseSurfaceImpl_GetPriority,
5003 IWineD3DSurfaceImpl_PreLoad,
5004 IWineD3DSurfaceImpl_UnLoad,
5005 IWineD3DBaseSurfaceImpl_GetType,
5006 /* IWineD3DSurface */
5007 IWineD3DBaseSurfaceImpl_GetContainer,
5008 IWineD3DBaseSurfaceImpl_GetDesc,
5009 IWineD3DSurfaceImpl_LockRect,
5010 IWineD3DSurfaceImpl_UnlockRect,
5011 IWineD3DSurfaceImpl_GetDC,
5012 IWineD3DSurfaceImpl_ReleaseDC,
5013 IWineD3DSurfaceImpl_Flip,
5014 IWineD3DSurfaceImpl_Blt,
5015 IWineD3DBaseSurfaceImpl_GetBltStatus,
5016 IWineD3DBaseSurfaceImpl_GetFlipStatus,
5017 IWineD3DBaseSurfaceImpl_IsLost,
5018 IWineD3DBaseSurfaceImpl_Restore,
5019 IWineD3DSurfaceImpl_BltFast,
5020 IWineD3DBaseSurfaceImpl_GetPalette,
5021 IWineD3DBaseSurfaceImpl_SetPalette,
5022 IWineD3DSurfaceImpl_RealizePalette,
5023 IWineD3DBaseSurfaceImpl_SetColorKey,
5024 IWineD3DBaseSurfaceImpl_GetPitch,
5025 IWineD3DSurfaceImpl_SetMem,
5026 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
5027 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
5028 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
5029 IWineD3DBaseSurfaceImpl_UpdateOverlay,
5030 IWineD3DBaseSurfaceImpl_SetClipper,
5031 IWineD3DBaseSurfaceImpl_GetClipper,
5032 /* Internal use: */
5033 IWineD3DSurfaceImpl_LoadTexture,
5034 IWineD3DSurfaceImpl_BindTexture,
5035 IWineD3DSurfaceImpl_SaveSnapshot,
5036 IWineD3DSurfaceImpl_SetContainer,
5037 IWineD3DBaseSurfaceImpl_GetData,
5038 IWineD3DSurfaceImpl_SetFormat,
5039 IWineD3DSurfaceImpl_PrivateSetup,
5040 IWineD3DSurfaceImpl_ModifyLocation,
5041 IWineD3DSurfaceImpl_LoadLocation,
5042 IWineD3DSurfaceImpl_GetImplType,
5043 IWineD3DSurfaceImpl_DrawOverlay
5045 #undef GLINFO_LOCATION
5047 #define GLINFO_LOCATION device->adapter->gl_info
5048 static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
5049 /* Context activation is done by the caller. */
5050 static void ffp_blit_free(IWineD3DDevice *iface) { }
5052 /* Context activation is done by the caller. */
5053 static HRESULT ffp_blit_set(IWineD3DDevice *iface, const struct GlPixelFormatDesc *format_desc,
5054 GLenum textype, UINT width, UINT height)
5056 ENTER_GL();
5057 glEnable(textype);
5058 checkGLcall("glEnable(textype)");
5059 LEAVE_GL();
5060 return WINED3D_OK;
5063 /* Context activation is done by the caller. */
5064 static void ffp_blit_unset(IWineD3DDevice *iface) {
5065 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
5066 ENTER_GL();
5067 glDisable(GL_TEXTURE_2D);
5068 checkGLcall("glDisable(GL_TEXTURE_2D)");
5069 if(GL_SUPPORT(ARB_TEXTURE_CUBE_MAP)) {
5070 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
5071 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
5073 if(GL_SUPPORT(ARB_TEXTURE_RECTANGLE)) {
5074 glDisable(GL_TEXTURE_RECTANGLE_ARB);
5075 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
5077 LEAVE_GL();
5080 static BOOL ffp_blit_color_fixup_supported(struct color_fixup_desc fixup)
5082 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
5084 TRACE("Checking support for fixup:\n");
5085 dump_color_fixup_desc(fixup);
5088 /* We only support identity conversions. */
5089 if (is_identity_fixup(fixup))
5091 TRACE("[OK]\n");
5092 return TRUE;
5095 TRACE("[FAILED]\n");
5096 return FALSE;
5099 const struct blit_shader ffp_blit = {
5100 ffp_blit_alloc,
5101 ffp_blit_free,
5102 ffp_blit_set,
5103 ffp_blit_unset,
5104 ffp_blit_color_fixup_supported