push cc8bc80451cc24f4d7cf75168b569f0ebfe19547
[wine/hacks.git] / dlls / wined3d / surface.c
blobc6eaa69168f29330483d0c4d59daa3443e6112cf
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
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d);
36 #define GLINFO_LOCATION (*gl_info)
38 static void surface_cleanup(IWineD3DSurfaceImpl *This)
40 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
41 const WineD3D_GL_Info *gl_info = &device->adapter->gl_info;
42 renderbuffer_entry_t *entry, *entry2;
44 TRACE("(%p) : Cleaning up.\n", This);
46 /* Need a context to destroy the texture. Use the currently active render
47 * target, but only if the primary render target exists. Otherwise
48 * lastActiveRenderTarget is garbage. When destroying the primary render
49 * target, Uninit3D() will activate a context before doing anything. */
50 if (device->render_targets && device->render_targets[0])
52 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
55 ENTER_GL();
57 if (This->glDescription.textureName)
59 /* Release the OpenGL texture. */
60 TRACE("Deleting texture %u.\n", This->glDescription.textureName);
61 glDeleteTextures(1, &This->glDescription.textureName);
64 if (This->Flags & SFLAG_PBO)
66 /* Delete the PBO. */
67 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
70 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry)
72 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
73 HeapFree(GetProcessHeap(), 0, entry);
76 LEAVE_GL();
78 if (This->Flags & SFLAG_DIBSECTION)
80 /* Release the DC. */
81 SelectObject(This->hDC, This->dib.holdbitmap);
82 DeleteDC(This->hDC);
83 /* Release the DIB section. */
84 DeleteObject(This->dib.DIBsection);
85 This->dib.bitmap_data = NULL;
86 This->resource.allocatedMemory = NULL;
89 if (This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem((IWineD3DSurface *)This, NULL);
90 if (This->overlay_dest) list_remove(&This->overlay_entry);
92 HeapFree(GetProcessHeap(), 0, This->palette9);
94 resource_cleanup((IWineD3DResource *)This);
97 UINT surface_calculate_size(const struct GlPixelFormatDesc *format_desc, UINT alignment, UINT width, UINT height)
99 UINT size;
101 if (format_desc->format == WINED3DFMT_UNKNOWN)
103 size = 0;
105 else if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
107 UINT row_block_count = (width + format_desc->block_width - 1) / format_desc->block_width;
108 UINT row_count = (height + format_desc->block_height - 1) / format_desc->block_height;
109 size = row_count * row_block_count * format_desc->block_byte_count;
111 else
113 /* The pitch is a multiple of 4 bytes. */
114 size = height * (((width * format_desc->byte_count) + alignment - 1) & ~(alignment - 1));
117 if (format_desc->heightscale != 0.0) size *= format_desc->heightscale;
119 return size;
122 HRESULT surface_init(IWineD3DSurfaceImpl *surface, WINED3DSURFTYPE surface_type, UINT alignment,
123 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
124 UINT multisample_quality, IWineD3DDeviceImpl *device, DWORD usage, WINED3DFORMAT format,
125 WINED3DPOOL pool, IUnknown *parent)
127 const WineD3D_GL_Info *gl_info = &device->adapter->gl_info;
128 const struct GlPixelFormatDesc *format_desc = getFormatDescEntry(format, &GLINFO_LOCATION);
129 void (*cleanup)(IWineD3DSurfaceImpl *This);
130 unsigned int resource_size;
131 HRESULT hr;
133 if (multisample_quality > 0)
135 FIXME("multisample_quality set to %u, substituting 0\n", multisample_quality);
136 multisample_quality = 0;
139 /* FIXME: Check that the format is supported by the device. */
141 resource_size = surface_calculate_size(format_desc, alignment, width, height);
143 /* Look at the implementation and set the correct Vtable. */
144 switch (surface_type)
146 case SURFACE_OPENGL:
147 surface->lpVtbl = &IWineD3DSurface_Vtbl;
148 cleanup = surface_cleanup;
149 break;
151 case SURFACE_GDI:
152 surface->lpVtbl = &IWineGDISurface_Vtbl;
153 cleanup = surface_gdi_cleanup;
154 break;
156 default:
157 ERR("Requested unknown surface implementation %#x.\n", surface_type);
158 return WINED3DERR_INVALIDCALL;
161 hr = resource_init((IWineD3DResource *)surface, WINED3DRTYPE_SURFACE,
162 device, resource_size, usage, format_desc, pool, parent);
163 if (FAILED(hr))
165 WARN("Failed to initialize resource, returning %#x.\n", hr);
166 return hr;
169 /* "Standalone" surface. */
170 IWineD3DSurface_SetContainer((IWineD3DSurface *)surface, NULL);
172 surface->currentDesc.Width = width;
173 surface->currentDesc.Height = height;
174 surface->currentDesc.MultiSampleType = multisample_type;
175 surface->currentDesc.MultiSampleQuality = multisample_quality;
176 surface->glDescription.level = level;
177 list_init(&surface->overlays);
179 /* Flags */
180 surface->Flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
181 if (discard) surface->Flags |= SFLAG_DISCARD;
182 if (lockable || format == WINED3DFMT_D16_LOCKABLE) surface->Flags |= SFLAG_LOCKABLE;
184 /* Quick lockable sanity check.
185 * TODO: remove this after surfaces, usage and lockability have been debugged properly
186 * this function is too deep to need to care about things like this.
187 * Levels need to be checked too, since they all affect what can be done. */
188 switch (pool)
190 case WINED3DPOOL_SCRATCH:
191 if(!lockable)
193 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
194 "which are mutually exclusive, setting lockable to TRUE.\n");
195 lockable = TRUE;
197 break;
199 case WINED3DPOOL_SYSTEMMEM:
200 if (!lockable)
201 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
202 break;
204 case WINED3DPOOL_MANAGED:
205 if (usage & WINED3DUSAGE_DYNAMIC)
206 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
207 break;
209 case WINED3DPOOL_DEFAULT:
210 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
211 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
212 break;
214 default:
215 FIXME("Unknown pool %#x.\n", pool);
216 break;
219 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
221 FIXME("Trying to create a render target that isn't in the default pool.\n");
224 /* Mark the texture as dirty so that it gets loaded first time around. */
225 surface_add_dirty_rect((IWineD3DSurface *)surface, NULL);
226 list_init(&surface->renderbuffers);
228 TRACE("surface %p, memory %p, size %u\n", surface, surface->resource.allocatedMemory, surface->resource.size);
230 /* Call the private setup routine */
231 hr = IWineD3DSurface_PrivateSetup((IWineD3DSurface *)surface);
232 if (FAILED(hr))
234 ERR("Private setup failed, returning %#x\n", hr);
235 cleanup(surface);
236 return hr;
239 return hr;
242 static void surface_force_reload(IWineD3DSurface *iface)
244 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
246 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
249 void surface_set_texture_name(IWineD3DSurface *iface, GLuint new_name, BOOL srgb)
251 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
252 GLuint *name;
253 DWORD flag;
255 if(srgb)
257 name = &This->glDescription.srgbTextureName;
258 flag = SFLAG_INSRGBTEX;
260 else
262 name = &This->glDescription.textureName;
263 flag = SFLAG_INTEXTURE;
266 TRACE("(%p) : setting texture name %u\n", This, new_name);
268 if (!*name && new_name)
270 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
271 * surface has no texture name yet. See if we can get rid of this. */
272 if (This->Flags & flag)
273 ERR("Surface has SFLAG_INTEXTURE set, but no texture name\n");
274 IWineD3DSurface_ModifyLocation(iface, flag, FALSE);
277 *name = new_name;
278 surface_force_reload(iface);
281 void surface_set_texture_target(IWineD3DSurface *iface, GLenum target)
283 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
285 TRACE("(%p) : setting target %#x\n", This, target);
287 if (This->glDescription.target != target)
289 if (target == GL_TEXTURE_RECTANGLE_ARB)
291 This->Flags &= ~SFLAG_NORMCOORD;
293 else if (This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB)
295 This->Flags |= SFLAG_NORMCOORD;
298 This->glDescription.target = target;
299 surface_force_reload(iface);
302 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This, BOOL srgb) {
303 int active_sampler;
305 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
306 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
307 * gl states. The current texture unit should always be a valid one.
309 * To be more specific, this is tricky because we can implicitly be called
310 * from sampler() in state.c. This means we can't touch anything other than
311 * whatever happens to be the currently active texture, or we would risk
312 * marking already applied sampler states dirty again.
314 * TODO: Track the current active texture per GL context instead of using glGet
316 GLint active_texture;
317 ENTER_GL();
318 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
319 LEAVE_GL();
320 active_sampler = This->resource.wineD3DDevice->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
322 if (active_sampler != -1) {
323 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(active_sampler));
325 IWineD3DSurface_BindTexture((IWineD3DSurface *)This, srgb);
328 /* This function checks if the primary render target uses the 8bit paletted format. */
329 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
331 if (device->render_targets && device->render_targets[0]) {
332 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
333 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
334 && (render_target->resource.format_desc->format == WINED3DFMT_P8))
335 return TRUE;
337 return FALSE;
340 #undef GLINFO_LOCATION
342 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
344 /* This call just downloads data, the caller is responsible for activating the
345 * right context and binding the correct texture. */
346 static void surface_download_data(IWineD3DSurfaceImpl *This) {
347 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
349 /* Only support read back of converted P8 surfaces */
350 if (This->Flags & SFLAG_CONVERTED && format_desc->format != WINED3DFMT_P8)
352 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(format_desc->format));
353 return;
356 ENTER_GL();
358 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
360 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
361 This, This->glDescription.level, format_desc->glFormat, format_desc->glType,
362 This->resource.allocatedMemory);
364 if (This->Flags & SFLAG_PBO)
366 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
367 checkGLcall("glBindBufferARB");
368 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target, This->glDescription.level, NULL));
369 checkGLcall("glGetCompressedTexImageARB()");
370 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
371 checkGLcall("glBindBufferARB");
373 else
375 GL_EXTCALL(glGetCompressedTexImageARB(This->glDescription.target,
376 This->glDescription.level, This->resource.allocatedMemory));
377 checkGLcall("glGetCompressedTexImageARB()");
380 LEAVE_GL();
381 } else {
382 void *mem;
383 GLenum format = format_desc->glFormat;
384 GLenum type = format_desc->glType;
385 int src_pitch = 0;
386 int dst_pitch = 0;
388 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
389 if (format_desc->format == WINED3DFMT_P8 && primary_render_target_is_p8(This->resource.wineD3DDevice))
391 format = GL_ALPHA;
392 type = GL_UNSIGNED_BYTE;
395 if (This->Flags & SFLAG_NONPOW2) {
396 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
397 src_pitch = format_desc->byte_count * This->pow2Width;
398 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
399 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
400 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
401 } else {
402 mem = This->resource.allocatedMemory;
405 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n", This, This->glDescription.level,
406 format, type, mem);
408 if(This->Flags & SFLAG_PBO) {
409 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
410 checkGLcall("glBindBufferARB");
412 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
413 type, NULL);
414 checkGLcall("glGetTexImage()");
416 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
417 checkGLcall("glBindBufferARB");
418 } else {
419 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
420 type, mem);
421 checkGLcall("glGetTexImage()");
423 LEAVE_GL();
425 if (This->Flags & SFLAG_NONPOW2) {
426 const BYTE *src_data;
427 BYTE *dst_data;
428 UINT y;
430 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
431 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
432 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
434 * We're doing this...
436 * instead of boxing the texture :
437 * |<-texture width ->| -->pow2width| /\
438 * |111111111111111111| | |
439 * |222 Texture 222222| boxed empty | texture height
440 * |3333 Data 33333333| | |
441 * |444444444444444444| | \/
442 * ----------------------------------- |
443 * | boxed empty | boxed empty | pow2height
444 * | | | \/
445 * -----------------------------------
448 * we're repacking the data to the expected texture width
450 * |<-texture width ->| -->pow2width| /\
451 * |111111111111111111222222222222222| |
452 * |222333333333333333333444444444444| texture height
453 * |444444 | |
454 * | | \/
455 * | | |
456 * | empty | pow2height
457 * | | \/
458 * -----------------------------------
460 * == is the same as
462 * |<-texture width ->| /\
463 * |111111111111111111|
464 * |222222222222222222|texture height
465 * |333333333333333333|
466 * |444444444444444444| \/
467 * --------------------
469 * this also means that any references to allocatedMemory should work with the data as if were a
470 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
472 * internally the texture is still stored in a boxed format so any references to textureName will
473 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
475 * Performance should not be an issue, because applications normally do not lock the surfaces when
476 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
477 * and doesn't have to be re-read.
479 src_data = mem;
480 dst_data = This->resource.allocatedMemory;
481 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
482 for (y = 1 ; y < This->currentDesc.Height; y++) {
483 /* skip the first row */
484 src_data += src_pitch;
485 dst_data += dst_pitch;
486 memcpy(dst_data, src_data, dst_pitch);
489 HeapFree(GetProcessHeap(), 0, mem);
493 /* Surface has now been downloaded */
494 This->Flags |= SFLAG_INSYSMEM;
497 /* This call just uploads data, the caller is responsible for activating the
498 * right context and binding the correct texture. */
499 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
500 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
502 if (format_desc->heightscale != 1.0 && format_desc->heightscale != 0.0) height *= format_desc->heightscale;
504 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
506 /* glCompressedTexSubImage2D() for uploading and glTexImage2D() for
507 * allocating does not work well on some drivers (r200 dri, MacOS ATI
508 * driver). glCompressedTexImage2D() does not accept NULL pointers. So
509 * for compressed textures surface_allocate_surface() does nothing,
510 * and this function uses glCompressedTexImage2D() instead of
511 * glCompressedTexSubImage2D(). */
512 TRACE("(%p) : Calling glCompressedTexImage2DARB w %u, h %u, data %p.\n", This, width, height, data);
514 ENTER_GL();
516 if (This->Flags & SFLAG_PBO)
518 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
519 checkGLcall("glBindBufferARB");
521 TRACE("(%p) pbo: %#x, data: %p.\n", This, This->pbo, data);
523 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level,
524 internal, width, height, 0 /* border */, This->resource.size, NULL));
525 checkGLcall("glCompressedTexImage2DARB");
527 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
528 checkGLcall("glBindBufferARB");
530 else
532 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level,
533 internal, width, height, 0 /* border */, This->resource.size, data));
534 checkGLcall("glCompressedTexSubImage2D");
537 LEAVE_GL();
539 else
541 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
542 ENTER_GL();
544 if(This->Flags & SFLAG_PBO) {
545 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
546 checkGLcall("glBindBufferARB");
547 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
549 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
550 checkGLcall("glTexSubImage2D");
552 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
553 checkGLcall("glBindBufferARB");
555 else {
556 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
557 checkGLcall("glTexSubImage2D");
560 LEAVE_GL();
564 /* This call just allocates the texture, the caller is responsible for
565 * activating the right context and binding the correct texture. */
566 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
567 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
568 BOOL enable_client_storage = FALSE;
569 const BYTE *mem = NULL;
571 if (format_desc->heightscale != 1.0 && format_desc->heightscale != 0.0) height *= format_desc->heightscale;
573 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",
574 This, This->glDescription.target, This->glDescription.level, debug_d3dformat(format_desc->format),
575 internal, width, height, format, type);
577 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
579 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
580 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
582 /* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
583 * once, unfortunately
585 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
586 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
587 This->Flags |= SFLAG_CLIENT;
588 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
589 ENTER_GL();
590 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
591 width, height, 0 /* border */, This->resource.size, mem));
592 LEAVE_GL();
595 return;
598 ENTER_GL();
600 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
601 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
602 /* In some cases we want to disable client storage.
603 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
604 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
605 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
606 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
607 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
609 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
610 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
611 This->Flags &= ~SFLAG_CLIENT;
612 enable_client_storage = TRUE;
613 } else {
614 This->Flags |= SFLAG_CLIENT;
616 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
617 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
619 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
622 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
623 checkGLcall("glTexImage2D");
625 if(enable_client_storage) {
626 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
627 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
629 LEAVE_GL();
632 /* In D3D the depth stencil dimensions have to be greater than or equal to the
633 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
634 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
635 /* GL locking is done by the caller */
636 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
637 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
638 renderbuffer_entry_t *entry;
639 GLuint renderbuffer = 0;
640 unsigned int src_width, src_height;
642 src_width = This->pow2Width;
643 src_height = This->pow2Height;
645 /* A depth stencil smaller than the render target is not valid */
646 if (width > src_width || height > src_height) return;
648 /* Remove any renderbuffer set if the sizes match */
649 if (width == src_width && height == src_height) {
650 This->current_renderbuffer = NULL;
651 return;
654 /* Look if we've already got a renderbuffer of the correct dimensions */
655 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
656 if (entry->width == width && entry->height == height) {
657 renderbuffer = entry->id;
658 This->current_renderbuffer = entry;
659 break;
663 if (!renderbuffer) {
664 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
665 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
666 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
667 This->resource.format_desc->glInternal, width, height));
669 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
670 entry->width = width;
671 entry->height = height;
672 entry->id = renderbuffer;
673 list_add_head(&This->renderbuffers, &entry->entry);
675 This->current_renderbuffer = entry;
678 checkGLcall("set_compatible_renderbuffer");
681 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
682 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
683 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
685 TRACE("(%p) : swapchain %p\n", This, swapchain);
687 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
688 TRACE("Returning GL_BACK\n");
689 return GL_BACK;
690 } else if (swapchain_impl->frontBuffer == iface) {
691 TRACE("Returning GL_FRONT\n");
692 return GL_FRONT;
695 FIXME("Higher back buffer, returning GL_BACK\n");
696 return GL_BACK;
699 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
700 void surface_add_dirty_rect(IWineD3DSurface *iface, const RECT *dirty_rect)
702 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
703 IWineD3DBaseTexture *baseTexture = NULL;
705 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
706 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
708 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
709 if (dirty_rect)
711 This->dirtyRect.left = min(This->dirtyRect.left, dirty_rect->left);
712 This->dirtyRect.top = min(This->dirtyRect.top, dirty_rect->top);
713 This->dirtyRect.right = max(This->dirtyRect.right, dirty_rect->right);
714 This->dirtyRect.bottom = max(This->dirtyRect.bottom, dirty_rect->bottom);
716 else
718 This->dirtyRect.left = 0;
719 This->dirtyRect.top = 0;
720 This->dirtyRect.right = This->currentDesc.Width;
721 This->dirtyRect.bottom = This->currentDesc.Height;
724 TRACE("(%p) : Dirty: yes, Rect:(%d, %d, %d, %d)\n", This, This->dirtyRect.left,
725 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
727 /* if the container is a basetexture then mark it dirty. */
728 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture)))
730 TRACE("Passing to container\n");
731 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
732 IWineD3DBaseTexture_Release(baseTexture);
736 static inline BOOL surface_can_stretch_rect(IWineD3DSurfaceImpl *src, IWineD3DSurfaceImpl *dst)
738 return ((src->resource.format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE)
739 || (src->resource.usage & WINED3DUSAGE_RENDERTARGET))
740 && ((dst->resource.format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE)
741 || (dst->resource.usage & WINED3DUSAGE_RENDERTARGET))
742 && (src->resource.format_desc->format == dst->resource.format_desc->format
743 || (is_identity_fixup(src->resource.format_desc->color_fixup)
744 && is_identity_fixup(dst->resource.format_desc->color_fixup)));
747 static ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface)
749 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
750 ULONG ref = InterlockedDecrement(&This->resource.ref);
751 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
753 if (!ref)
755 surface_cleanup(This);
757 TRACE("(%p) Released.\n", This);
758 HeapFree(GetProcessHeap(), 0, This);
761 return ref;
764 /* ****************************************************
765 IWineD3DSurface IWineD3DResource parts follow
766 **************************************************** */
768 void surface_internal_preload(IWineD3DSurface *iface, enum WINED3DSRGB srgb)
770 /* TODO: check for locks */
771 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
772 IWineD3DBaseTexture *baseTexture = NULL;
773 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
775 TRACE("(%p)Checking to see if the container is a base texture\n", This);
776 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
777 IWineD3DBaseTextureImpl *tex_impl = (IWineD3DBaseTextureImpl *) baseTexture;
778 TRACE("Passing to container\n");
779 tex_impl->baseTexture.internal_preload(baseTexture, SRGB_RGB);
780 IWineD3DBaseTexture_Release(baseTexture);
781 } else {
782 TRACE("(%p) : About to load surface\n", This);
784 if(!device->isInDraw) {
785 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
788 if (This->resource.format_desc->format == WINED3DFMT_P8
789 || This->resource.format_desc->format == WINED3DFMT_A8P8)
791 if(palette9_changed(This)) {
792 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
793 /* TODO: This is not necessarily needed with hw palettized texture support */
794 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
795 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
796 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
800 IWineD3DSurface_LoadTexture(iface, srgb == SRGB_SRGB ? TRUE : FALSE);
802 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
803 /* Tell opengl to try and keep this texture in video ram (well mostly) */
804 GLclampf tmp;
805 tmp = 0.9f;
806 ENTER_GL();
807 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
808 LEAVE_GL();
811 return;
814 static void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
815 surface_internal_preload(iface, SRGB_ANY);
818 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
819 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
820 This->resource.allocatedMemory =
821 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
823 ENTER_GL();
824 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
825 checkGLcall("glBindBuffer(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
826 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
827 checkGLcall("glGetBufferSubData");
828 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
829 checkGLcall("glDeleteBuffers");
830 LEAVE_GL();
832 This->pbo = 0;
833 This->Flags &= ~SFLAG_PBO;
836 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
837 IWineD3DBaseTexture *texture = NULL;
838 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
839 renderbuffer_entry_t *entry, *entry2;
840 TRACE("(%p)\n", iface);
842 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
843 /* Default pool resources are supposed to be destroyed before Reset is called.
844 * Implicit resources stay however. So this means we have an implicit render target
845 * or depth stencil. The content may be destroyed, but we still have to tear down
846 * opengl resources, so we cannot leave early.
848 * Put the most up to date surface location into the drawable. D3D-wise this content
849 * is undefined, so it would be nowhere, but that would make the location management
850 * more complicated. The drawable is a sane location, because if we mark sysmem or
851 * texture up to date, drawPrim will copy the uninitialized texture or sysmem to the
852 * uninitialized drawable. That's pointless and we'd have to allocate the texture /
853 * sysmem copy here.
855 if (This->resource.usage & WINED3DUSAGE_DEPTHSTENCIL) {
856 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
857 } else {
858 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, TRUE);
860 } else {
861 /* Load the surface into system memory */
862 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
863 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
865 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
866 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
868 /* Destroy PBOs, but load them into real sysmem before */
869 if(This->Flags & SFLAG_PBO) {
870 surface_remove_pbo(This);
873 /* Destroy fbo render buffers. This is needed for implicit render targets, for
874 * all application-created targets the application has to release the surface
875 * before calling _Reset
877 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
878 ENTER_GL();
879 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
880 LEAVE_GL();
881 list_remove(&entry->entry);
882 HeapFree(GetProcessHeap(), 0, entry);
884 list_init(&This->renderbuffers);
885 This->current_renderbuffer = NULL;
887 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
888 * destroy it
890 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
891 if(!texture) {
892 ENTER_GL();
893 glDeleteTextures(1, &This->glDescription.textureName);
894 This->glDescription.textureName = 0;
895 glDeleteTextures(1, &This->glDescription.srgbTextureName);
896 This->glDescription.srgbTextureName = 0;
897 LEAVE_GL();
898 } else {
899 IWineD3DBaseTexture_Release(texture);
901 return;
904 /* ******************************************************
905 IWineD3DSurface IWineD3DSurface parts follow
906 ****************************************************** */
908 static void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription)
910 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
911 TRACE("(%p) : returning %p\n", This, &This->glDescription);
912 *glDescription = &This->glDescription;
915 /* Read the framebuffer back into the surface */
916 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
917 IWineD3DSwapChainImpl *swapchain;
918 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
919 BYTE *mem;
920 GLint fmt;
921 GLint type;
922 BYTE *row, *top, *bottom;
923 int i;
924 BOOL bpp;
925 RECT local_rect;
926 BOOL srcIsUpsideDown;
927 GLint rowLen = 0;
928 GLint skipPix = 0;
929 GLint skipRow = 0;
931 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
932 static BOOL warned = FALSE;
933 if(!warned) {
934 ERR("The application tries to lock the render target, but render target locking is disabled\n");
935 warned = TRUE;
937 return;
940 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
941 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
942 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
943 * context->last_was_blit set on the unlock.
945 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
946 ENTER_GL();
948 /* Select the correct read buffer, and give some debug output.
949 * There is no need to keep track of the current read buffer or reset it, every part of the code
950 * that reads sets the read buffer as desired.
952 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
954 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
955 TRACE("Locking %#x buffer\n", buffer);
956 glReadBuffer(buffer);
957 checkGLcall("glReadBuffer");
959 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
960 srcIsUpsideDown = FALSE;
961 } else {
962 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
963 * Read from the back buffer
965 TRACE("Locking offscreen render target\n");
966 glReadBuffer(myDevice->offscreenBuffer);
967 srcIsUpsideDown = TRUE;
970 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
971 if(!rect) {
972 local_rect.left = 0;
973 local_rect.top = 0;
974 local_rect.right = This->currentDesc.Width;
975 local_rect.bottom = This->currentDesc.Height;
976 } else {
977 local_rect = *rect;
979 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
981 switch(This->resource.format_desc->format)
983 case WINED3DFMT_P8:
985 if(primary_render_target_is_p8(myDevice)) {
986 /* In case of P8 render targets the index is stored in the alpha component */
987 fmt = GL_ALPHA;
988 type = GL_UNSIGNED_BYTE;
989 mem = dest;
990 bpp = This->resource.format_desc->byte_count;
991 } else {
992 /* GL can't return palettized data, so read ARGB pixels into a
993 * separate block of memory and convert them into palettized format
994 * in software. Slow, but if the app means to use palettized render
995 * targets and locks it...
997 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
998 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
999 * for the color channels when palettizing the colors.
1001 fmt = GL_RGB;
1002 type = GL_UNSIGNED_BYTE;
1003 pitch *= 3;
1004 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
1005 if(!mem) {
1006 ERR("Out of memory\n");
1007 LEAVE_GL();
1008 return;
1010 bpp = This->resource.format_desc->byte_count * 3;
1013 break;
1015 default:
1016 mem = dest;
1017 fmt = This->resource.format_desc->glFormat;
1018 type = This->resource.format_desc->glType;
1019 bpp = This->resource.format_desc->byte_count;
1022 if(This->Flags & SFLAG_PBO) {
1023 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
1024 checkGLcall("glBindBufferARB");
1025 if(mem != NULL) {
1026 ERR("mem not null for pbo -- unexpected\n");
1027 mem = NULL;
1031 /* Save old pixel store pack state */
1032 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
1033 checkGLcall("glIntegerv");
1034 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
1035 checkGLcall("glIntegerv");
1036 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
1037 checkGLcall("glIntegerv");
1039 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1040 glPixelStorei(GL_PACK_ROW_LENGTH, This->currentDesc.Width);
1041 checkGLcall("glPixelStorei");
1042 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
1043 checkGLcall("glPixelStorei");
1044 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
1045 checkGLcall("glPixelStorei");
1047 glReadPixels(local_rect.left, (!srcIsUpsideDown) ? (This->currentDesc.Height - local_rect.bottom) : local_rect.top ,
1048 local_rect.right - local_rect.left,
1049 local_rect.bottom - local_rect.top,
1050 fmt, type, mem);
1051 checkGLcall("glReadPixels");
1053 /* Reset previous pixel store pack state */
1054 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
1055 checkGLcall("glPixelStorei");
1056 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
1057 checkGLcall("glPixelStorei");
1058 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
1059 checkGLcall("glPixelStorei");
1061 if(This->Flags & SFLAG_PBO) {
1062 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1063 checkGLcall("glBindBufferARB");
1065 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
1066 * to get a pointer to it and perform the flipping in software. This is a lot
1067 * faster than calling glReadPixels for each line. In case we want more speed
1068 * we should rerender it flipped in a FBO and read the data back from the FBO. */
1069 if(!srcIsUpsideDown) {
1070 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1071 checkGLcall("glBindBufferARB");
1073 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1074 checkGLcall("glMapBufferARB");
1078 /* TODO: Merge this with the palettization loop below for P8 targets */
1079 if(!srcIsUpsideDown) {
1080 UINT len, off;
1081 /* glReadPixels returns the image upside down, and there is no way to prevent this.
1082 Flip the lines in software */
1083 len = (local_rect.right - local_rect.left) * bpp;
1084 off = local_rect.left * bpp;
1086 row = HeapAlloc(GetProcessHeap(), 0, len);
1087 if(!row) {
1088 ERR("Out of memory\n");
1089 if (This->resource.format_desc->format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
1090 LEAVE_GL();
1091 return;
1094 top = mem + pitch * local_rect.top;
1095 bottom = mem + pitch * (local_rect.bottom - 1);
1096 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
1097 memcpy(row, top + off, len);
1098 memcpy(top + off, bottom + off, len);
1099 memcpy(bottom + off, row, len);
1100 top += pitch;
1101 bottom -= pitch;
1103 HeapFree(GetProcessHeap(), 0, row);
1105 /* Unmap the temp PBO buffer */
1106 if(This->Flags & SFLAG_PBO) {
1107 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1108 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1112 LEAVE_GL();
1114 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
1115 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
1116 * the same color but we have no choice.
1117 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
1119 if ((This->resource.format_desc->format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice))
1121 const PALETTEENTRY *pal = NULL;
1122 DWORD width = pitch / 3;
1123 int x, y, c;
1125 if(This->palette) {
1126 pal = This->palette->palents;
1127 } else {
1128 ERR("Palette is missing, cannot perform inverse palette lookup\n");
1129 HeapFree(GetProcessHeap(), 0, mem);
1130 return ;
1133 for(y = local_rect.top; y < local_rect.bottom; y++) {
1134 for(x = local_rect.left; x < local_rect.right; x++) {
1135 /* start lines pixels */
1136 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
1137 const BYTE *green = blue + 1;
1138 const BYTE *red = green + 1;
1140 for(c = 0; c < 256; c++) {
1141 if(*red == pal[c].peRed &&
1142 *green == pal[c].peGreen &&
1143 *blue == pal[c].peBlue)
1145 *((BYTE *) dest + y * width + x) = c;
1146 break;
1151 HeapFree(GetProcessHeap(), 0, mem);
1155 /* Read the framebuffer contents into a texture */
1156 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This, BOOL srgb)
1158 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1159 IWineD3DSwapChainImpl *swapchain;
1160 int bpp;
1161 GLenum format, internal, type;
1162 CONVERT_TYPES convert;
1163 GLint prevRead;
1164 BOOL alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
1166 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
1168 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
1169 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
1170 * states in the stateblock, and no driver was found yet that had bugs in that regard.
1172 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
1173 surface_bind_and_dirtify(This, srgb);
1175 ENTER_GL();
1176 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1177 LEAVE_GL();
1179 /* Select the correct read buffer, and give some debug output.
1180 * There is no need to keep track of the current read buffer or reset it, every part of the code
1181 * that reads sets the read buffer as desired.
1183 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
1185 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1186 TRACE("Locking %#x buffer\n", buffer);
1188 ENTER_GL();
1189 glReadBuffer(buffer);
1190 checkGLcall("glReadBuffer");
1191 LEAVE_GL();
1193 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1194 } else {
1195 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1196 * Read from the back buffer
1198 TRACE("Locking offscreen render target\n");
1199 ENTER_GL();
1200 glReadBuffer(device->offscreenBuffer);
1201 checkGLcall("glReadBuffer");
1202 LEAVE_GL();
1205 if(!(This->Flags & alloc_flag)) {
1206 surface_allocate_surface(This, internal, This->pow2Width,
1207 This->pow2Height, format, type);
1208 This->Flags |= alloc_flag;
1211 ENTER_GL();
1212 /* If !SrcIsUpsideDown we should flip the surface.
1213 * This can be done using glCopyTexSubImage2D but this
1214 * is VERY slow, so don't do that. We should prevent
1215 * this code from getting called in such cases or perhaps
1216 * we can use FBOs */
1218 glCopyTexSubImage2D(This->glDescription.target,
1219 This->glDescription.level,
1220 0, 0, 0, 0,
1221 This->currentDesc.Width,
1222 This->currentDesc.Height);
1223 checkGLcall("glCopyTexSubImage2D");
1225 glReadBuffer(prevRead);
1226 checkGLcall("glReadBuffer");
1228 LEAVE_GL();
1229 TRACE("Updated target %d\n", This->glDescription.target);
1232 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
1233 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
1234 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
1235 * changed
1237 if(!(This->Flags & SFLAG_DYNLOCK)) {
1238 This->lockCount++;
1239 /* MAXLOCKCOUNT is defined in wined3d_private.h */
1240 if(This->lockCount > MAXLOCKCOUNT) {
1241 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
1242 This->Flags |= SFLAG_DYNLOCK;
1246 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
1247 * Also don't create a PBO for systemmem surfaces.
1249 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
1250 GLenum error;
1251 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1253 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1254 ENTER_GL();
1256 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
1257 error = glGetError();
1258 if(This->pbo == 0 || error != GL_NO_ERROR) {
1259 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
1262 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
1264 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1265 checkGLcall("glBindBufferARB");
1267 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1268 checkGLcall("glBufferDataARB");
1270 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1271 checkGLcall("glBindBufferARB");
1273 /* We don't need the system memory anymore and we can't even use it for PBOs */
1274 if(!(This->Flags & SFLAG_CLIENT)) {
1275 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1276 This->resource.heapMemory = NULL;
1278 This->resource.allocatedMemory = NULL;
1279 This->Flags |= SFLAG_PBO;
1280 LEAVE_GL();
1281 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
1282 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1283 * or a pbo to map
1285 if(!This->resource.heapMemory) {
1286 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1288 This->resource.allocatedMemory =
1289 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1290 if(This->Flags & SFLAG_INSYSMEM) {
1291 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1296 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1297 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1298 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1299 const RECT *pass_rect = pRect;
1301 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1303 /* This is also done in the base class, but we have to verify this before loading any data from
1304 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1305 * may interfere, and all other bad things may happen
1307 if (This->Flags & SFLAG_LOCKED) {
1308 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1309 return WINED3DERR_INVALIDCALL;
1311 This->Flags |= SFLAG_LOCKED;
1313 if (!(This->Flags & SFLAG_LOCKABLE))
1315 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1318 if (Flags & WINED3DLOCK_DISCARD) {
1319 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1320 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1321 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1322 This->Flags |= SFLAG_INSYSMEM;
1323 goto lock_end;
1326 if (This->Flags & SFLAG_INSYSMEM) {
1327 TRACE("Local copy is up to date, not downloading data\n");
1328 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1329 goto lock_end;
1332 /* IWineD3DSurface_LoadLocation() does not check if the rectangle specifies
1333 * the full surface. Most callers don't need that, so do it here. */
1334 if (pRect && pRect->top == 0 && pRect->left == 0
1335 && pRect->right == This->currentDesc.Width
1336 && pRect->bottom == This->currentDesc.Height)
1338 pass_rect = NULL;
1341 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
1342 && ((This->Flags & SFLAG_SWAPCHAIN) || iface == myDevice->render_targets[0])))
1344 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1347 lock_end:
1348 if(This->Flags & SFLAG_PBO) {
1349 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1350 ENTER_GL();
1351 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1352 checkGLcall("glBindBufferARB");
1354 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1355 if(This->resource.allocatedMemory) {
1356 ERR("The surface already has PBO memory allocated!\n");
1359 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1360 checkGLcall("glMapBufferARB");
1362 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1363 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1364 checkGLcall("glBindBufferARB");
1366 LEAVE_GL();
1369 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1370 /* Don't dirtify */
1371 } else {
1372 IWineD3DBaseTexture *pBaseTexture;
1374 * Dirtify on lock
1375 * as seen in msdn docs
1377 surface_add_dirty_rect(iface, pRect);
1379 /** Dirtify Container if needed */
1380 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1381 TRACE("Making container dirty\n");
1382 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1383 IWineD3DBaseTexture_Release(pBaseTexture);
1384 } else {
1385 TRACE("Surface is standalone, no need to dirty the container\n");
1389 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1392 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1393 GLint prev_store;
1394 GLint prev_rasterpos[4];
1395 GLint skipBytes = 0;
1396 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1397 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1398 IWineD3DSwapChainImpl *swapchain;
1400 /* Activate the correct context for the render target */
1401 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1402 ENTER_GL();
1404 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
1405 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1406 TRACE("Unlocking %#x buffer\n", buffer);
1407 glDrawBuffer(buffer);
1408 checkGLcall("glDrawBuffer");
1410 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1411 } else {
1412 /* Primary offscreen render target */
1413 TRACE("Offscreen render target\n");
1414 glDrawBuffer(myDevice->offscreenBuffer);
1415 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1418 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1419 checkGLcall("glIntegerv");
1420 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1421 checkGLcall("glIntegerv");
1422 glPixelZoom(1.0, -1.0);
1423 checkGLcall("glPixelZoom");
1425 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1426 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1427 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1429 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1430 checkGLcall("glRasterPos2f");
1432 /* Some drivers(radeon dri, others?) don't like exceptions during
1433 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1434 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1435 * catch to put the dib section in InSync mode, which leads to a crash
1436 * and a blocked x server on my radeon card.
1438 * The following lines read the dib section so it is put in InSync mode
1439 * before glDrawPixels is called and the crash is prevented. There won't
1440 * be any interfering gdi accesses, because UnlockRect is called from
1441 * ReleaseDC, and the app won't use the dc any more afterwards.
1443 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1444 volatile BYTE read;
1445 read = This->resource.allocatedMemory[0];
1448 if(This->Flags & SFLAG_PBO) {
1449 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1450 checkGLcall("glBindBufferARB");
1453 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1454 if(This->Flags & SFLAG_LOCKED) {
1455 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1456 (This->lockedRect.bottom - This->lockedRect.top)-1,
1457 fmt, type,
1458 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1459 checkGLcall("glDrawPixels");
1460 } else {
1461 glDrawPixels(This->currentDesc.Width,
1462 This->currentDesc.Height,
1463 fmt, type, mem);
1464 checkGLcall("glDrawPixels");
1467 if(This->Flags & SFLAG_PBO) {
1468 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1469 checkGLcall("glBindBufferARB");
1472 glPixelZoom(1.0,1.0);
1473 checkGLcall("glPixelZoom");
1475 glRasterPos3iv(&prev_rasterpos[0]);
1476 checkGLcall("glRasterPos3iv");
1478 /* Reset to previous pack row length */
1479 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1480 checkGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1482 if(!swapchain) {
1483 glDrawBuffer(myDevice->offscreenBuffer);
1484 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1485 } else if(swapchain->backBuffer) {
1486 glDrawBuffer(GL_BACK);
1487 checkGLcall("glDrawBuffer(GL_BACK)");
1488 } else {
1489 glDrawBuffer(GL_FRONT);
1490 checkGLcall("glDrawBuffer(GL_FRONT)");
1492 LEAVE_GL();
1494 return;
1497 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1498 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1499 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1500 BOOL fullsurface;
1502 if (!(This->Flags & SFLAG_LOCKED)) {
1503 WARN("trying to Unlock an unlocked surf@%p\n", This);
1504 return WINEDDERR_NOTLOCKED;
1507 if (This->Flags & SFLAG_PBO) {
1508 TRACE("Freeing PBO memory\n");
1509 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1510 ENTER_GL();
1511 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1512 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1513 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1514 checkGLcall("glUnmapBufferARB");
1515 LEAVE_GL();
1516 This->resource.allocatedMemory = NULL;
1519 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1521 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1522 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1523 goto unlock_end;
1526 if ((This->Flags & SFLAG_SWAPCHAIN) || (myDevice->render_targets && iface == myDevice->render_targets[0]))
1528 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1529 static BOOL warned = FALSE;
1530 if(!warned) {
1531 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1532 warned = TRUE;
1534 goto unlock_end;
1537 if(This->dirtyRect.left == 0 &&
1538 This->dirtyRect.top == 0 &&
1539 This->dirtyRect.right == This->currentDesc.Width &&
1540 This->dirtyRect.bottom == This->currentDesc.Height) {
1541 fullsurface = TRUE;
1542 } else {
1543 /* TODO: Proper partial rectangle tracking */
1544 fullsurface = FALSE;
1545 This->Flags |= SFLAG_INSYSMEM;
1548 switch(wined3d_settings.rendertargetlock_mode) {
1549 case RTL_READTEX:
1550 case RTL_TEXTEX:
1551 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1552 /* drop through */
1554 case RTL_AUTO:
1555 case RTL_READDRAW:
1556 case RTL_TEXDRAW:
1557 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1558 break;
1561 if(!fullsurface) {
1562 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1563 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1564 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1565 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1566 * not fully up to date because only a subrectangle was read in LockRect.
1568 This->Flags &= ~SFLAG_INSYSMEM;
1569 This->Flags |= SFLAG_INDRAWABLE;
1572 This->dirtyRect.left = This->currentDesc.Width;
1573 This->dirtyRect.top = This->currentDesc.Height;
1574 This->dirtyRect.right = 0;
1575 This->dirtyRect.bottom = 0;
1576 } else if(iface == myDevice->stencilBufferTarget) {
1577 FIXME("Depth Stencil buffer locking is not implemented\n");
1578 } else {
1579 /* The rest should be a normal texture */
1580 IWineD3DBaseTextureImpl *impl;
1581 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1582 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1583 * states need resetting
1585 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1586 if(impl->baseTexture.bindCount) {
1587 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1589 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1593 unlock_end:
1594 This->Flags &= ~SFLAG_LOCKED;
1595 memset(&This->lockedRect, 0, sizeof(RECT));
1597 /* Overlays have to be redrawn manually after changes with the GL implementation */
1598 if(This->overlay_dest) {
1599 IWineD3DSurface_DrawOverlay(iface);
1601 return WINED3D_OK;
1604 static HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC)
1606 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1607 WINED3DLOCKED_RECT lock;
1608 HRESULT hr;
1609 RGBQUAD col[256];
1611 TRACE("(%p)->(%p)\n",This,pHDC);
1613 if(This->Flags & SFLAG_USERPTR) {
1614 ERR("Not supported on surfaces with an application-provided surfaces\n");
1615 return WINEDDERR_NODC;
1618 /* Give more detailed info for ddraw */
1619 if (This->Flags & SFLAG_DCINUSE)
1620 return WINEDDERR_DCALREADYCREATED;
1622 /* Can't GetDC if the surface is locked */
1623 if (This->Flags & SFLAG_LOCKED)
1624 return WINED3DERR_INVALIDCALL;
1626 /* According to Direct3D9 docs, only these formats are supported */
1627 if (((IWineD3DImpl *)This->resource.wineD3DDevice->wineD3D)->dxVersion > 7) {
1628 if (This->resource.format_desc->format != WINED3DFMT_R5G6B5
1629 && This->resource.format_desc->format != WINED3DFMT_X1R5G5B5
1630 && This->resource.format_desc->format != WINED3DFMT_R8G8B8
1631 && This->resource.format_desc->format != WINED3DFMT_X8R8G8B8)
1632 return WINED3DERR_INVALIDCALL;
1635 memset(&lock, 0, sizeof(lock)); /* To be sure */
1637 /* Create a DIB section if there isn't a hdc yet */
1638 if(!This->hDC) {
1639 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1640 if(This->Flags & SFLAG_CLIENT) {
1641 surface_internal_preload(iface, SRGB_RGB);
1644 /* Use the dib section from now on if we are not using a PBO */
1645 if(!(This->Flags & SFLAG_PBO))
1646 This->resource.allocatedMemory = This->dib.bitmap_data;
1649 /* Lock the surface */
1650 hr = IWineD3DSurface_LockRect(iface,
1651 &lock,
1652 NULL,
1655 if(This->Flags & SFLAG_PBO) {
1656 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1657 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1660 if(FAILED(hr)) {
1661 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1662 /* keep the dib section */
1663 return hr;
1666 if (This->resource.format_desc->format == WINED3DFMT_P8
1667 || This->resource.format_desc->format == WINED3DFMT_A8P8)
1669 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1670 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1671 unsigned int n;
1672 const PALETTEENTRY *pal = NULL;
1674 if(This->palette) {
1675 pal = This->palette->palents;
1676 } else {
1677 IWineD3DSurfaceImpl *dds_primary;
1678 IWineD3DSwapChainImpl *swapchain;
1679 swapchain = (IWineD3DSwapChainImpl *)This->resource.wineD3DDevice->swapchains[0];
1680 dds_primary = (IWineD3DSurfaceImpl *)swapchain->frontBuffer;
1681 if (dds_primary && dds_primary->palette)
1682 pal = dds_primary->palette->palents;
1685 if (pal) {
1686 for (n=0; n<256; n++) {
1687 col[n].rgbRed = pal[n].peRed;
1688 col[n].rgbGreen = pal[n].peGreen;
1689 col[n].rgbBlue = pal[n].peBlue;
1690 col[n].rgbReserved = 0;
1692 SetDIBColorTable(This->hDC, 0, 256, col);
1696 *pHDC = This->hDC;
1697 TRACE("returning %p\n",*pHDC);
1698 This->Flags |= SFLAG_DCINUSE;
1700 return WINED3D_OK;
1703 static HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC)
1705 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1707 TRACE("(%p)->(%p)\n",This,hDC);
1709 if (!(This->Flags & SFLAG_DCINUSE))
1710 return WINEDDERR_NODC;
1712 if (This->hDC !=hDC) {
1713 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1714 return WINEDDERR_NODC;
1717 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1718 /* Copy the contents of the DIB over to the PBO */
1719 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1722 /* we locked first, so unlock now */
1723 IWineD3DSurface_UnlockRect(iface);
1725 This->Flags &= ~SFLAG_DCINUSE;
1727 return WINED3D_OK;
1730 /* ******************************************************
1731 IWineD3DSurface Internal (No mapping to directx api) parts follow
1732 ****************************************************** */
1734 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) {
1735 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1736 const struct GlPixelFormatDesc *glDesc = This->resource.format_desc;
1737 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1739 /* Default values: From the surface */
1740 *format = glDesc->glFormat;
1741 *type = glDesc->glType;
1742 *convert = NO_CONVERSION;
1743 *target_bpp = glDesc->byte_count;
1745 if(srgb_mode) {
1746 *internal = glDesc->glGammaInternal;
1748 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET
1749 && !(This->Flags & SFLAG_SWAPCHAIN))
1751 *internal = glDesc->rtInternal;
1752 } else {
1753 *internal = glDesc->glInternal;
1756 /* Ok, now look if we have to do any conversion */
1757 switch(This->resource.format_desc->format)
1759 case WINED3DFMT_P8:
1760 /* ****************
1761 Paletted Texture
1762 **************** */
1764 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1765 * of the two is available make sure texturing is requested as neither of the two works in
1766 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1767 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1768 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1769 * conflicts with this.
1771 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) ||
1772 (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) &&
1773 device->render_targets &&
1774 This == (IWineD3DSurfaceImpl*)device->render_targets[0])) ||
1775 colorkey_active || !use_texturing ) {
1776 *format = GL_RGBA;
1777 *internal = GL_RGBA;
1778 *type = GL_UNSIGNED_BYTE;
1779 *target_bpp = 4;
1780 if(colorkey_active) {
1781 *convert = CONVERT_PALETTED_CK;
1782 } else {
1783 *convert = CONVERT_PALETTED;
1786 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1787 *format = GL_ALPHA;
1788 *type = GL_UNSIGNED_BYTE;
1789 *target_bpp = 1;
1792 break;
1794 case WINED3DFMT_R3G3B2:
1795 /* **********************
1796 GL_UNSIGNED_BYTE_3_3_2
1797 ********************** */
1798 if (colorkey_active) {
1799 /* This texture format will never be used.. So do not care about color keying
1800 up until the point in time it will be needed :-) */
1801 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1803 break;
1805 case WINED3DFMT_R5G6B5:
1806 if (colorkey_active) {
1807 *convert = CONVERT_CK_565;
1808 *format = GL_RGBA;
1809 *internal = GL_RGB5_A1;
1810 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1812 break;
1814 case WINED3DFMT_X1R5G5B5:
1815 if (colorkey_active) {
1816 *convert = CONVERT_CK_5551;
1817 *format = GL_BGRA;
1818 *internal = GL_RGB5_A1;
1819 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1821 break;
1823 case WINED3DFMT_R8G8B8:
1824 if (colorkey_active) {
1825 *convert = CONVERT_CK_RGB24;
1826 *format = GL_RGBA;
1827 *internal = GL_RGBA8;
1828 *type = GL_UNSIGNED_INT_8_8_8_8;
1829 *target_bpp = 4;
1831 break;
1833 case WINED3DFMT_X8R8G8B8:
1834 if (colorkey_active) {
1835 *convert = CONVERT_RGB32_888;
1836 *format = GL_RGBA;
1837 *internal = GL_RGBA8;
1838 *type = GL_UNSIGNED_INT_8_8_8_8;
1840 break;
1842 case WINED3DFMT_R8G8_SNORM:
1843 if (GL_SUPPORT(NV_TEXTURE_SHADER)) break;
1844 *convert = CONVERT_V8U8;
1845 *format = GL_BGR;
1846 *type = GL_UNSIGNED_BYTE;
1847 *target_bpp = 3;
1848 break;
1850 case WINED3DFMT_L6V5U5:
1851 *convert = CONVERT_L6V5U5;
1852 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1853 *target_bpp = 3;
1854 /* Use format and types from table */
1855 } else {
1856 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1857 *target_bpp = 2;
1858 *format = GL_RGB;
1859 *type = GL_UNSIGNED_SHORT_5_6_5;
1861 break;
1863 case WINED3DFMT_X8L8V8U8:
1864 *convert = CONVERT_X8L8V8U8;
1865 *target_bpp = 4;
1866 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1867 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1868 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1869 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1870 * the needed type and format parameter, so the internal format contains a
1871 * 4th component, which is returned as alpha
1873 } else {
1874 *format = GL_BGRA;
1875 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1877 break;
1879 case WINED3DFMT_R8G8B8A8_SNORM:
1880 if (GL_SUPPORT(NV_TEXTURE_SHADER)) break;
1881 *convert = CONVERT_Q8W8V8U8;
1882 *format = GL_BGRA;
1883 *type = GL_UNSIGNED_BYTE;
1884 *target_bpp = 4;
1885 break;
1887 case WINED3DFMT_R16G16_SNORM:
1888 if (GL_SUPPORT(NV_TEXTURE_SHADER)) break;
1889 *convert = CONVERT_V16U16;
1890 *format = GL_BGR;
1891 *type = GL_UNSIGNED_SHORT;
1892 *target_bpp = 6;
1893 break;
1895 case WINED3DFMT_A4L4:
1896 /* A4L4 exists as an internal gl format, but for some reason there is not
1897 * format+type combination to load it. Thus convert it to A8L8, then load it
1898 * with A4L4 internal, but A8L8 format+type
1900 *convert = CONVERT_A4L4;
1901 *format = GL_LUMINANCE_ALPHA;
1902 *type = GL_UNSIGNED_BYTE;
1903 *target_bpp = 2;
1904 break;
1906 case WINED3DFMT_R16G16_UNORM:
1907 *convert = CONVERT_G16R16;
1908 *format = GL_RGB;
1909 *type = GL_UNSIGNED_SHORT;
1910 *target_bpp = 6;
1911 break;
1913 case WINED3DFMT_R16G16_FLOAT:
1914 *convert = CONVERT_R16G16F;
1915 *format = GL_RGB;
1916 *type = GL_HALF_FLOAT_ARB;
1917 *target_bpp = 6;
1918 break;
1920 case WINED3DFMT_R32G32_FLOAT:
1921 *convert = CONVERT_R32G32F;
1922 *format = GL_RGB;
1923 *type = GL_FLOAT;
1924 *target_bpp = 12;
1925 break;
1927 case WINED3DFMT_D15S1:
1928 if (GL_SUPPORT(EXT_PACKED_DEPTH_STENCIL))
1930 *convert = CONVERT_D15S1;
1931 *target_bpp = 4;
1933 break;
1935 case WINED3DFMT_D24X4S4:
1936 if (GL_SUPPORT(EXT_PACKED_DEPTH_STENCIL))
1938 *convert = CONVERT_D24X4S4;
1940 break;
1942 case WINED3DFMT_D24FS8:
1943 if (GL_SUPPORT(ARB_DEPTH_BUFFER_FLOAT))
1945 *convert = CONVERT_D24FS8;
1946 *target_bpp = 8;
1948 break;
1950 default:
1951 break;
1954 return WINED3D_OK;
1957 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey)
1959 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1960 IWineD3DPaletteImpl *pal = This->palette;
1961 BOOL index_in_alpha = FALSE;
1962 unsigned int i;
1964 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
1965 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
1966 * is slow. Further RGB->P8 conversion is not possible because palettes can have
1967 * duplicate entries. Store the color key in the unused alpha component to speed the
1968 * download up and to make conversion unneeded. */
1969 index_in_alpha = primary_render_target_is_p8(device);
1971 if (!pal)
1973 UINT dxVersion = ((IWineD3DImpl *)device->wineD3D)->dxVersion;
1975 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
1976 if (dxVersion <= 7)
1978 ERR("This code should never get entered for DirectDraw!, expect problems\n");
1979 if (index_in_alpha)
1981 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
1982 * there's no palette at this time. */
1983 for (i = 0; i < 256; i++) table[i][3] = i;
1986 else
1988 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
1989 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
1990 * capability flag is present (wine does advertise this capability) */
1991 for (i = 0; i < 256; ++i)
1993 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1994 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1995 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1996 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2000 else
2002 TRACE("Using surface palette %p\n", pal);
2003 /* Get the surface's palette */
2004 for (i = 0; i < 256; ++i)
2006 table[i][0] = pal->palents[i].peRed;
2007 table[i][1] = pal->palents[i].peGreen;
2008 table[i][2] = pal->palents[i].peBlue;
2010 /* When index_in_alpha is set the palette index is stored in the
2011 * alpha component. In case of a readback we can then read
2012 * GL_ALPHA. Color keying is handled in BltOverride using a
2013 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
2014 * color key itself is passed to glAlphaFunc in other cases the
2015 * alpha component of pixels that should be masked away is set to 0. */
2016 if (index_in_alpha)
2018 table[i][3] = i;
2020 else if (colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue)
2021 && (i <= This->SrcBltCKey.dwColorSpaceHighValue))
2023 table[i][3] = 0x00;
2025 else if(pal->Flags & WINEDDPCAPS_ALPHA)
2027 table[i][3] = pal->palents[i].peFlags;
2029 else
2031 table[i][3] = 0xFF;
2037 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
2038 UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This)
2040 const BYTE *source;
2041 BYTE *dest;
2042 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
2044 switch (convert) {
2045 case NO_CONVERSION:
2047 memcpy(dst, src, pitch * height);
2048 break;
2050 case CONVERT_PALETTED:
2051 case CONVERT_PALETTED_CK:
2053 IWineD3DPaletteImpl* pal = This->palette;
2054 BYTE table[256][4];
2055 unsigned int x, y;
2057 if( pal == NULL) {
2058 /* TODO: If we are a sublevel, try to get the palette from level 0 */
2061 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2063 for (y = 0; y < height; y++)
2065 source = src + pitch * y;
2066 dest = dst + outpitch * y;
2067 /* This is an 1 bpp format, using the width here is fine */
2068 for (x = 0; x < width; x++) {
2069 BYTE color = *source++;
2070 *dest++ = table[color][0];
2071 *dest++ = table[color][1];
2072 *dest++ = table[color][2];
2073 *dest++ = table[color][3];
2077 break;
2079 case CONVERT_CK_565:
2081 /* Converting the 565 format in 5551 packed to emulate color-keying.
2083 Note : in all these conversion, it would be best to average the averaging
2084 pixels to get the color of the pixel that will be color-keyed to
2085 prevent 'color bleeding'. This will be done later on if ever it is
2086 too visible.
2088 Note2: Nvidia documents say that their driver does not support alpha + color keying
2089 on the same surface and disables color keying in such a case
2091 unsigned int x, y;
2092 const WORD *Source;
2093 WORD *Dest;
2095 TRACE("Color keyed 565\n");
2097 for (y = 0; y < height; y++) {
2098 Source = (const WORD *)(src + y * pitch);
2099 Dest = (WORD *) (dst + y * outpitch);
2100 for (x = 0; x < width; x++ ) {
2101 WORD color = *Source++;
2102 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
2103 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2104 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2105 *Dest |= 0x0001;
2107 Dest++;
2111 break;
2113 case CONVERT_CK_5551:
2115 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
2116 unsigned int x, y;
2117 const WORD *Source;
2118 WORD *Dest;
2119 TRACE("Color keyed 5551\n");
2120 for (y = 0; y < height; y++) {
2121 Source = (const WORD *)(src + y * pitch);
2122 Dest = (WORD *) (dst + y * outpitch);
2123 for (x = 0; x < width; x++ ) {
2124 WORD color = *Source++;
2125 *Dest = color;
2126 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2127 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2128 *Dest |= (1 << 15);
2130 else {
2131 *Dest &= ~(1 << 15);
2133 Dest++;
2137 break;
2139 case CONVERT_CK_RGB24:
2141 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
2142 unsigned int x, y;
2143 for (y = 0; y < height; y++)
2145 source = src + pitch * y;
2146 dest = dst + outpitch * y;
2147 for (x = 0; x < width; x++) {
2148 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
2149 DWORD dstcolor = color << 8;
2150 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2151 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2152 dstcolor |= 0xff;
2154 *(DWORD*)dest = dstcolor;
2155 source += 3;
2156 dest += 4;
2160 break;
2162 case CONVERT_RGB32_888:
2164 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
2165 unsigned int x, y;
2166 for (y = 0; y < height; y++)
2168 source = src + pitch * y;
2169 dest = dst + outpitch * y;
2170 for (x = 0; x < width; x++) {
2171 DWORD color = 0xffffff & *(const DWORD*)source;
2172 DWORD dstcolor = color << 8;
2173 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2174 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2175 dstcolor |= 0xff;
2177 *(DWORD*)dest = dstcolor;
2178 source += 4;
2179 dest += 4;
2183 break;
2185 case CONVERT_V8U8:
2187 unsigned int x, y;
2188 const short *Source;
2189 unsigned char *Dest;
2190 for(y = 0; y < height; y++) {
2191 Source = (const short *)(src + y * pitch);
2192 Dest = dst + y * outpitch;
2193 for (x = 0; x < width; x++ ) {
2194 long color = (*Source++);
2195 /* B */ Dest[0] = 0xff;
2196 /* G */ Dest[1] = (color >> 8) + 128; /* V */
2197 /* R */ Dest[2] = (color) + 128; /* U */
2198 Dest += 3;
2201 break;
2204 case CONVERT_V16U16:
2206 unsigned int x, y;
2207 const DWORD *Source;
2208 unsigned short *Dest;
2209 for(y = 0; y < height; y++) {
2210 Source = (const DWORD *)(src + y * pitch);
2211 Dest = (unsigned short *) (dst + y * outpitch);
2212 for (x = 0; x < width; x++ ) {
2213 DWORD color = (*Source++);
2214 /* B */ Dest[0] = 0xffff;
2215 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
2216 /* R */ Dest[2] = (color ) + 32768; /* U */
2217 Dest += 3;
2220 break;
2223 case CONVERT_Q8W8V8U8:
2225 unsigned int x, y;
2226 const DWORD *Source;
2227 unsigned char *Dest;
2228 for(y = 0; y < height; y++) {
2229 Source = (const DWORD *)(src + y * pitch);
2230 Dest = dst + y * outpitch;
2231 for (x = 0; x < width; x++ ) {
2232 long color = (*Source++);
2233 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
2234 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2235 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2236 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
2237 Dest += 4;
2240 break;
2243 case CONVERT_L6V5U5:
2245 unsigned int x, y;
2246 const WORD *Source;
2247 unsigned char *Dest;
2249 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2250 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
2251 * fixed function and shaders without further conversion once the surface is
2252 * loaded
2254 for(y = 0; y < height; y++) {
2255 Source = (const WORD *)(src + y * pitch);
2256 Dest = dst + y * outpitch;
2257 for (x = 0; x < width; x++ ) {
2258 short color = (*Source++);
2259 unsigned char l = ((color >> 10) & 0xfc);
2260 char v = ((color >> 5) & 0x3e);
2261 char u = ((color ) & 0x1f);
2263 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
2264 * and doubles the positive range. Thus shift left only once, gl does the 2nd
2265 * shift. GL reads a signed value and converts it into an unsigned value.
2267 /* M */ Dest[2] = l << 1;
2269 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
2270 * from 5 bit values to 8 bit values.
2272 /* V */ Dest[1] = v << 3;
2273 /* U */ Dest[0] = u << 3;
2274 Dest += 3;
2277 } else {
2278 for(y = 0; y < height; y++) {
2279 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
2280 Source = (const WORD *)(src + y * pitch);
2281 for (x = 0; x < width; x++ ) {
2282 short color = (*Source++);
2283 unsigned char l = ((color >> 10) & 0xfc);
2284 short v = ((color >> 5) & 0x3e);
2285 short u = ((color ) & 0x1f);
2286 short v_conv = v + 16;
2287 short u_conv = u + 16;
2289 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
2290 Dest_s += 1;
2294 break;
2297 case CONVERT_X8L8V8U8:
2299 unsigned int x, y;
2300 const DWORD *Source;
2301 unsigned char *Dest;
2303 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2304 /* This implementation works with the fixed function pipeline and shaders
2305 * without further modification after converting the surface.
2307 for(y = 0; y < height; y++) {
2308 Source = (const DWORD *)(src + y * pitch);
2309 Dest = dst + y * outpitch;
2310 for (x = 0; x < width; x++ ) {
2311 long color = (*Source++);
2312 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
2313 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
2314 /* U */ Dest[0] = (color & 0xff); /* U */
2315 /* I */ Dest[3] = 255; /* X */
2316 Dest += 4;
2319 } else {
2320 /* Doesn't work correctly with the fixed function pipeline, but can work in
2321 * shaders if the shader is adjusted. (There's no use for this format in gl's
2322 * standard fixed function pipeline anyway).
2324 for(y = 0; y < height; y++) {
2325 Source = (const DWORD *)(src + y * pitch);
2326 Dest = dst + y * outpitch;
2327 for (x = 0; x < width; x++ ) {
2328 long color = (*Source++);
2329 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
2330 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2331 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2332 Dest += 4;
2336 break;
2339 case CONVERT_A4L4:
2341 unsigned int x, y;
2342 const unsigned char *Source;
2343 unsigned char *Dest;
2344 for(y = 0; y < height; y++) {
2345 Source = src + y * pitch;
2346 Dest = dst + y * outpitch;
2347 for (x = 0; x < width; x++ ) {
2348 unsigned char color = (*Source++);
2349 /* A */ Dest[1] = (color & 0xf0) << 0;
2350 /* L */ Dest[0] = (color & 0x0f) << 4;
2351 Dest += 2;
2354 break;
2357 case CONVERT_G16R16:
2358 case CONVERT_R16G16F:
2360 unsigned int x, y;
2361 const WORD *Source;
2362 WORD *Dest;
2364 for(y = 0; y < height; y++) {
2365 Source = (const WORD *)(src + y * pitch);
2366 Dest = (WORD *) (dst + y * outpitch);
2367 for (x = 0; x < width; x++ ) {
2368 WORD green = (*Source++);
2369 WORD red = (*Source++);
2370 Dest[0] = green;
2371 Dest[1] = red;
2372 /* Strictly speaking not correct for R16G16F, but it doesn't matter because the
2373 * shader overwrites it anyway
2375 Dest[2] = 0xffff;
2376 Dest += 3;
2379 break;
2382 case CONVERT_R32G32F:
2384 unsigned int x, y;
2385 const float *Source;
2386 float *Dest;
2387 for(y = 0; y < height; y++) {
2388 Source = (const float *)(src + y * pitch);
2389 Dest = (float *) (dst + y * outpitch);
2390 for (x = 0; x < width; x++ ) {
2391 float green = (*Source++);
2392 float red = (*Source++);
2393 Dest[0] = green;
2394 Dest[1] = red;
2395 Dest[2] = 1.0;
2396 Dest += 3;
2399 break;
2402 case CONVERT_D15S1:
2404 unsigned int x, y;
2406 for (y = 0; y < height; ++y)
2408 const WORD *source = (const WORD *)(src + y * pitch);
2409 DWORD *dest = (DWORD *)(dst + y * outpitch);
2411 for (x = 0; x < width; ++x)
2413 /* The depth data is normalized, so needs to be scaled,
2414 * the stencil data isn't. Scale depth data by
2415 * (2^24-1)/(2^15-1) ~~ (2^9 + 2^-6). */
2416 WORD d15 = source[x] >> 1;
2417 DWORD d24 = (d15 << 9) + (d15 >> 6);
2418 dest[x] = (d24 << 8) | (source[x] & 0x1);
2421 break;
2424 case CONVERT_D24X4S4:
2426 unsigned int x, y;
2428 for (y = 0; y < height; ++y)
2430 const DWORD *source = (const DWORD *)(src + y * pitch);
2431 DWORD *dest = (DWORD *)(dst + y * outpitch);
2433 for (x = 0; x < width; ++x)
2435 /* Just need to clear out the X4 part. */
2436 dest[x] = source[x] & ~0xf0;
2439 break;
2442 case CONVERT_D24FS8:
2444 unsigned int x, y;
2446 for (y = 0; y < height; ++y)
2448 const DWORD *source = (const DWORD *)(src + y * pitch);
2449 float *dest_f = (float *)(dst + y * outpitch);
2450 DWORD *dest_s = (DWORD *)(dst + y * outpitch);
2452 for (x = 0; x < width; ++x)
2454 dest_f[x * 2] = float_24_to_32((source[x] & 0xffffff00) >> 8);
2455 dest_s[x * 2 + 1] = source[x] & 0xff;
2458 break;
2461 default:
2462 ERR("Unsupported conversion type %#x.\n", convert);
2464 return WINED3D_OK;
2467 /* This function is used in case of 8bit paletted textures to upload the palette.
2468 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2469 extensions like ATI_fragment_shaders is possible.
2471 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2472 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2473 BYTE table[256][4];
2474 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2476 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2478 /* Try to use the paletted texture extension */
2479 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2481 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2482 ENTER_GL();
2483 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2484 LEAVE_GL();
2486 else
2488 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2489 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2490 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2492 ENTER_GL();
2494 /* Create the fragment program if we don't have it */
2495 if(!device->paletteConversionShader)
2497 const char *fragment_palette_conversion =
2498 "!!ARBfp1.0\n"
2499 "TEMP index;\n"
2500 /* { 255/256, 0.5/255*255/256, 0, 0 } */
2501 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n"
2502 /* The alpha-component contains the palette index */
2503 "TEX index, fragment.texcoord[0], texture[0], 2D;\n"
2504 /* Scale the index by 255/256 and add a bias of '0.5' in order to sample in the middle */
2505 "MAD index.a, index.a, constants.x, constants.y;\n"
2506 /* Use the alpha-component as an index in the palette to get the final color */
2507 "TEX result.color, index.a, texture[1], 1D;\n"
2508 "END";
2510 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2511 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2512 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2513 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), fragment_palette_conversion));
2514 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2517 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2518 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2520 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2521 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2523 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2524 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2525 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2526 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2528 /* Switch back to unit 0 in which the 2D texture will be stored. */
2529 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2531 /* Rebind the texture because it isn't bound anymore */
2532 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2534 LEAVE_GL();
2538 BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2539 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2541 if (This->palette || (This->resource.format_desc->format != WINED3DFMT_P8
2542 && This->resource.format_desc->format != WINED3DFMT_A8P8))
2544 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2545 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2547 return FALSE;
2550 if(This->palette9) {
2551 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2552 return FALSE;
2554 } else {
2555 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2557 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2558 return TRUE;
2561 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2562 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2563 DWORD flag = srgb_mode ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2565 if (!(This->Flags & flag)) {
2566 TRACE("Reloading because surface is dirty\n");
2567 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2568 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2569 /* Reload: vice versa OR */
2570 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2571 /* Also reload: Color key is active AND the color key has changed */
2572 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2573 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2574 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2575 TRACE("Reloading because of color keying\n");
2576 /* To perform the color key conversion we need a sysmem copy of
2577 * the surface. Make sure we have it
2580 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2581 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2582 /* TODO: This is not necessarily needed with hw palettized texture support */
2583 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2584 } else {
2585 TRACE("surface is already in texture\n");
2586 return WINED3D_OK;
2589 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2590 * These resources are not bound by device size or format restrictions. Because of this,
2591 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2592 * However, these resources can always be created, locked, and copied.
2594 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2596 FIXME("(%p) Operation not supported for scratch textures\n",This);
2597 return WINED3DERR_INVALIDCALL;
2600 IWineD3DSurface_LoadLocation(iface, flag, NULL /* no partial locking for textures yet */);
2602 #if 0
2604 static unsigned int gen = 0;
2605 char buffer[4096];
2606 ++gen;
2607 if ((gen % 10) == 0) {
2608 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2609 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2612 * debugging crash code
2613 if (gen == 250) {
2614 void** test = NULL;
2615 *test = 0;
2619 #endif
2621 if (!(This->Flags & SFLAG_DONOTFREE)) {
2622 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2623 This->resource.allocatedMemory = NULL;
2624 This->resource.heapMemory = NULL;
2625 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2628 return WINED3D_OK;
2631 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface, BOOL srgb) {
2632 /* TODO: check for locks */
2633 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2634 IWineD3DBaseTexture *baseTexture = NULL;
2635 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2637 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2638 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2639 TRACE("Passing to container\n");
2640 IWineD3DBaseTexture_BindTexture(baseTexture, srgb);
2641 IWineD3DBaseTexture_Release(baseTexture);
2642 } else {
2643 GLuint *name;
2644 TRACE("(%p) : Binding surface\n", This);
2646 name = srgb ? &This->glDescription.srgbTextureName : &This->glDescription.textureName;
2647 if(!device->isInDraw) {
2648 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2651 ENTER_GL();
2653 if (!This->glDescription.level) {
2654 if (!*name) {
2655 glGenTextures(1, name);
2656 checkGLcall("glGenTextures");
2657 TRACE("Surface %p given name %d\n", This, *name);
2659 glBindTexture(This->glDescription.target, *name);
2660 checkGLcall("glBindTexture");
2661 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2662 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2663 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2664 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2665 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2666 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2667 glTexParameteri(This->glDescription.target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2668 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2669 glTexParameteri(This->glDescription.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2670 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2672 /* This is where we should be reducing the amount of GLMemoryUsed */
2673 } else if (*name) {
2674 /* Mipmap surfaces should have a base texture container */
2675 ERR("Mipmap surface has a glTexture bound to it!\n");
2678 glBindTexture(This->glDescription.target, *name);
2679 checkGLcall("glBindTexture");
2681 LEAVE_GL();
2683 return;
2686 #include <errno.h>
2687 #include <stdio.h>
2688 static HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename)
2690 FILE* f = NULL;
2691 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2692 char *allocatedMemory;
2693 const char *textureRow;
2694 IWineD3DSwapChain *swapChain = NULL;
2695 int width, height, i, y;
2696 GLuint tmpTexture = 0;
2697 DWORD color;
2698 /*FIXME:
2699 Textures may not be stored in ->allocatedgMemory and a GlTexture
2700 so we should lock the surface before saving a snapshot, or at least check that
2702 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2703 by calling GetTexImage and in compressed form by calling
2704 GetCompressedTexImageARB. Queried compressed images can be saved and
2705 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2706 texture images do not need to be processed by the GL and should
2707 significantly improve texture loading performance relative to uncompressed
2708 images. */
2710 /* Setup the width and height to be the internal texture width and height. */
2711 width = This->pow2Width;
2712 height = This->pow2Height;
2713 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2714 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2716 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2717 /* if were not a real texture then read the back buffer into a real texture */
2718 /* we don't want to interfere with the back buffer so read the data into a temporary
2719 * texture and then save the data out of the temporary texture
2721 GLint prevRead;
2722 ENTER_GL();
2723 TRACE("(%p) Reading render target into texture\n", This);
2725 glGenTextures(1, &tmpTexture);
2726 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2728 glTexImage2D(GL_TEXTURE_2D,
2730 GL_RGBA,
2731 width,
2732 height,
2733 0/*border*/,
2734 GL_RGBA,
2735 GL_UNSIGNED_INT_8_8_8_8_REV,
2736 NULL);
2738 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2739 checkGLcall("glGetIntegerv");
2740 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2741 checkGLcall("glReadBuffer");
2742 glCopyTexImage2D(GL_TEXTURE_2D,
2744 GL_RGBA,
2747 width,
2748 height,
2751 checkGLcall("glCopyTexImage2D");
2752 glReadBuffer(prevRead);
2753 LEAVE_GL();
2755 } else { /* bind the real texture, and make sure it up to date */
2756 surface_internal_preload(iface, SRGB_RGB);
2757 surface_bind_and_dirtify(This, FALSE);
2759 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2760 ENTER_GL();
2761 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2762 glGetTexImage(GL_TEXTURE_2D,
2763 This->glDescription.level,
2764 GL_RGBA,
2765 GL_UNSIGNED_INT_8_8_8_8_REV,
2766 allocatedMemory);
2767 checkGLcall("glTexImage2D");
2768 if (tmpTexture) {
2769 glBindTexture(GL_TEXTURE_2D, 0);
2770 glDeleteTextures(1, &tmpTexture);
2772 LEAVE_GL();
2774 f = fopen(filename, "w+");
2775 if (NULL == f) {
2776 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2777 return WINED3DERR_INVALIDCALL;
2779 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2780 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format_desc->format));
2781 /* TGA header */
2782 fputc(0,f);
2783 fputc(0,f);
2784 fputc(2,f);
2785 fputc(0,f);
2786 fputc(0,f);
2787 fputc(0,f);
2788 fputc(0,f);
2789 fputc(0,f);
2790 fputc(0,f);
2791 fputc(0,f);
2792 fputc(0,f);
2793 fputc(0,f);
2794 /* short width*/
2795 fwrite(&width,2,1,f);
2796 /* short height */
2797 fwrite(&height,2,1,f);
2798 /* format rgba */
2799 fputc(0x20,f);
2800 fputc(0x28,f);
2801 /* raw data */
2802 /* 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 */
2803 if(swapChain)
2804 textureRow = allocatedMemory + (width * (height - 1) *4);
2805 else
2806 textureRow = allocatedMemory;
2807 for (y = 0 ; y < height; y++) {
2808 for (i = 0; i < width; i++) {
2809 color = *((const DWORD*)textureRow);
2810 fputc((color >> 16) & 0xFF, f); /* B */
2811 fputc((color >> 8) & 0xFF, f); /* G */
2812 fputc((color >> 0) & 0xFF, f); /* R */
2813 fputc((color >> 24) & 0xFF, f); /* A */
2814 textureRow += 4;
2816 /* take two rows of the pointer to the texture memory */
2817 if(swapChain)
2818 (textureRow-= width << 3);
2821 TRACE("Closing file\n");
2822 fclose(f);
2824 if(swapChain) {
2825 IWineD3DSwapChain_Release(swapChain);
2827 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2828 return WINED3D_OK;
2831 static HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2832 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2833 HRESULT hr;
2835 TRACE("(%p) : Calling base function first\n", This);
2836 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2837 if(SUCCEEDED(hr)) {
2838 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2839 TRACE("(%p) : glFormat %d, glFormatInternal %d, glType %d\n", This, This->resource.format_desc->glFormat,
2840 This->resource.format_desc->glInternal, This->resource.format_desc->glType);
2842 return hr;
2845 static HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2846 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2848 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2849 WARN("Surface is locked or the HDC is in use\n");
2850 return WINED3DERR_INVALIDCALL;
2853 if(Mem && Mem != This->resource.allocatedMemory) {
2854 void *release = NULL;
2856 /* Do I have to copy the old surface content? */
2857 if(This->Flags & SFLAG_DIBSECTION) {
2858 /* Release the DC. No need to hold the critical section for the update
2859 * Thread because this thread runs only on front buffers, but this method
2860 * fails for render targets in the check above.
2862 SelectObject(This->hDC, This->dib.holdbitmap);
2863 DeleteDC(This->hDC);
2864 /* Release the DIB section */
2865 DeleteObject(This->dib.DIBsection);
2866 This->dib.bitmap_data = NULL;
2867 This->resource.allocatedMemory = NULL;
2868 This->hDC = NULL;
2869 This->Flags &= ~SFLAG_DIBSECTION;
2870 } else if(!(This->Flags & SFLAG_USERPTR)) {
2871 release = This->resource.heapMemory;
2872 This->resource.heapMemory = NULL;
2874 This->resource.allocatedMemory = Mem;
2875 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2877 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2878 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2880 /* For client textures opengl has to be notified */
2881 if(This->Flags & SFLAG_CLIENT) {
2882 DWORD oldFlags = This->Flags;
2883 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2884 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2885 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2886 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2889 /* Now free the old memory if any */
2890 HeapFree(GetProcessHeap(), 0, release);
2891 } else if(This->Flags & SFLAG_USERPTR) {
2892 /* LockRect and GetDC will re-create the dib section and allocated memory */
2893 This->resource.allocatedMemory = NULL;
2894 /* HeapMemory should be NULL already */
2895 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2896 This->Flags &= ~SFLAG_USERPTR;
2898 if(This->Flags & SFLAG_CLIENT) {
2899 DWORD oldFlags = This->Flags;
2900 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2901 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2902 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2903 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2906 return WINED3D_OK;
2909 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
2911 /* Flip the surface contents */
2912 /* Flip the DC */
2914 HDC tmp;
2915 tmp = front->hDC;
2916 front->hDC = back->hDC;
2917 back->hDC = tmp;
2920 /* Flip the DIBsection */
2922 HBITMAP tmp;
2923 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
2924 tmp = front->dib.DIBsection;
2925 front->dib.DIBsection = back->dib.DIBsection;
2926 back->dib.DIBsection = tmp;
2928 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
2929 else front->Flags &= ~SFLAG_DIBSECTION;
2930 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
2931 else back->Flags &= ~SFLAG_DIBSECTION;
2934 /* Flip the surface data */
2936 void* tmp;
2938 tmp = front->dib.bitmap_data;
2939 front->dib.bitmap_data = back->dib.bitmap_data;
2940 back->dib.bitmap_data = tmp;
2942 tmp = front->resource.allocatedMemory;
2943 front->resource.allocatedMemory = back->resource.allocatedMemory;
2944 back->resource.allocatedMemory = tmp;
2946 tmp = front->resource.heapMemory;
2947 front->resource.heapMemory = back->resource.heapMemory;
2948 back->resource.heapMemory = tmp;
2951 /* Flip the PBO */
2953 GLuint tmp_pbo = front->pbo;
2954 front->pbo = back->pbo;
2955 back->pbo = tmp_pbo;
2958 /* client_memory should not be different, but just in case */
2960 BOOL tmp;
2961 tmp = front->dib.client_memory;
2962 front->dib.client_memory = back->dib.client_memory;
2963 back->dib.client_memory = tmp;
2966 /* Flip the opengl texture */
2968 glDescriptor tmp_desc = back->glDescription;
2969 back->glDescription = front->glDescription;
2970 front->glDescription = tmp_desc;
2974 DWORD tmp_flags = back->Flags;
2975 back->Flags = front->Flags;
2976 front->Flags = tmp_flags;
2980 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2981 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2982 IWineD3DSwapChainImpl *swapchain = NULL;
2983 HRESULT hr;
2984 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2986 /* Flipping is only supported on RenderTargets and overlays*/
2987 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
2988 WARN("Tried to flip a non-render target, non-overlay surface\n");
2989 return WINEDDERR_NOTFLIPPABLE;
2992 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
2993 flip_surface(This, (IWineD3DSurfaceImpl *) override);
2995 /* Update the overlay if it is visible */
2996 if(This->overlay_dest) {
2997 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
2998 } else {
2999 return WINED3D_OK;
3003 if(override) {
3004 /* DDraw sets this for the X11 surfaces, so don't confuse the user
3005 * FIXME("(%p) Target override is not supported by now\n", This);
3006 * Additionally, it isn't really possible to support triple-buffering
3007 * properly on opengl at all
3011 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
3012 if(!swapchain) {
3013 ERR("Flipped surface is not on a swapchain\n");
3014 return WINEDDERR_NOTFLIPPABLE;
3017 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
3018 * and only d3d8 and d3d9 apps specify the presentation interval
3020 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
3021 /* Most common case first to avoid wasting time on all the other cases */
3022 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
3023 } else if(Flags & WINEDDFLIP_NOVSYNC) {
3024 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3025 } else if(Flags & WINEDDFLIP_INTERVAL2) {
3026 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
3027 } else if(Flags & WINEDDFLIP_INTERVAL3) {
3028 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
3029 } else {
3030 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
3033 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
3034 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
3035 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
3036 return hr;
3039 /* Does a direct frame buffer -> texture copy. Stretching is done
3040 * with single pixel copy calls
3042 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3043 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
3044 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
3046 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3047 float xrel, yrel;
3048 UINT row;
3049 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3052 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
3053 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3054 ENTER_GL();
3056 /* Bind the target texture */
3057 glBindTexture(This->glDescription.target, This->glDescription.textureName);
3058 checkGLcall("glBindTexture");
3059 if(!swapchain) {
3060 TRACE("Reading from an offscreen target\n");
3061 upsidedown = !upsidedown;
3062 glReadBuffer(myDevice->offscreenBuffer);
3063 } else {
3064 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
3065 glReadBuffer(buffer);
3067 checkGLcall("glReadBuffer");
3069 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
3070 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
3072 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
3073 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3075 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
3076 ERR("Texture filtering not supported in direct blit\n");
3078 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
3079 ERR("Texture filtering not supported in direct blit\n");
3082 if(upsidedown &&
3083 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
3084 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
3085 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
3087 glCopyTexSubImage2D(This->glDescription.target,
3088 This->glDescription.level,
3089 drect->x1, drect->y1, /* xoffset, yoffset */
3090 srect->x1, Src->currentDesc.Height - srect->y2,
3091 drect->x2 - drect->x1, drect->y2 - drect->y1);
3092 } else {
3093 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
3094 /* I have to process this row by row to swap the image,
3095 * otherwise it would be upside down, so stretching in y direction
3096 * doesn't cost extra time
3098 * However, stretching in x direction can be avoided if not necessary
3100 for(row = drect->y1; row < drect->y2; row++) {
3101 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
3102 /* Well, that stuff works, but it's very slow.
3103 * find a better way instead
3105 UINT col;
3107 for(col = drect->x1; col < drect->x2; col++) {
3108 glCopyTexSubImage2D(This->glDescription.target,
3109 This->glDescription.level,
3110 drect->x1 + col, row, /* xoffset, yoffset */
3111 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
3112 1, 1);
3114 } else {
3115 glCopyTexSubImage2D(This->glDescription.target,
3116 This->glDescription.level,
3117 drect->x1, row, /* xoffset, yoffset */
3118 srect->x1, yoffset - (int) (row * yrel),
3119 drect->x2-drect->x1, 1);
3123 checkGLcall("glCopyTexSubImage2D");
3125 LEAVE_GL();
3128 /* Uses the hardware to stretch and flip the image */
3129 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3130 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
3131 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
3133 GLuint src, backup = 0;
3134 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3135 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3136 float left, right, top, bottom; /* Texture coordinates */
3137 UINT fbwidth = Src->currentDesc.Width;
3138 UINT fbheight = Src->currentDesc.Height;
3139 GLenum drawBuffer = GL_BACK;
3140 GLenum texture_target;
3141 BOOL noBackBufferBackup;
3143 TRACE("Using hwstretch blit\n");
3144 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3145 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
3146 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3148 noBackBufferBackup = !swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3149 if(!noBackBufferBackup && Src->glDescription.textureName == 0) {
3150 /* Get it a description */
3151 surface_internal_preload(SrcSurface, SRGB_RGB);
3153 ENTER_GL();
3155 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3156 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3158 if(myDevice->activeContext->aux_buffers >= 2) {
3159 /* Got more than one aux buffer? Use the 2nd aux buffer */
3160 drawBuffer = GL_AUX1;
3161 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && myDevice->activeContext->aux_buffers >= 1) {
3162 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3163 drawBuffer = GL_AUX0;
3166 if(noBackBufferBackup) {
3167 glGenTextures(1, &backup);
3168 checkGLcall("glGenTextures\n");
3169 glBindTexture(GL_TEXTURE_2D, backup);
3170 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
3171 texture_target = GL_TEXTURE_2D;
3172 } else {
3173 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3174 * we are reading from the back buffer, the backup can be used as source texture
3176 texture_target = Src->glDescription.target;
3177 glBindTexture(texture_target, Src->glDescription.textureName);
3178 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
3179 glEnable(texture_target);
3180 checkGLcall("glEnable(texture_target)");
3182 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3183 Src->Flags &= ~SFLAG_INTEXTURE;
3186 if(swapchain) {
3187 glReadBuffer(surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain));
3188 } else {
3189 TRACE("Reading from an offscreen target\n");
3190 upsidedown = !upsidedown;
3191 glReadBuffer(myDevice->offscreenBuffer);
3194 /* TODO: Only back up the part that will be overwritten */
3195 glCopyTexSubImage2D(texture_target, 0,
3196 0, 0 /* read offsets */,
3197 0, 0,
3198 fbwidth,
3199 fbheight);
3201 checkGLcall("glCopyTexSubImage2D");
3203 /* No issue with overriding these - the sampler is dirty due to blit usage */
3204 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3205 magLookup[Filter - WINED3DTEXF_NONE]);
3206 checkGLcall("glTexParameteri");
3207 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3208 minMipLookup[Filter].mip[WINED3DTEXF_NONE]);
3209 checkGLcall("glTexParameteri");
3211 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
3212 src = backup ? backup : Src->glDescription.textureName;
3213 } else {
3214 glReadBuffer(GL_FRONT);
3215 checkGLcall("glReadBuffer(GL_FRONT)");
3217 glGenTextures(1, &src);
3218 checkGLcall("glGenTextures(1, &src)");
3219 glBindTexture(GL_TEXTURE_2D, src);
3220 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
3222 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
3223 * out for power of 2 sizes
3225 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
3226 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3227 checkGLcall("glTexImage2D");
3228 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
3229 0, 0 /* read offsets */,
3230 0, 0,
3231 fbwidth,
3232 fbheight);
3234 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3235 checkGLcall("glTexParameteri");
3236 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3237 checkGLcall("glTexParameteri");
3239 glReadBuffer(GL_BACK);
3240 checkGLcall("glReadBuffer(GL_BACK)");
3242 if(texture_target != GL_TEXTURE_2D) {
3243 glDisable(texture_target);
3244 glEnable(GL_TEXTURE_2D);
3245 texture_target = GL_TEXTURE_2D;
3248 checkGLcall("glEnd and previous");
3250 left = srect->x1;
3251 right = srect->x2;
3253 if(upsidedown) {
3254 top = Src->currentDesc.Height - srect->y1;
3255 bottom = Src->currentDesc.Height - srect->y2;
3256 } else {
3257 top = Src->currentDesc.Height - srect->y2;
3258 bottom = Src->currentDesc.Height - srect->y1;
3261 if(Src->Flags & SFLAG_NORMCOORD) {
3262 left /= Src->pow2Width;
3263 right /= Src->pow2Width;
3264 top /= Src->pow2Height;
3265 bottom /= Src->pow2Height;
3268 /* draw the source texture stretched and upside down. The correct surface is bound already */
3269 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3270 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3272 glDrawBuffer(drawBuffer);
3273 glReadBuffer(drawBuffer);
3275 glBegin(GL_QUADS);
3276 /* bottom left */
3277 glTexCoord2f(left, bottom);
3278 glVertex2i(0, fbheight);
3280 /* top left */
3281 glTexCoord2f(left, top);
3282 glVertex2i(0, fbheight - drect->y2 - drect->y1);
3284 /* top right */
3285 glTexCoord2f(right, top);
3286 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
3288 /* bottom right */
3289 glTexCoord2f(right, bottom);
3290 glVertex2i(drect->x2 - drect->x1, fbheight);
3291 glEnd();
3292 checkGLcall("glEnd and previous");
3294 if(texture_target != This->glDescription.target) {
3295 glDisable(texture_target);
3296 glEnable(This->glDescription.target);
3297 texture_target = This->glDescription.target;
3300 /* Now read the stretched and upside down image into the destination texture */
3301 glBindTexture(texture_target, This->glDescription.textureName);
3302 checkGLcall("glBindTexture");
3303 glCopyTexSubImage2D(texture_target,
3305 drect->x1, drect->y1, /* xoffset, yoffset */
3306 0, 0, /* We blitted the image to the origin */
3307 drect->x2 - drect->x1, drect->y2 - drect->y1);
3308 checkGLcall("glCopyTexSubImage2D");
3310 if(drawBuffer == GL_BACK) {
3311 /* Write the back buffer backup back */
3312 if(backup) {
3313 if(texture_target != GL_TEXTURE_2D) {
3314 glDisable(texture_target);
3315 glEnable(GL_TEXTURE_2D);
3316 texture_target = GL_TEXTURE_2D;
3318 glBindTexture(GL_TEXTURE_2D, backup);
3319 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3320 } else {
3321 if(texture_target != Src->glDescription.target) {
3322 glDisable(texture_target);
3323 glEnable(Src->glDescription.target);
3324 texture_target = Src->glDescription.target;
3326 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3327 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
3330 glBegin(GL_QUADS);
3331 /* top left */
3332 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
3333 glVertex2i(0, 0);
3335 /* bottom left */
3336 glTexCoord2f(0.0, 0.0);
3337 glVertex2i(0, fbheight);
3339 /* bottom right */
3340 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
3341 glVertex2i(fbwidth, Src->currentDesc.Height);
3343 /* top right */
3344 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
3345 glVertex2i(fbwidth, 0);
3346 glEnd();
3347 } else {
3348 /* Restore the old draw buffer */
3349 glDrawBuffer(GL_BACK);
3351 glDisable(texture_target);
3352 checkGLcall("glDisable(texture_target)");
3354 /* Cleanup */
3355 if(src != Src->glDescription.textureName && src != backup) {
3356 glDeleteTextures(1, &src);
3357 checkGLcall("glDeleteTextures(1, &src)");
3359 if(backup) {
3360 glDeleteTextures(1, &backup);
3361 checkGLcall("glDeleteTextures(1, &backup)");
3364 LEAVE_GL();
3367 /* Not called from the VTable */
3368 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3369 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx,
3370 WINED3DTEXTUREFILTERTYPE Filter)
3372 WINED3DRECT rect;
3373 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3374 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3375 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3377 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3379 /* Get the swapchain. One of the surfaces has to be a primary surface */
3380 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3381 WARN("Destination is in sysmem, rejecting gl blt\n");
3382 return WINED3DERR_INVALIDCALL;
3384 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3385 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3386 if(Src) {
3387 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3388 WARN("Src is in sysmem, rejecting gl blt\n");
3389 return WINED3DERR_INVALIDCALL;
3391 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3392 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3395 /* Early sort out of cases where no render target is used */
3396 if(!dstSwapchain && !srcSwapchain &&
3397 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3398 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3399 return WINED3DERR_INVALIDCALL;
3402 /* No destination color keying supported */
3403 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3404 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3405 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3406 return WINED3DERR_INVALIDCALL;
3409 if (DestRect) {
3410 rect.x1 = DestRect->left;
3411 rect.y1 = DestRect->top;
3412 rect.x2 = DestRect->right;
3413 rect.y2 = DestRect->bottom;
3414 } else {
3415 rect.x1 = 0;
3416 rect.y1 = 0;
3417 rect.x2 = This->currentDesc.Width;
3418 rect.y2 = This->currentDesc.Height;
3421 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3422 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3423 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3424 /* Half-life does a Blt from the back buffer to the front buffer,
3425 * Full surface size, no flags... Use present instead
3427 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3430 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3431 while(1)
3433 RECT mySrcRect;
3434 TRACE("Looking if a Present can be done...\n");
3435 /* Source Rectangle must be full surface */
3436 if( SrcRect ) {
3437 if(SrcRect->left != 0 || SrcRect->top != 0 ||
3438 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
3439 TRACE("No, Source rectangle doesn't match\n");
3440 break;
3443 mySrcRect.left = 0;
3444 mySrcRect.top = 0;
3445 mySrcRect.right = Src->currentDesc.Width;
3446 mySrcRect.bottom = Src->currentDesc.Height;
3448 /* No stretching may occur */
3449 if(mySrcRect.right != rect.x2 - rect.x1 ||
3450 mySrcRect.bottom != rect.y2 - rect.y1) {
3451 TRACE("No, stretching is done\n");
3452 break;
3455 /* Destination must be full surface or match the clipping rectangle */
3456 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3458 RECT cliprect;
3459 POINT pos[2];
3460 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3461 pos[0].x = rect.x1;
3462 pos[0].y = rect.y1;
3463 pos[1].x = rect.x2;
3464 pos[1].y = rect.y2;
3465 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3466 pos, 2);
3468 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3469 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3471 TRACE("No, dest rectangle doesn't match(clipper)\n");
3472 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3473 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3474 break;
3477 else
3479 if(rect.x1 != 0 || rect.y1 != 0 ||
3480 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3481 TRACE("No, dest rectangle doesn't match(surface size)\n");
3482 break;
3486 TRACE("Yes\n");
3488 /* These flags are unimportant for the flag check, remove them */
3489 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3490 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3492 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3493 * take very long, while a flip is fast.
3494 * This applies to Half-Life, which does such Blts every time it finished
3495 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3496 * menu. This is also used by all apps when they do windowed rendering
3498 * The problem is that flipping is not really the same as copying. After a
3499 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3500 * untouched. Therefore it's necessary to override the swap effect
3501 * and to set it back after the flip.
3503 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3504 * testcases.
3507 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3508 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3510 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3511 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3513 dstSwapchain->presentParms.SwapEffect = orig_swap;
3515 return WINED3D_OK;
3517 break;
3520 TRACE("Unsupported blit between buffers on the same swapchain\n");
3521 return WINED3DERR_INVALIDCALL;
3522 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3523 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3524 return WINED3DERR_INVALIDCALL;
3525 } else if(dstSwapchain && srcSwapchain) {
3526 FIXME("Implement hardware blit between two different swapchains\n");
3527 return WINED3DERR_INVALIDCALL;
3528 } else if(dstSwapchain) {
3529 if(SrcSurface == myDevice->render_targets[0]) {
3530 TRACE("Blit from active render target to a swapchain\n");
3531 /* Handled with regular texture -> swapchain blit */
3533 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3534 FIXME("Implement blit from a swapchain to the active render target\n");
3535 return WINED3DERR_INVALIDCALL;
3538 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3539 /* Blit from render target to texture */
3540 WINED3DRECT srect;
3541 BOOL upsideDown, stretchx;
3542 BOOL paletteOverride = FALSE;
3544 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3545 TRACE("Color keying not supported by frame buffer to texture blit\n");
3546 return WINED3DERR_INVALIDCALL;
3547 /* Destination color key is checked above */
3550 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3551 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3553 if(SrcRect) {
3554 if(SrcRect->top < SrcRect->bottom) {
3555 srect.y1 = SrcRect->top;
3556 srect.y2 = SrcRect->bottom;
3557 upsideDown = FALSE;
3558 } else {
3559 srect.y1 = SrcRect->bottom;
3560 srect.y2 = SrcRect->top;
3561 upsideDown = TRUE;
3563 srect.x1 = SrcRect->left;
3564 srect.x2 = SrcRect->right;
3565 } else {
3566 srect.x1 = 0;
3567 srect.y1 = 0;
3568 srect.x2 = Src->currentDesc.Width;
3569 srect.y2 = Src->currentDesc.Height;
3570 upsideDown = FALSE;
3572 if(rect.x1 > rect.x2) {
3573 UINT tmp = rect.x2;
3574 rect.x2 = rect.x1;
3575 rect.x1 = tmp;
3576 upsideDown = !upsideDown;
3579 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3580 stretchx = TRUE;
3581 } else {
3582 stretchx = FALSE;
3585 /* When blitting from a render target a texture, the texture isn't required to have a palette.
3586 * In this case grab the palette from the render target. */
3587 if ((This->resource.format_desc->format == WINED3DFMT_P8) && (This->palette == NULL))
3589 paletteOverride = TRUE;
3590 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3591 This->palette = Src->palette;
3594 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3595 * flip the image nor scale it.
3597 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3598 * -> If the app wants a image width an unscaled width, copy it line per line
3599 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3600 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3601 * back buffer. This is slower than reading line per line, thus not used for flipping
3602 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3603 * pixel by pixel
3605 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3606 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3607 * backends.
3609 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)
3610 && surface_can_stretch_rect(Src, This))
3612 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3613 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3614 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3615 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3616 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3617 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3618 } else {
3619 TRACE("Using hardware stretching to flip / stretch the texture\n");
3620 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3623 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3624 if(paletteOverride)
3625 This->palette = NULL;
3627 if(!(This->Flags & SFLAG_DONOTFREE)) {
3628 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3629 This->resource.allocatedMemory = NULL;
3630 This->resource.heapMemory = NULL;
3631 } else {
3632 This->Flags &= ~SFLAG_INSYSMEM;
3634 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3635 * path is never entered
3637 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3639 return WINED3D_OK;
3640 } else if(Src) {
3641 /* Blit from offscreen surface to render target */
3642 float glTexCoord[4];
3643 DWORD oldCKeyFlags = Src->CKeyFlags;
3644 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3645 RECT SourceRectangle;
3646 BOOL paletteOverride = FALSE;
3648 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3650 if(SrcRect) {
3651 SourceRectangle.left = SrcRect->left;
3652 SourceRectangle.right = SrcRect->right;
3653 SourceRectangle.top = SrcRect->top;
3654 SourceRectangle.bottom = SrcRect->bottom;
3655 } else {
3656 SourceRectangle.left = 0;
3657 SourceRectangle.right = Src->currentDesc.Width;
3658 SourceRectangle.top = 0;
3659 SourceRectangle.bottom = Src->currentDesc.Height;
3662 /* When blitting from an offscreen surface to a rendertarget, the source
3663 * surface is not required to have a palette. Our rendering / conversion
3664 * code further down the road retrieves the palette from the surface, so
3665 * it must have a palette set. */
3666 if ((Src->resource.format_desc->format == WINED3DFMT_P8) && (Src->palette == NULL))
3668 paletteOverride = TRUE;
3669 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3670 Src->palette = This->palette;
3673 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)
3674 && !(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3675 && surface_can_stretch_rect(Src, This))
3677 TRACE("Using stretch_rect_fbo\n");
3678 /* The source is always a texture, but never the currently active render target, and the texture
3679 * contents are never upside down
3681 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3682 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3684 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3685 if(paletteOverride)
3686 Src->palette = NULL;
3687 return WINED3D_OK;
3690 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3691 /* Fall back to software */
3692 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3693 SourceRectangle.left, SourceRectangle.top,
3694 SourceRectangle.right, SourceRectangle.bottom);
3695 return WINED3DERR_INVALIDCALL;
3698 /* Color keying: Check if we have to do a color keyed blt,
3699 * and if not check if a color key is activated.
3701 * Just modify the color keying parameters in the surface and restore them afterwards
3702 * The surface keeps track of the color key last used to load the opengl surface.
3703 * PreLoad will catch the change to the flags and color key and reload if necessary.
3705 if(Flags & WINEDDBLT_KEYSRC) {
3706 /* Use color key from surface */
3707 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3708 /* Use color key from DDBltFx */
3709 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3710 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3711 } else {
3712 /* Do not use color key */
3713 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3716 /* Now load the surface */
3717 surface_internal_preload((IWineD3DSurface *) Src, SRGB_RGB);
3719 /* Activate the destination context, set it up for blitting */
3720 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3722 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3723 * while OpenGL coordinates are window relative.
3724 * Also beware of the origin difference(top left vs bottom left).
3725 * Also beware that the front buffer's surface size is screen width x screen height,
3726 * whereas the real gl drawable size is the size of the window.
3728 if (dstSwapchain && (IWineD3DSurface *)This == dstSwapchain->frontBuffer) {
3729 RECT windowsize;
3730 POINT offset = {0,0};
3731 UINT h;
3732 ClientToScreen(dstSwapchain->win_handle, &offset);
3733 GetClientRect(dstSwapchain->win_handle, &windowsize);
3734 h = windowsize.bottom - windowsize.top;
3735 rect.x1 -= offset.x; rect.x2 -=offset.x;
3736 rect.y1 -= offset.y; rect.y2 -=offset.y;
3737 rect.y1 += This->currentDesc.Height - h; rect.y2 += This->currentDesc.Height - h;
3740 if (!is_identity_fixup(This->resource.format_desc->color_fixup))
3742 FIXME("Destination format %s has a fixup, this is not supported.\n",
3743 debug_d3dformat(This->resource.format_desc->format));
3744 dump_color_fixup_desc(This->resource.format_desc->color_fixup);
3747 if (!myDevice->blitter->color_fixup_supported(Src->resource.format_desc->color_fixup))
3749 FIXME("Source format %s has an unsupported fixup:\n",
3750 debug_d3dformat(Src->resource.format_desc->format));
3751 dump_color_fixup_desc(Src->resource.format_desc->color_fixup);
3754 myDevice->blitter->set_shader((IWineD3DDevice *) myDevice, Src->resource.format_desc,
3755 Src->glDescription.target, Src->pow2Width, Src->pow2Height);
3757 ENTER_GL();
3759 /* Bind the texture */
3760 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3761 checkGLcall("glBindTexture");
3763 /* Filtering for StretchRect */
3764 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3765 magLookup[Filter - WINED3DTEXF_NONE]);
3766 checkGLcall("glTexParameteri");
3767 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3768 minMipLookup[Filter].mip[WINED3DTEXF_NONE]);
3769 checkGLcall("glTexParameteri");
3770 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3771 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3772 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3773 checkGLcall("glTexEnvi");
3775 /* This is for color keying */
3776 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3777 glEnable(GL_ALPHA_TEST);
3778 checkGLcall("glEnable GL_ALPHA_TEST");
3780 /* When the primary render target uses P8, the alpha component contains the palette index.
3781 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3782 * should be masked away have alpha set to 0. */
3783 if(primary_render_target_is_p8(myDevice))
3784 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0);
3785 else
3786 glAlphaFunc(GL_NOTEQUAL, 0.0);
3787 checkGLcall("glAlphaFunc\n");
3788 } else {
3789 glDisable(GL_ALPHA_TEST);
3790 checkGLcall("glDisable GL_ALPHA_TEST");
3793 /* Draw a textured quad
3795 glBegin(GL_QUADS);
3797 glColor3d(1.0f, 1.0f, 1.0f);
3798 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3799 glVertex3f(rect.x1,
3800 rect.y1,
3801 0.0);
3803 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3804 glVertex3f(rect.x1, rect.y2, 0.0);
3806 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3807 glVertex3f(rect.x2,
3808 rect.y2,
3809 0.0);
3811 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3812 glVertex3f(rect.x2,
3813 rect.y1,
3814 0.0);
3815 glEnd();
3816 checkGLcall("glEnd");
3818 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3819 glDisable(GL_ALPHA_TEST);
3820 checkGLcall("glDisable(GL_ALPHA_TEST)");
3823 glBindTexture(Src->glDescription.target, 0);
3824 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3826 /* Restore the color key parameters */
3827 Src->CKeyFlags = oldCKeyFlags;
3828 Src->SrcBltCKey = oldBltCKey;
3830 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3831 if(paletteOverride)
3832 Src->palette = NULL;
3834 LEAVE_GL();
3836 /* Leave the opengl state valid for blitting */
3837 myDevice->blitter->unset_shader((IWineD3DDevice *) myDevice);
3839 /* Flush in case the drawable is used by multiple GL contexts */
3840 if(dstSwapchain && (This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer || dstSwapchain->num_contexts >= 2))
3841 glFlush();
3843 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3844 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3845 * is outdated now
3847 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3849 return WINED3D_OK;
3850 } else {
3851 /* Source-Less Blit to render target */
3852 if (Flags & WINEDDBLT_COLORFILL) {
3853 /* This is easy to handle for the D3D Device... */
3854 DWORD color;
3856 TRACE("Colorfill\n");
3858 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3859 must be true if we are here */
3860 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3861 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3862 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3863 TRACE("Surface is higher back buffer, falling back to software\n");
3864 return WINED3DERR_INVALIDCALL;
3867 /* The color as given in the Blt function is in the format of the frame-buffer...
3868 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3870 if (This->resource.format_desc->format == WINED3DFMT_P8)
3872 DWORD alpha;
3874 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3875 else alpha = 0xFF000000;
3877 if (This->palette) {
3878 color = (alpha |
3879 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3880 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3881 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3882 } else {
3883 color = alpha;
3886 else if (This->resource.format_desc->format == WINED3DFMT_R5G6B5)
3888 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3889 color = 0xFFFFFFFF;
3890 } else {
3891 color = ((0xFF000000) |
3892 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3893 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3894 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3897 else if ((This->resource.format_desc->format == WINED3DFMT_R8G8B8)
3898 || (This->resource.format_desc->format == WINED3DFMT_X8R8G8B8))
3900 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3902 else if (This->resource.format_desc->format == WINED3DFMT_A8R8G8B8)
3904 color = DDBltFx->u5.dwFillColor;
3906 else {
3907 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3908 return WINED3DERR_INVALIDCALL;
3911 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3912 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3913 1, /* Number of rectangles */
3914 &rect, WINED3DCLEAR_TARGET, color,
3915 0.0 /* Z */,
3916 0 /* Stencil */);
3917 return WINED3D_OK;
3921 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3922 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3923 return WINED3DERR_INVALIDCALL;
3926 static HRESULT IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3927 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx)
3929 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3930 float depth;
3932 if (Flags & WINEDDBLT_DEPTHFILL) {
3933 switch(This->resource.format_desc->format)
3935 case WINED3DFMT_D16_UNORM:
3936 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3937 break;
3938 case WINED3DFMT_D15S1:
3939 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3940 break;
3941 case WINED3DFMT_D24S8:
3942 case WINED3DFMT_D24X8:
3943 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3944 break;
3945 case WINED3DFMT_D32:
3946 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3947 break;
3948 default:
3949 depth = 0.0;
3950 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format_desc->format));
3953 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3954 DestRect == NULL ? 0 : 1,
3955 (const WINED3DRECT *)DestRect,
3956 WINED3DCLEAR_ZBUFFER,
3957 0x00000000,
3958 depth,
3959 0x00000000);
3962 FIXME("(%p): Unsupp depthstencil blit\n", This);
3963 return WINED3DERR_INVALIDCALL;
3966 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
3967 const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3968 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3969 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3970 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3971 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3972 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3974 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3976 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3977 return WINEDDERR_SURFACEBUSY;
3980 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3981 * except depth blits, which seem to work
3983 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3984 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3985 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3986 return WINED3DERR_INVALIDCALL;
3987 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3988 TRACE("Z Blit override handled the blit\n");
3989 return WINED3D_OK;
3993 /* Special cases for RenderTargets */
3994 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3995 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3996 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3999 /* For the rest call the X11 surface implementation.
4000 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
4001 * other Blts are rather rare
4003 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
4006 static HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
4007 IWineD3DSurface *Source, const RECT *rsrc, DWORD trans)
4009 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4010 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
4011 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
4012 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
4014 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
4016 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
4017 return WINEDDERR_SURFACEBUSY;
4020 if(myDevice->inScene &&
4021 (iface == myDevice->stencilBufferTarget ||
4022 (Source && Source == myDevice->stencilBufferTarget))) {
4023 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
4024 return WINED3DERR_INVALIDCALL;
4027 /* Special cases for RenderTargets */
4028 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
4029 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
4031 RECT SrcRect, DstRect;
4032 DWORD Flags=0;
4034 if(rsrc) {
4035 SrcRect.left = rsrc->left;
4036 SrcRect.top= rsrc->top;
4037 SrcRect.bottom = rsrc->bottom;
4038 SrcRect.right = rsrc->right;
4039 } else {
4040 SrcRect.left = 0;
4041 SrcRect.top = 0;
4042 SrcRect.right = srcImpl->currentDesc.Width;
4043 SrcRect.bottom = srcImpl->currentDesc.Height;
4046 DstRect.left = dstx;
4047 DstRect.top=dsty;
4048 DstRect.right = dstx + SrcRect.right - SrcRect.left;
4049 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
4051 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
4052 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
4053 Flags |= WINEDDBLT_KEYSRC;
4054 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
4055 Flags |= WINEDDBLT_KEYDEST;
4056 if(trans & WINEDDBLTFAST_WAIT)
4057 Flags |= WINEDDBLT_WAIT;
4058 if(trans & WINEDDBLTFAST_DONOTWAIT)
4059 Flags |= WINEDDBLT_DONOTWAIT;
4061 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
4065 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
4068 static HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface)
4070 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4071 RGBQUAD col[256];
4072 IWineD3DPaletteImpl *pal = This->palette;
4073 unsigned int n;
4074 TRACE("(%p)\n", This);
4076 if (!pal) return WINED3D_OK;
4078 if (This->resource.format_desc->format == WINED3DFMT_P8
4079 || This->resource.format_desc->format == WINED3DFMT_A8P8)
4081 int bpp;
4082 GLenum format, internal, type;
4083 CONVERT_TYPES convert;
4085 /* Check if we are using a RTL mode which uses texturing for uploads */
4086 BOOL use_texture = (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX);
4088 /* Check if we have hardware palette conversion if we have convert is set to NO_CONVERSION */
4089 d3dfmt_get_conv(This, TRUE, use_texture, &format, &internal, &type, &convert, &bpp, FALSE);
4091 if((This->resource.usage & WINED3DUSAGE_RENDERTARGET) && (convert == NO_CONVERSION))
4093 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
4094 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
4096 /* We want to force a palette refresh, so mark the drawable as not being up to date */
4097 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
4099 /* Re-upload the palette */
4100 d3dfmt_p8_upload_palette(iface, convert);
4101 } else {
4102 if(!(This->Flags & SFLAG_INSYSMEM)) {
4103 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
4104 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
4106 TRACE("Dirtifying surface\n");
4107 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
4111 if(This->Flags & SFLAG_DIBSECTION) {
4112 TRACE("(%p): Updating the hdc's palette\n", This);
4113 for (n=0; n<256; n++) {
4114 col[n].rgbRed = pal->palents[n].peRed;
4115 col[n].rgbGreen = pal->palents[n].peGreen;
4116 col[n].rgbBlue = pal->palents[n].peBlue;
4117 col[n].rgbReserved = 0;
4119 SetDIBColorTable(This->hDC, 0, 256, col);
4122 /* Propagate the changes to the drawable when we have a palette. */
4123 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
4124 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
4126 return WINED3D_OK;
4129 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
4130 /** Check against the maximum texture sizes supported by the video card **/
4131 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4132 unsigned int pow2Width, pow2Height;
4134 This->glDescription.textureName = 0;
4135 This->glDescription.target = GL_TEXTURE_2D;
4137 /* Non-power2 support */
4138 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO) || GL_SUPPORT(WINE_NORMALIZED_TEXRECT)) {
4139 pow2Width = This->currentDesc.Width;
4140 pow2Height = This->currentDesc.Height;
4141 } else {
4142 /* Find the nearest pow2 match */
4143 pow2Width = pow2Height = 1;
4144 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
4145 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
4147 This->pow2Width = pow2Width;
4148 This->pow2Height = pow2Height;
4150 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
4151 /** TODO: add support for non power two compressed textures **/
4152 if (This->resource.format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
4154 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
4155 This, This->currentDesc.Width, This->currentDesc.Height);
4156 return WINED3DERR_NOTAVAILABLE;
4160 if(pow2Width != This->currentDesc.Width ||
4161 pow2Height != This->currentDesc.Height) {
4162 This->Flags |= SFLAG_NONPOW2;
4165 TRACE("%p\n", This);
4166 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
4167 /* one of three options
4168 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)
4169 2: Set the texture to the maximum size (bad idea)
4170 3: WARN and return WINED3DERR_NOTAVAILABLE;
4171 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.
4173 WARN("(%p) Creating an oversized surface: %ux%u (texture is %ux%u)\n",
4174 This, This->pow2Width, This->pow2Height, This->currentDesc.Width, This->currentDesc.Height);
4175 This->Flags |= SFLAG_OVERSIZE;
4177 /* This will be initialized on the first blt */
4178 This->glRect.left = 0;
4179 This->glRect.top = 0;
4180 This->glRect.right = 0;
4181 This->glRect.bottom = 0;
4182 } else {
4183 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
4184 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
4185 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
4186 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
4188 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE)
4189 && !((This->resource.format_desc->format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE)
4190 && (wined3d_settings.rendertargetlock_mode == RTL_READTEX
4191 || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
4193 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
4194 This->pow2Width = This->currentDesc.Width;
4195 This->pow2Height = This->currentDesc.Height;
4196 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
4199 /* No oversize, gl rect is the full texture size */
4200 This->Flags &= ~SFLAG_OVERSIZE;
4201 This->glRect.left = 0;
4202 This->glRect.top = 0;
4203 This->glRect.right = This->pow2Width;
4204 This->glRect.bottom = This->pow2Height;
4207 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4208 switch(wined3d_settings.offscreen_rendering_mode) {
4209 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4210 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4211 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4215 This->Flags |= SFLAG_INSYSMEM;
4217 return WINED3D_OK;
4220 struct depth_blt_info
4222 GLenum binding;
4223 GLenum bind_target;
4224 enum tex_types tex_type;
4225 GLfloat coords[4][3];
4228 static void surface_get_depth_blt_info(GLenum target, GLsizei w, GLsizei h, struct depth_blt_info *info)
4230 GLfloat (*coords)[3] = info->coords;
4232 switch (target)
4234 default:
4235 FIXME("Unsupported texture target %#x\n", target);
4236 /* Fall back to GL_TEXTURE_2D */
4237 case GL_TEXTURE_2D:
4238 info->binding = GL_TEXTURE_BINDING_2D;
4239 info->bind_target = GL_TEXTURE_2D;
4240 info->tex_type = tex_2d;
4241 coords[0][0] = 0.0f; coords[0][1] = 1.0f; coords[0][2] = 0.0f;
4242 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 0.0f;
4243 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4244 coords[3][0] = 1.0f; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4245 break;
4247 case GL_TEXTURE_RECTANGLE_ARB:
4248 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
4249 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
4250 info->tex_type = tex_rect;
4251 coords[0][0] = 0.0f; coords[0][1] = h; coords[0][2] = 0.0f;
4252 coords[1][0] = w; coords[1][1] = h; coords[1][2] = 0.0f;
4253 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4254 coords[3][0] = w; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4255 break;
4257 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4258 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4259 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4260 info->tex_type = tex_cube;
4261 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4262 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4263 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4264 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4266 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4267 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4268 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4269 info->tex_type = tex_cube;
4270 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4271 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4272 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4273 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4275 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4276 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4277 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4278 info->tex_type = tex_cube;
4279 coords[0][0] = -1.0f; coords[0][1] = 1.0f; coords[0][2] = 1.0f;
4280 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 1.0f;
4281 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4282 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4284 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4285 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4286 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4287 info->tex_type = tex_cube;
4288 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4289 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4290 coords[2][0] = -1.0f; coords[2][1] = -1.0f; coords[2][2] = 1.0f;
4291 coords[3][0] = 1.0f; coords[3][1] = -1.0f; coords[3][2] = 1.0f;
4293 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4294 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4295 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4296 info->tex_type = tex_cube;
4297 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4298 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4299 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4300 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4302 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4303 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4304 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4305 info->tex_type = tex_cube;
4306 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4307 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4308 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4309 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4313 /* GL locking is done by the caller */
4314 static void surface_depth_blt(IWineD3DSurfaceImpl *This, GLuint texture, GLsizei w, GLsizei h, GLenum target)
4316 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4317 struct depth_blt_info info;
4318 GLint old_binding = 0;
4320 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4322 glDisable(GL_CULL_FACE);
4323 glEnable(GL_BLEND);
4324 glDisable(GL_ALPHA_TEST);
4325 glDisable(GL_SCISSOR_TEST);
4326 glDisable(GL_STENCIL_TEST);
4327 glEnable(GL_DEPTH_TEST);
4328 glDepthFunc(GL_ALWAYS);
4329 glDepthMask(GL_TRUE);
4330 glBlendFunc(GL_ZERO, GL_ONE);
4331 glViewport(0, 0, w, h);
4333 surface_get_depth_blt_info(target, w, h, &info);
4334 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4335 glGetIntegerv(info.binding, &old_binding);
4336 glBindTexture(info.bind_target, texture);
4338 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4340 glBegin(GL_TRIANGLE_STRIP);
4341 glTexCoord3fv(info.coords[0]);
4342 glVertex2f(-1.0f, -1.0f);
4343 glTexCoord3fv(info.coords[1]);
4344 glVertex2f(1.0f, -1.0f);
4345 glTexCoord3fv(info.coords[2]);
4346 glVertex2f(-1.0f, 1.0f);
4347 glTexCoord3fv(info.coords[3]);
4348 glVertex2f(1.0f, 1.0f);
4349 glEnd();
4351 glBindTexture(info.bind_target, old_binding);
4353 glPopAttrib();
4355 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4358 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
4359 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4361 TRACE("(%p) New location %#x\n", This, location);
4363 if (location & ~SFLAG_DS_LOCATIONS) {
4364 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
4367 This->Flags &= ~SFLAG_DS_LOCATIONS;
4368 This->Flags |= location;
4371 void surface_load_ds_location(IWineD3DSurface *iface, DWORD location) {
4372 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4373 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4375 TRACE("(%p) New location %#x\n", This, location);
4377 /* TODO: Make this work for modes other than FBO */
4378 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4380 if (This->Flags & location) {
4381 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
4382 return;
4385 if (This->current_renderbuffer) {
4386 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
4387 return;
4390 if (location == SFLAG_DS_OFFSCREEN) {
4391 if (This->Flags & SFLAG_DS_ONSCREEN) {
4392 GLint old_binding = 0;
4393 GLenum bind_target;
4395 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
4397 ENTER_GL();
4399 if (!device->depth_blt_texture) {
4400 glGenTextures(1, &device->depth_blt_texture);
4403 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4404 * directly on the FBO texture. That's because we need to flip. */
4405 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4406 if (This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
4407 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4408 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4409 } else {
4410 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4411 bind_target = GL_TEXTURE_2D;
4413 glBindTexture(bind_target, device->depth_blt_texture);
4414 glCopyTexImage2D(bind_target,
4415 This->glDescription.level,
4416 This->resource.format_desc->glInternal,
4419 This->currentDesc.Width,
4420 This->currentDesc.Height,
4422 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4423 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4424 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4425 glBindTexture(bind_target, old_binding);
4427 /* Setup the destination */
4428 if (!device->depth_blt_rb) {
4429 GL_EXTCALL(glGenRenderbuffersEXT(1, &device->depth_blt_rb));
4430 checkGLcall("glGenRenderbuffersEXT");
4432 if (device->depth_blt_rb_w != This->currentDesc.Width
4433 || device->depth_blt_rb_h != This->currentDesc.Height) {
4434 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4435 checkGLcall("glBindRenderbufferEXT");
4436 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, This->currentDesc.Width, This->currentDesc.Height));
4437 checkGLcall("glRenderbufferStorageEXT");
4438 device->depth_blt_rb_w = This->currentDesc.Width;
4439 device->depth_blt_rb_h = This->currentDesc.Height;
4442 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->dst_fbo);
4443 GL_EXTCALL(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4444 checkGLcall("glFramebufferRenderbufferEXT");
4445 context_attach_depth_stencil_fbo(device, GL_FRAMEBUFFER_EXT, iface, FALSE);
4447 /* Do the actual blit */
4448 surface_depth_blt(This, device->depth_blt_texture, This->currentDesc.Width, This->currentDesc.Height, bind_target);
4449 checkGLcall("depth_blt");
4451 if (device->activeContext->current_fbo) {
4452 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->current_fbo->id);
4453 } else {
4454 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4455 checkGLcall("glBindFramebuffer()");
4458 LEAVE_GL();
4459 } else {
4460 FIXME("No up to date depth stencil location\n");
4462 } else if (location == SFLAG_DS_ONSCREEN) {
4463 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4464 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4466 ENTER_GL();
4468 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4469 checkGLcall("glBindFramebuffer()");
4470 surface_depth_blt(This, This->glDescription.textureName, This->currentDesc.Width, This->currentDesc.Height, This->glDescription.target);
4471 checkGLcall("depth_blt");
4473 if (device->activeContext->current_fbo) {
4474 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, device->activeContext->current_fbo->id));
4475 checkGLcall("glBindFramebuffer()");
4478 LEAVE_GL();
4479 } else {
4480 FIXME("No up to date depth stencil location\n");
4482 } else {
4483 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4486 This->Flags |= location;
4489 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4490 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4491 IWineD3DBaseTexture *texture;
4492 IWineD3DSurfaceImpl *overlay;
4494 TRACE("(%p)->(%s, %s)\n", iface, debug_surflocation(flag),
4495 persistent ? "TRUE" : "FALSE");
4497 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4498 if (This->Flags & SFLAG_SWAPCHAIN)
4500 TRACE("Surface %p is an onscreen surface\n", iface);
4501 } else {
4502 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4503 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4507 if(persistent) {
4508 if(((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) ||
4509 ((This->Flags & SFLAG_INSRGBTEX) && !(flag & SFLAG_INSRGBTEX))) {
4510 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4511 TRACE("Passing to container\n");
4512 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4513 IWineD3DBaseTexture_Release(texture);
4516 This->Flags &= ~SFLAG_LOCATIONS;
4517 This->Flags |= flag;
4519 /* Redraw emulated overlays, if any */
4520 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4521 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4522 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4525 } else {
4526 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))) {
4527 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4528 TRACE("Passing to container\n");
4529 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4530 IWineD3DBaseTexture_Release(texture);
4533 This->Flags &= ~flag;
4536 if(!(This->Flags & SFLAG_LOCATIONS)) {
4537 ERR("%p: Surface does not have any up to date location\n", This);
4541 struct coords {
4542 GLfloat x, y, z;
4545 struct float_rect
4547 float l;
4548 float t;
4549 float r;
4550 float b;
4553 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
4555 f->l = ((r->left * 2.0f) / w) - 1.0f;
4556 f->t = ((r->top * 2.0f) / h) - 1.0f;
4557 f->r = ((r->right * 2.0f) / w) - 1.0f;
4558 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
4561 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
4562 struct coords coords[4];
4563 RECT rect;
4564 IWineD3DSwapChain *swapchain;
4565 IWineD3DBaseTexture *texture;
4566 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4567 GLenum bind_target;
4568 struct float_rect f;
4570 if(rect_in) {
4571 rect = *rect_in;
4572 } else {
4573 rect.left = 0;
4574 rect.top = 0;
4575 rect.right = This->currentDesc.Width;
4576 rect.bottom = This->currentDesc.Height;
4579 switch(This->glDescription.target)
4581 case GL_TEXTURE_2D:
4582 bind_target = GL_TEXTURE_2D;
4584 coords[0].x = (float)rect.left / This->pow2Width;
4585 coords[0].y = (float)rect.top / This->pow2Height;
4586 coords[0].z = 0;
4588 coords[1].x = (float)rect.left / This->pow2Width;
4589 coords[1].y = (float)rect.bottom / This->pow2Height;
4590 coords[1].z = 0;
4592 coords[2].x = (float)rect.right / This->pow2Width;
4593 coords[2].y = (float)rect.bottom / This->pow2Height;
4594 coords[2].z = 0;
4596 coords[3].x = (float)rect.right / This->pow2Width;
4597 coords[3].y = (float)rect.top / This->pow2Height;
4598 coords[3].z = 0;
4599 break;
4601 case GL_TEXTURE_RECTANGLE_ARB:
4602 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4603 coords[0].x = rect.left; coords[0].y = rect.top; coords[0].z = 0;
4604 coords[1].x = rect.left; coords[1].y = rect.bottom; coords[1].z = 0;
4605 coords[2].x = rect.right; coords[2].y = rect.bottom; coords[2].z = 0;
4606 coords[3].x = rect.right; coords[3].y = rect.top; coords[3].z = 0;
4607 break;
4609 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4610 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4611 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4612 coords[0].x = 1; coords[0].y = -f.t; coords[0].z = -f.l;
4613 coords[1].x = 1; coords[1].y = -f.b; coords[1].z = -f.l;
4614 coords[2].x = 1; coords[2].y = -f.b; coords[2].z = -f.r;
4615 coords[3].x = 1; coords[3].y = -f.t; coords[3].z = -f.r;
4616 break;
4618 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4619 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4620 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4621 coords[0].x = -1; coords[0].y = -f.t; coords[0].z = f.l;
4622 coords[1].x = -1; coords[1].y = -f.b; coords[1].z = f.l;
4623 coords[2].x = -1; coords[2].y = -f.b; coords[2].z = f.r;
4624 coords[3].x = -1; coords[3].y = -f.t; coords[3].z = f.r;
4625 break;
4627 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4628 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4629 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4630 coords[0].x = f.l; coords[0].y = 1; coords[0].z = f.t;
4631 coords[1].x = f.l; coords[1].y = 1; coords[1].z = f.b;
4632 coords[2].x = f.r; coords[2].y = 1; coords[2].z = f.b;
4633 coords[3].x = f.r; coords[3].y = 1; coords[3].z = f.t;
4634 break;
4636 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4637 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4638 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4639 coords[0].x = f.l; coords[0].y = -1; coords[0].z = -f.t;
4640 coords[1].x = f.l; coords[1].y = -1; coords[1].z = -f.b;
4641 coords[2].x = f.r; coords[2].y = -1; coords[2].z = -f.b;
4642 coords[3].x = f.r; coords[3].y = -1; coords[3].z = -f.t;
4643 break;
4645 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4646 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4647 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4648 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1;
4649 coords[1].x = f.l; coords[1].y = -f.b; coords[1].z = 1;
4650 coords[2].x = f.r; coords[2].y = -f.b; coords[2].z = 1;
4651 coords[3].x = f.r; coords[3].y = -f.t; coords[3].z = 1;
4652 break;
4654 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4655 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4656 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4657 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1;
4658 coords[1].x = -f.l; coords[1].y = -f.b; coords[1].z = -1;
4659 coords[2].x = -f.r; coords[2].y = -f.b; coords[2].z = -1;
4660 coords[3].x = -f.r; coords[3].y = -f.t; coords[3].z = -1;
4661 break;
4663 default:
4664 ERR("Unexpected texture target %#x\n", This->glDescription.target);
4665 return;
4668 ActivateContext(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4669 ENTER_GL();
4671 glEnable(bind_target);
4672 checkGLcall("glEnable(bind_target)");
4673 glBindTexture(bind_target, This->glDescription.textureName);
4674 checkGLcall("bind_target, This->glDescription.textureName)");
4675 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4676 checkGLcall("glTexParameteri");
4677 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4678 checkGLcall("glTexParameteri");
4680 if (device->render_offscreen)
4682 LONG tmp = rect.top;
4683 rect.top = rect.bottom;
4684 rect.bottom = tmp;
4687 glBegin(GL_QUADS);
4688 glTexCoord3fv(&coords[0].x);
4689 glVertex2i(rect.left, rect.top);
4691 glTexCoord3fv(&coords[1].x);
4692 glVertex2i(rect.left, rect.bottom);
4694 glTexCoord3fv(&coords[2].x);
4695 glVertex2i(rect.right, rect.bottom);
4697 glTexCoord3fv(&coords[3].x);
4698 glVertex2i(rect.right, rect.top);
4699 glEnd();
4700 checkGLcall("glEnd");
4702 glDisable(bind_target);
4703 checkGLcall("glDisable(bind_target)");
4705 LEAVE_GL();
4707 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain)))
4709 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
4710 if(((IWineD3DSwapChainImpl*)swapchain)->frontBuffer == (IWineD3DSurface*)This ||
4711 ((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
4712 glFlush();
4714 IWineD3DSwapChain_Release(swapchain);
4715 } else {
4716 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
4717 * reset properly next draw
4719 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture)))
4721 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
4722 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
4723 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
4724 IWineD3DBaseTexture_Release(texture);
4729 /*****************************************************************************
4730 * IWineD3DSurface::LoadLocation
4732 * Copies the current surface data from wherever it is to the requested
4733 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4734 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4735 * multiple locations, the gl texture is preferred over the drawable, which is
4736 * preferred over system memory. The PBO counts as system memory. If rect is
4737 * not NULL, only the specified rectangle is copied (only supported for
4738 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4739 * location is marked up to date after the copy.
4741 * Parameters:
4742 * flag: Surface location flag to be updated
4743 * rect: rectangle to be copied
4745 * Returns:
4746 * WINED3D_OK on success
4747 * WINED3DERR_DEVICELOST on an internal error
4749 *****************************************************************************/
4750 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4751 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4752 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4753 GLenum format, internal, type;
4754 CONVERT_TYPES convert;
4755 int bpp;
4756 int width, pitch, outpitch;
4757 BYTE *mem;
4758 BOOL drawable_read_ok = TRUE;
4760 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4761 if (This->Flags & SFLAG_SWAPCHAIN)
4763 TRACE("Surface %p is an onscreen surface\n", iface);
4764 } else {
4765 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4766 * Prefer SFLAG_INTEXTURE. */
4767 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4768 drawable_read_ok = FALSE;
4772 TRACE("(%p)->(%s, %p)\n", iface, debug_surflocation(flag), rect);
4773 if(rect) {
4774 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4777 if(This->Flags & flag) {
4778 TRACE("Location already up to date\n");
4779 return WINED3D_OK;
4782 if(!(This->Flags & SFLAG_LOCATIONS)) {
4783 ERR("%p: Surface does not have any up to date location\n", This);
4784 This->Flags |= SFLAG_LOST;
4785 return WINED3DERR_DEVICELOST;
4788 if(flag == SFLAG_INSYSMEM) {
4789 surface_prepare_system_memory(This);
4791 /* Download the surface to system memory */
4792 if(This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) {
4793 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4794 surface_bind_and_dirtify(This, !(This->Flags & SFLAG_INTEXTURE));
4796 surface_download_data(This);
4797 } else {
4798 /* Note: It might be faster to download into a texture first. */
4799 read_from_framebuffer(This, rect,
4800 This->resource.allocatedMemory,
4801 IWineD3DSurface_GetPitch(iface));
4803 } else if(flag == SFLAG_INDRAWABLE) {
4804 if(This->Flags & SFLAG_INTEXTURE) {
4805 surface_blt_to_drawable(This, rect);
4806 } else {
4807 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4808 /* This needs a shader to convert the srgb data sampled from the GL texture into RGB
4809 * values, otherwise we get incorrect values in the target. For now go the slow way
4810 * via a system memory copy
4812 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4815 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, FALSE);
4817 /* The width is in 'length' not in bytes */
4818 width = This->currentDesc.Width;
4819 pitch = IWineD3DSurface_GetPitch(iface);
4821 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4822 * but it isn't set (yet) in all cases it is getting called. */
4823 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4824 TRACE("Removing the pbo attached to surface %p\n", This);
4825 surface_remove_pbo(This);
4828 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4829 int height = This->currentDesc.Height;
4831 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4832 outpitch = width * bpp;
4833 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4835 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4836 if(!mem) {
4837 ERR("Out of memory %d, %d!\n", outpitch, height);
4838 return WINED3DERR_OUTOFVIDEOMEMORY;
4840 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4842 This->Flags |= SFLAG_CONVERTED;
4843 } else {
4844 This->Flags &= ~SFLAG_CONVERTED;
4845 mem = This->resource.allocatedMemory;
4848 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4850 /* Don't delete PBO memory */
4851 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4852 HeapFree(GetProcessHeap(), 0, mem);
4854 } else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */ {
4855 if (drawable_read_ok && (This->Flags & SFLAG_INDRAWABLE)) {
4856 read_from_framebuffer_texture(This, flag == SFLAG_INSRGBTEX);
4857 } else { /* Upload from system memory */
4858 BOOL srgb = flag == SFLAG_INSRGBTEX;
4859 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4860 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
4862 if(srgb) {
4863 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE) {
4864 /* Performance warning ... */
4865 FIXME("%p: Downloading rgb texture to reload it as srgb\n", This);
4866 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4868 } else {
4869 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4870 /* Performance warning ... */
4871 FIXME("%p: Downloading srgb texture to reload it as rgb\n", This);
4872 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4876 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4877 surface_bind_and_dirtify(This, srgb);
4879 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4880 This->Flags |= SFLAG_GLCKEY;
4881 This->glCKey = This->SrcBltCKey;
4883 else This->Flags &= ~SFLAG_GLCKEY;
4885 /* The width is in 'length' not in bytes */
4886 width = This->currentDesc.Width;
4887 pitch = IWineD3DSurface_GetPitch(iface);
4889 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4890 * but it isn't set (yet) in all cases it is getting called. */
4891 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4892 TRACE("Removing the pbo attached to surface %p\n", This);
4893 surface_remove_pbo(This);
4896 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4897 int height = This->currentDesc.Height;
4899 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4900 outpitch = width * bpp;
4901 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4903 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4904 if(!mem) {
4905 ERR("Out of memory %d, %d!\n", outpitch, height);
4906 return WINED3DERR_OUTOFVIDEOMEMORY;
4908 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4910 This->Flags |= SFLAG_CONVERTED;
4912 else if ((This->resource.format_desc->format == WINED3DFMT_P8)
4913 && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)))
4915 d3dfmt_p8_upload_palette(iface, convert);
4916 This->Flags &= ~SFLAG_CONVERTED;
4917 mem = This->resource.allocatedMemory;
4918 } else {
4919 This->Flags &= ~SFLAG_CONVERTED;
4920 mem = This->resource.allocatedMemory;
4923 /* Make sure the correct pitch is used */
4924 ENTER_GL();
4925 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4926 LEAVE_GL();
4928 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4929 TRACE("non power of two support\n");
4930 if(!(This->Flags & alloc_flag)) {
4931 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4932 This->Flags |= alloc_flag;
4934 if (mem || (This->Flags & SFLAG_PBO)) {
4935 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4937 } else {
4938 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4939 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4941 if(!(This->Flags & alloc_flag)) {
4942 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4943 This->Flags |= alloc_flag;
4945 if (mem || (This->Flags & SFLAG_PBO)) {
4946 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4950 /* Restore the default pitch */
4951 ENTER_GL();
4952 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4953 LEAVE_GL();
4955 /* Don't delete PBO memory */
4956 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4957 HeapFree(GetProcessHeap(), 0, mem);
4961 if(rect == NULL) {
4962 This->Flags |= flag;
4965 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !(This->Flags & SFLAG_SWAPCHAIN)
4966 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4967 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4968 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4971 return WINED3D_OK;
4974 static HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container)
4976 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4977 IWineD3DSwapChain *swapchain = NULL;
4979 /* Update the drawable size method */
4980 if(container) {
4981 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4983 if(swapchain) {
4984 This->get_drawable_size = get_drawable_size_swapchain;
4985 IWineD3DSwapChain_Release(swapchain);
4986 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4987 switch(wined3d_settings.offscreen_rendering_mode) {
4988 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4989 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4990 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4994 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4997 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4998 return SURFACE_OPENGL;
5001 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
5002 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
5003 HRESULT hr;
5005 /* If there's no destination surface there is nothing to do */
5006 if(!This->overlay_dest) return WINED3D_OK;
5008 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
5009 * update the overlay. Prevent an endless recursion
5011 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
5012 return WINED3D_OK;
5014 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
5015 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
5016 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
5017 NULL, WINED3DTEXF_LINEAR);
5018 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
5020 return hr;
5023 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
5025 /* IUnknown */
5026 IWineD3DBaseSurfaceImpl_QueryInterface,
5027 IWineD3DBaseSurfaceImpl_AddRef,
5028 IWineD3DSurfaceImpl_Release,
5029 /* IWineD3DResource */
5030 IWineD3DBaseSurfaceImpl_GetParent,
5031 IWineD3DBaseSurfaceImpl_GetDevice,
5032 IWineD3DBaseSurfaceImpl_SetPrivateData,
5033 IWineD3DBaseSurfaceImpl_GetPrivateData,
5034 IWineD3DBaseSurfaceImpl_FreePrivateData,
5035 IWineD3DBaseSurfaceImpl_SetPriority,
5036 IWineD3DBaseSurfaceImpl_GetPriority,
5037 IWineD3DSurfaceImpl_PreLoad,
5038 IWineD3DSurfaceImpl_UnLoad,
5039 IWineD3DBaseSurfaceImpl_GetType,
5040 /* IWineD3DSurface */
5041 IWineD3DBaseSurfaceImpl_GetContainer,
5042 IWineD3DBaseSurfaceImpl_GetDesc,
5043 IWineD3DSurfaceImpl_LockRect,
5044 IWineD3DSurfaceImpl_UnlockRect,
5045 IWineD3DSurfaceImpl_GetDC,
5046 IWineD3DSurfaceImpl_ReleaseDC,
5047 IWineD3DSurfaceImpl_Flip,
5048 IWineD3DSurfaceImpl_Blt,
5049 IWineD3DBaseSurfaceImpl_GetBltStatus,
5050 IWineD3DBaseSurfaceImpl_GetFlipStatus,
5051 IWineD3DBaseSurfaceImpl_IsLost,
5052 IWineD3DBaseSurfaceImpl_Restore,
5053 IWineD3DSurfaceImpl_BltFast,
5054 IWineD3DBaseSurfaceImpl_GetPalette,
5055 IWineD3DBaseSurfaceImpl_SetPalette,
5056 IWineD3DSurfaceImpl_RealizePalette,
5057 IWineD3DBaseSurfaceImpl_SetColorKey,
5058 IWineD3DBaseSurfaceImpl_GetPitch,
5059 IWineD3DSurfaceImpl_SetMem,
5060 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
5061 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
5062 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
5063 IWineD3DBaseSurfaceImpl_UpdateOverlay,
5064 IWineD3DBaseSurfaceImpl_SetClipper,
5065 IWineD3DBaseSurfaceImpl_GetClipper,
5066 /* Internal use: */
5067 IWineD3DSurfaceImpl_LoadTexture,
5068 IWineD3DSurfaceImpl_BindTexture,
5069 IWineD3DSurfaceImpl_SaveSnapshot,
5070 IWineD3DSurfaceImpl_SetContainer,
5071 IWineD3DSurfaceImpl_GetGlDesc,
5072 IWineD3DBaseSurfaceImpl_GetData,
5073 IWineD3DSurfaceImpl_SetFormat,
5074 IWineD3DSurfaceImpl_PrivateSetup,
5075 IWineD3DSurfaceImpl_ModifyLocation,
5076 IWineD3DSurfaceImpl_LoadLocation,
5077 IWineD3DSurfaceImpl_GetImplType,
5078 IWineD3DSurfaceImpl_DrawOverlay
5080 #undef GLINFO_LOCATION
5082 #define GLINFO_LOCATION device->adapter->gl_info
5083 static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
5084 static void ffp_blit_free(IWineD3DDevice *iface) { }
5086 static HRESULT ffp_blit_set(IWineD3DDevice *iface, const struct GlPixelFormatDesc *format_desc,
5087 GLenum textype, UINT width, UINT height)
5089 ENTER_GL();
5090 glEnable(textype);
5091 checkGLcall("glEnable(textype)");
5092 LEAVE_GL();
5093 return WINED3D_OK;
5096 static void ffp_blit_unset(IWineD3DDevice *iface) {
5097 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
5098 ENTER_GL();
5099 glDisable(GL_TEXTURE_2D);
5100 checkGLcall("glDisable(GL_TEXTURE_2D)");
5101 if(GL_SUPPORT(ARB_TEXTURE_CUBE_MAP)) {
5102 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
5103 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
5105 if(GL_SUPPORT(ARB_TEXTURE_RECTANGLE)) {
5106 glDisable(GL_TEXTURE_RECTANGLE_ARB);
5107 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
5109 LEAVE_GL();
5112 static BOOL ffp_blit_color_fixup_supported(struct color_fixup_desc fixup)
5114 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
5116 TRACE("Checking support for fixup:\n");
5117 dump_color_fixup_desc(fixup);
5120 /* We only support identity conversions. */
5121 if (is_identity_fixup(fixup))
5123 TRACE("[OK]\n");
5124 return TRUE;
5127 TRACE("[FAILED]\n");
5128 return FALSE;
5131 const struct blit_shader ffp_blit = {
5132 ffp_blit_alloc,
5133 ffp_blit_free,
5134 ffp_blit_set,
5135 ffp_blit_unset,
5136 ffp_blit_color_fixup_supported