push c4c845830c6aff14e1b16bbb8b4a57a7e0d6213f
[wine/hacks.git] / dlls / wined3d / surface.c
blob83d02d4c29fde2fff5a27efea4b00d512096294f
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->texture_name)
59 /* Release the OpenGL texture. */
60 TRACE("Deleting texture %u.\n", This->texture_name);
61 glDeleteTextures(1, &This->texture_name);
64 if (This->Flags & SFLAG_PBO)
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.0f) 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->texture_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->texture_name_srgb;
258 flag = SFLAG_INSRGBTEX;
260 else
262 name = &This->texture_name;
263 flag = SFLAG_INTEXTURE;
266 TRACE("(%p) : setting texture name %u\n", This, new_name);
268 if (!*name && new_name)
270 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
271 * surface has no texture name yet. See if we can get rid of this. */
272 if (This->Flags & flag)
273 ERR("Surface has SFLAG_INTEXTURE set, but no texture name\n");
274 IWineD3DSurface_ModifyLocation(iface, flag, FALSE);
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->texture_target != target)
289 if (target == GL_TEXTURE_RECTANGLE_ARB)
291 This->Flags &= ~SFLAG_NORMCOORD;
293 else if (This->texture_target == GL_TEXTURE_RECTANGLE_ARB)
295 This->Flags |= SFLAG_NORMCOORD;
298 This->texture_target = target;
299 surface_force_reload(iface);
302 /* Context activation is done by the caller. */
303 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This, BOOL srgb) {
304 int active_sampler;
306 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
307 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
308 * gl states. The current texture unit should always be a valid one.
310 * To be more specific, this is tricky because we can implicitly be called
311 * from sampler() in state.c. This means we can't touch anything other than
312 * whatever happens to be the currently active texture, or we would risk
313 * marking already applied sampler states dirty again.
315 * TODO: Track the current active texture per GL context instead of using glGet
317 GLint active_texture;
318 ENTER_GL();
319 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
320 LEAVE_GL();
321 active_sampler = This->resource.wineD3DDevice->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
323 if (active_sampler != -1) {
324 IWineD3DDeviceImpl_MarkStateDirty(This->resource.wineD3DDevice, STATE_SAMPLER(active_sampler));
326 IWineD3DSurface_BindTexture((IWineD3DSurface *)This, srgb);
329 /* This function checks if the primary render target uses the 8bit paletted format. */
330 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
332 if (device->render_targets && device->render_targets[0]) {
333 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
334 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
335 && (render_target->resource.format_desc->format == WINED3DFMT_P8))
336 return TRUE;
338 return FALSE;
341 #undef GLINFO_LOCATION
343 #define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
345 /* This call just downloads data, the caller is responsible for binding the
346 * correct texture. */
347 /* Context activation is done by the caller. */
348 static void surface_download_data(IWineD3DSurfaceImpl *This) {
349 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
351 /* Only support read back of converted P8 surfaces */
352 if (This->Flags & SFLAG_CONVERTED && format_desc->format != WINED3DFMT_P8)
354 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(format_desc->format));
355 return;
358 ENTER_GL();
360 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
362 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
363 This, This->texture_level, format_desc->glFormat, format_desc->glType,
364 This->resource.allocatedMemory);
366 if (This->Flags & SFLAG_PBO)
368 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
369 checkGLcall("glBindBufferARB");
370 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target, This->texture_level, NULL));
371 checkGLcall("glGetCompressedTexImageARB()");
372 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
373 checkGLcall("glBindBufferARB");
375 else
377 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target,
378 This->texture_level, This->resource.allocatedMemory));
379 checkGLcall("glGetCompressedTexImageARB()");
382 LEAVE_GL();
383 } else {
384 void *mem;
385 GLenum format = format_desc->glFormat;
386 GLenum type = format_desc->glType;
387 int src_pitch = 0;
388 int dst_pitch = 0;
390 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
391 if (format_desc->format == WINED3DFMT_P8 && primary_render_target_is_p8(This->resource.wineD3DDevice))
393 format = GL_ALPHA;
394 type = GL_UNSIGNED_BYTE;
397 if (This->Flags & SFLAG_NONPOW2) {
398 unsigned char alignment = This->resource.wineD3DDevice->surface_alignment;
399 src_pitch = format_desc->byte_count * This->pow2Width;
400 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
401 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
402 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
403 } else {
404 mem = This->resource.allocatedMemory;
407 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
408 This, This->texture_level, format, type, mem);
410 if(This->Flags & SFLAG_PBO) {
411 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
412 checkGLcall("glBindBufferARB");
414 glGetTexImage(This->texture_target, This->texture_level, format, type, NULL);
415 checkGLcall("glGetTexImage()");
417 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
418 checkGLcall("glBindBufferARB");
419 } else {
420 glGetTexImage(This->texture_target, This->texture_level, format, 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 binding the
498 * correct texture. */
499 /* Context activation is done by the caller. */
500 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
501 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
503 if (format_desc->heightscale != 1.0f && format_desc->heightscale != 0.0f) height *= format_desc->heightscale;
505 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
507 /* glCompressedTexSubImage2D() for uploading and glTexImage2D() for
508 * allocating does not work well on some drivers (r200 dri, MacOS ATI
509 * driver). glCompressedTexImage2D() does not accept NULL pointers. So
510 * for compressed textures surface_allocate_surface() does nothing,
511 * and this function uses glCompressedTexImage2D() instead of
512 * glCompressedTexSubImage2D(). */
513 TRACE("(%p) : Calling glCompressedTexImage2DARB w %u, h %u, data %p.\n", This, width, height, data);
515 ENTER_GL();
517 if (This->Flags & SFLAG_PBO)
519 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
520 checkGLcall("glBindBufferARB");
522 TRACE("(%p) pbo: %#x, data: %p.\n", This, This->pbo, data);
524 GL_EXTCALL(glCompressedTexImage2DARB(This->texture_target, This->texture_level,
525 internal, width, height, 0 /* border */, This->resource.size, NULL));
526 checkGLcall("glCompressedTexImage2DARB");
528 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
529 checkGLcall("glBindBufferARB");
531 else
533 GL_EXTCALL(glCompressedTexImage2DARB(This->texture_target, This->texture_level,
534 internal, width, height, 0 /* border */, This->resource.size, data));
535 checkGLcall("glCompressedTexSubImage2D");
538 LEAVE_GL();
540 else
542 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
543 ENTER_GL();
545 if(This->Flags & SFLAG_PBO) {
546 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
547 checkGLcall("glBindBufferARB");
548 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
550 glTexSubImage2D(This->texture_target, This->texture_level, 0, 0, width, height, format, type, NULL);
551 checkGLcall("glTexSubImage2D");
553 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
554 checkGLcall("glBindBufferARB");
556 else {
557 glTexSubImage2D(This->texture_target, This->texture_level, 0, 0, width, height, format, type, data);
558 checkGLcall("glTexSubImage2D");
561 LEAVE_GL();
565 /* This call just allocates the texture, the caller is responsible for binding
566 * the correct texture. */
567 /* Context activation is done by the caller. */
568 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
569 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
570 BOOL enable_client_storage = FALSE;
571 const BYTE *mem = NULL;
573 if (format_desc->heightscale != 1.0f && format_desc->heightscale != 0.0f) height *= format_desc->heightscale;
575 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",
576 This, This->texture_target, This->texture_level, debug_d3dformat(format_desc->format),
577 internal, width, height, format, type);
579 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
581 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
582 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
584 /* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
585 * once, unfortunately
587 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
588 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
589 This->Flags |= SFLAG_CLIENT;
590 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
591 ENTER_GL();
592 GL_EXTCALL(glCompressedTexImage2DARB(This->texture_target, This->texture_level, internal,
593 width, height, 0 /* border */, This->resource.size, mem));
594 LEAVE_GL();
597 return;
600 ENTER_GL();
602 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
603 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
604 /* In some cases we want to disable client storage.
605 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
606 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
607 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
608 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
609 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
611 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
612 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
613 This->Flags &= ~SFLAG_CLIENT;
614 enable_client_storage = TRUE;
615 } else {
616 This->Flags |= SFLAG_CLIENT;
618 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
619 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
621 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
624 glTexImage2D(This->texture_target, This->texture_level, internal, width, height, 0, format, type, mem);
625 checkGLcall("glTexImage2D");
627 if(enable_client_storage) {
628 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
629 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
631 LEAVE_GL();
634 /* In D3D the depth stencil dimensions have to be greater than or equal to the
635 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
636 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
637 /* GL locking is done by the caller */
638 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
639 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
640 renderbuffer_entry_t *entry;
641 GLuint renderbuffer = 0;
642 unsigned int src_width, src_height;
644 src_width = This->pow2Width;
645 src_height = This->pow2Height;
647 /* A depth stencil smaller than the render target is not valid */
648 if (width > src_width || height > src_height) return;
650 /* Remove any renderbuffer set if the sizes match */
651 if (width == src_width && height == src_height) {
652 This->current_renderbuffer = NULL;
653 return;
656 /* Look if we've already got a renderbuffer of the correct dimensions */
657 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
658 if (entry->width == width && entry->height == height) {
659 renderbuffer = entry->id;
660 This->current_renderbuffer = entry;
661 break;
665 if (!renderbuffer) {
666 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
667 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
668 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
669 This->resource.format_desc->glInternal, width, height));
671 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
672 entry->width = width;
673 entry->height = height;
674 entry->id = renderbuffer;
675 list_add_head(&This->renderbuffers, &entry->entry);
677 This->current_renderbuffer = entry;
680 checkGLcall("set_compatible_renderbuffer");
683 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
684 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
685 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
687 TRACE("(%p) : swapchain %p\n", This, swapchain);
689 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
690 TRACE("Returning GL_BACK\n");
691 return GL_BACK;
692 } else if (swapchain_impl->frontBuffer == iface) {
693 TRACE("Returning GL_FRONT\n");
694 return GL_FRONT;
697 FIXME("Higher back buffer, returning GL_BACK\n");
698 return GL_BACK;
701 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
702 void surface_add_dirty_rect(IWineD3DSurface *iface, const RECT *dirty_rect)
704 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
705 IWineD3DBaseTexture *baseTexture = NULL;
707 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
708 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
710 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
711 if (dirty_rect)
713 This->dirtyRect.left = min(This->dirtyRect.left, dirty_rect->left);
714 This->dirtyRect.top = min(This->dirtyRect.top, dirty_rect->top);
715 This->dirtyRect.right = max(This->dirtyRect.right, dirty_rect->right);
716 This->dirtyRect.bottom = max(This->dirtyRect.bottom, dirty_rect->bottom);
718 else
720 This->dirtyRect.left = 0;
721 This->dirtyRect.top = 0;
722 This->dirtyRect.right = This->currentDesc.Width;
723 This->dirtyRect.bottom = This->currentDesc.Height;
726 TRACE("(%p) : Dirty: yes, Rect:(%d, %d, %d, %d)\n", This, This->dirtyRect.left,
727 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
729 /* if the container is a basetexture then mark it dirty. */
730 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture)))
732 TRACE("Passing to container\n");
733 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
734 IWineD3DBaseTexture_Release(baseTexture);
738 static inline BOOL surface_can_stretch_rect(IWineD3DSurfaceImpl *src, IWineD3DSurfaceImpl *dst)
740 return ((src->resource.format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE)
741 || (src->resource.usage & WINED3DUSAGE_RENDERTARGET))
742 && ((dst->resource.format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE)
743 || (dst->resource.usage & WINED3DUSAGE_RENDERTARGET))
744 && (src->resource.format_desc->format == dst->resource.format_desc->format
745 || (is_identity_fixup(src->resource.format_desc->color_fixup)
746 && is_identity_fixup(dst->resource.format_desc->color_fixup)));
749 static ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface)
751 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
752 ULONG ref = InterlockedDecrement(&This->resource.ref);
753 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
755 if (!ref)
757 surface_cleanup(This);
759 TRACE("(%p) Released.\n", This);
760 HeapFree(GetProcessHeap(), 0, This);
763 return ref;
766 /* ****************************************************
767 IWineD3DSurface IWineD3DResource parts follow
768 **************************************************** */
770 void surface_internal_preload(IWineD3DSurface *iface, enum WINED3DSRGB srgb)
772 /* TODO: check for locks */
773 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
774 IWineD3DBaseTexture *baseTexture = NULL;
775 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
777 TRACE("(%p)Checking to see if the container is a base texture\n", This);
778 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
779 IWineD3DBaseTextureImpl *tex_impl = (IWineD3DBaseTextureImpl *) baseTexture;
780 TRACE("Passing to container\n");
781 tex_impl->baseTexture.internal_preload(baseTexture, SRGB_RGB);
782 IWineD3DBaseTexture_Release(baseTexture);
783 } else {
784 TRACE("(%p) : About to load surface\n", This);
786 if(!device->isInDraw) {
787 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
790 if (This->resource.format_desc->format == WINED3DFMT_P8
791 || This->resource.format_desc->format == WINED3DFMT_A8P8)
793 if(palette9_changed(This)) {
794 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
795 /* TODO: This is not necessarily needed with hw palettized texture support */
796 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
797 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
798 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
802 IWineD3DSurface_LoadTexture(iface, srgb == SRGB_SRGB ? TRUE : FALSE);
804 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
805 /* Tell opengl to try and keep this texture in video ram (well mostly) */
806 GLclampf tmp;
807 tmp = 0.9f;
808 ENTER_GL();
809 glPrioritizeTextures(1, &This->texture_name, &tmp);
810 LEAVE_GL();
813 return;
816 static void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
817 surface_internal_preload(iface, SRGB_ANY);
820 /* Context activation is done by the caller. */
821 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
822 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
823 This->resource.allocatedMemory =
824 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
826 ENTER_GL();
827 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
828 checkGLcall("glBindBuffer(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
829 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
830 checkGLcall("glGetBufferSubData");
831 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
832 checkGLcall("glDeleteBuffers");
833 LEAVE_GL();
835 This->pbo = 0;
836 This->Flags &= ~SFLAG_PBO;
839 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
840 IWineD3DBaseTexture *texture = NULL;
841 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
842 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
843 renderbuffer_entry_t *entry, *entry2;
844 TRACE("(%p)\n", iface);
846 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
847 /* Default pool resources are supposed to be destroyed before Reset is called.
848 * Implicit resources stay however. So this means we have an implicit render target
849 * or depth stencil. The content may be destroyed, but we still have to tear down
850 * opengl resources, so we cannot leave early.
852 * Put the most up to date surface location into the drawable. D3D-wise this content
853 * is undefined, so it would be nowhere, but that would make the location management
854 * more complicated. The drawable is a sane location, because if we mark sysmem or
855 * texture up to date, drawPrim will copy the uninitialized texture or sysmem to the
856 * uninitialized drawable. That's pointless and we'd have to allocate the texture /
857 * sysmem copy here.
859 if (This->resource.usage & WINED3DUSAGE_DEPTHSTENCIL) {
860 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
861 } else {
862 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, TRUE);
864 } else {
865 /* Load the surface into system memory */
866 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
867 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
869 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
870 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSRGBTEX, FALSE);
871 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
873 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
875 /* Destroy PBOs, but load them into real sysmem before */
876 if(This->Flags & SFLAG_PBO) {
877 surface_remove_pbo(This);
880 /* Destroy fbo render buffers. This is needed for implicit render targets, for
881 * all application-created targets the application has to release the surface
882 * before calling _Reset
884 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
885 ENTER_GL();
886 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
887 LEAVE_GL();
888 list_remove(&entry->entry);
889 HeapFree(GetProcessHeap(), 0, entry);
891 list_init(&This->renderbuffers);
892 This->current_renderbuffer = NULL;
894 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
895 * destroy it
897 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
898 if(!texture) {
899 ENTER_GL();
900 glDeleteTextures(1, &This->texture_name);
901 This->texture_name = 0;
902 glDeleteTextures(1, &This->texture_name_srgb);
903 This->texture_name_srgb = 0;
904 LEAVE_GL();
905 } else {
906 IWineD3DBaseTexture_Release(texture);
908 return;
911 /* ******************************************************
912 IWineD3DSurface IWineD3DSurface parts follow
913 ****************************************************** */
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->texture_target, This->texture_level,
1219 0, 0, 0, 0, This->currentDesc.Width, This->currentDesc.Height);
1220 checkGLcall("glCopyTexSubImage2D");
1222 glReadBuffer(prevRead);
1223 checkGLcall("glReadBuffer");
1225 LEAVE_GL();
1226 TRACE("Updated target %d\n", This->texture_target);
1229 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
1230 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
1231 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
1232 * changed
1234 if(!(This->Flags & SFLAG_DYNLOCK)) {
1235 This->lockCount++;
1236 /* MAXLOCKCOUNT is defined in wined3d_private.h */
1237 if(This->lockCount > MAXLOCKCOUNT) {
1238 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
1239 This->Flags |= SFLAG_DYNLOCK;
1243 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
1244 * Also don't create a PBO for systemmem surfaces.
1246 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
1247 GLenum error;
1248 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1250 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1251 ENTER_GL();
1253 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
1254 error = glGetError();
1255 if(This->pbo == 0 || error != GL_NO_ERROR) {
1256 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
1259 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
1261 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1262 checkGLcall("glBindBufferARB");
1264 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1265 checkGLcall("glBufferDataARB");
1267 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1268 checkGLcall("glBindBufferARB");
1270 /* We don't need the system memory anymore and we can't even use it for PBOs */
1271 if(!(This->Flags & SFLAG_CLIENT)) {
1272 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1273 This->resource.heapMemory = NULL;
1275 This->resource.allocatedMemory = NULL;
1276 This->Flags |= SFLAG_PBO;
1277 LEAVE_GL();
1278 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
1279 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1280 * or a pbo to map
1282 if(!This->resource.heapMemory) {
1283 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1285 This->resource.allocatedMemory =
1286 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1287 if(This->Flags & SFLAG_INSYSMEM) {
1288 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1293 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1294 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1295 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1296 const RECT *pass_rect = pRect;
1298 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1300 /* This is also done in the base class, but we have to verify this before loading any data from
1301 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1302 * may interfere, and all other bad things may happen
1304 if (This->Flags & SFLAG_LOCKED) {
1305 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1306 return WINED3DERR_INVALIDCALL;
1308 This->Flags |= SFLAG_LOCKED;
1310 if (!(This->Flags & SFLAG_LOCKABLE))
1312 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1315 if (Flags & WINED3DLOCK_DISCARD) {
1316 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1317 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1318 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1319 This->Flags |= SFLAG_INSYSMEM;
1320 goto lock_end;
1323 if (This->Flags & SFLAG_INSYSMEM) {
1324 TRACE("Local copy is up to date, not downloading data\n");
1325 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1326 goto lock_end;
1329 /* IWineD3DSurface_LoadLocation() does not check if the rectangle specifies
1330 * the full surface. Most callers don't need that, so do it here. */
1331 if (pRect && pRect->top == 0 && pRect->left == 0
1332 && pRect->right == This->currentDesc.Width
1333 && pRect->bottom == This->currentDesc.Height)
1335 pass_rect = NULL;
1338 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
1339 && ((This->Flags & SFLAG_SWAPCHAIN) || iface == myDevice->render_targets[0])))
1341 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1344 lock_end:
1345 if(This->Flags & SFLAG_PBO) {
1346 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1347 ENTER_GL();
1348 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1349 checkGLcall("glBindBufferARB");
1351 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1352 if(This->resource.allocatedMemory) {
1353 ERR("The surface already has PBO memory allocated!\n");
1356 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1357 checkGLcall("glMapBufferARB");
1359 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1360 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1361 checkGLcall("glBindBufferARB");
1363 LEAVE_GL();
1366 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1367 /* Don't dirtify */
1368 } else {
1369 IWineD3DBaseTexture *pBaseTexture;
1371 * Dirtify on lock
1372 * as seen in msdn docs
1374 surface_add_dirty_rect(iface, pRect);
1376 /** Dirtify Container if needed */
1377 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1378 TRACE("Making container dirty\n");
1379 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1380 IWineD3DBaseTexture_Release(pBaseTexture);
1381 } else {
1382 TRACE("Surface is standalone, no need to dirty the container\n");
1386 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1389 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1390 GLint prev_store;
1391 GLint prev_rasterpos[4];
1392 GLint skipBytes = 0;
1393 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1394 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1395 IWineD3DSwapChainImpl *swapchain;
1397 /* Activate the correct context for the render target */
1398 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1399 ENTER_GL();
1401 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
1402 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1403 TRACE("Unlocking %#x buffer\n", buffer);
1404 glDrawBuffer(buffer);
1405 checkGLcall("glDrawBuffer");
1407 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1408 } else {
1409 /* Primary offscreen render target */
1410 TRACE("Offscreen render target\n");
1411 glDrawBuffer(myDevice->offscreenBuffer);
1412 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1415 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1416 checkGLcall("glIntegerv");
1417 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1418 checkGLcall("glIntegerv");
1419 glPixelZoom(1.0f, -1.0f);
1420 checkGLcall("glPixelZoom");
1422 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1423 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1424 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1426 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1427 checkGLcall("glRasterPos2f");
1429 /* Some drivers(radeon dri, others?) don't like exceptions during
1430 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1431 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1432 * catch to put the dib section in InSync mode, which leads to a crash
1433 * and a blocked x server on my radeon card.
1435 * The following lines read the dib section so it is put in InSync mode
1436 * before glDrawPixels is called and the crash is prevented. There won't
1437 * be any interfering gdi accesses, because UnlockRect is called from
1438 * ReleaseDC, and the app won't use the dc any more afterwards.
1440 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1441 volatile BYTE read;
1442 read = This->resource.allocatedMemory[0];
1445 if(This->Flags & SFLAG_PBO) {
1446 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1447 checkGLcall("glBindBufferARB");
1450 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1451 if(This->Flags & SFLAG_LOCKED) {
1452 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1453 (This->lockedRect.bottom - This->lockedRect.top)-1,
1454 fmt, type,
1455 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1456 checkGLcall("glDrawPixels");
1457 } else {
1458 glDrawPixels(This->currentDesc.Width,
1459 This->currentDesc.Height,
1460 fmt, type, mem);
1461 checkGLcall("glDrawPixels");
1464 if(This->Flags & SFLAG_PBO) {
1465 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1466 checkGLcall("glBindBufferARB");
1469 glPixelZoom(1.0f, 1.0f);
1470 checkGLcall("glPixelZoom");
1472 glRasterPos3iv(&prev_rasterpos[0]);
1473 checkGLcall("glRasterPos3iv");
1475 /* Reset to previous pack row length */
1476 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1477 checkGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1479 if(!swapchain) {
1480 glDrawBuffer(myDevice->offscreenBuffer);
1481 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1482 } else if(swapchain->backBuffer) {
1483 glDrawBuffer(GL_BACK);
1484 checkGLcall("glDrawBuffer(GL_BACK)");
1485 } else {
1486 glDrawBuffer(GL_FRONT);
1487 checkGLcall("glDrawBuffer(GL_FRONT)");
1489 LEAVE_GL();
1491 return;
1494 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1495 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1496 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1497 BOOL fullsurface;
1499 if (!(This->Flags & SFLAG_LOCKED)) {
1500 WARN("trying to Unlock an unlocked surf@%p\n", This);
1501 return WINEDDERR_NOTLOCKED;
1504 if (This->Flags & SFLAG_PBO) {
1505 TRACE("Freeing PBO memory\n");
1506 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1507 ENTER_GL();
1508 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1509 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1510 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1511 checkGLcall("glUnmapBufferARB");
1512 LEAVE_GL();
1513 This->resource.allocatedMemory = NULL;
1516 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1518 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1519 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1520 goto unlock_end;
1523 if ((This->Flags & SFLAG_SWAPCHAIN) || (myDevice->render_targets && iface == myDevice->render_targets[0]))
1525 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1526 static BOOL warned = FALSE;
1527 if(!warned) {
1528 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1529 warned = TRUE;
1531 goto unlock_end;
1534 if(This->dirtyRect.left == 0 &&
1535 This->dirtyRect.top == 0 &&
1536 This->dirtyRect.right == This->currentDesc.Width &&
1537 This->dirtyRect.bottom == This->currentDesc.Height) {
1538 fullsurface = TRUE;
1539 } else {
1540 /* TODO: Proper partial rectangle tracking */
1541 fullsurface = FALSE;
1542 This->Flags |= SFLAG_INSYSMEM;
1545 switch(wined3d_settings.rendertargetlock_mode) {
1546 case RTL_READTEX:
1547 case RTL_TEXTEX:
1548 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1549 /* drop through */
1551 case RTL_AUTO:
1552 case RTL_READDRAW:
1553 case RTL_TEXDRAW:
1554 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1555 break;
1558 if(!fullsurface) {
1559 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1560 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1561 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1562 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1563 * not fully up to date because only a subrectangle was read in LockRect.
1565 This->Flags &= ~SFLAG_INSYSMEM;
1566 This->Flags |= SFLAG_INDRAWABLE;
1569 This->dirtyRect.left = This->currentDesc.Width;
1570 This->dirtyRect.top = This->currentDesc.Height;
1571 This->dirtyRect.right = 0;
1572 This->dirtyRect.bottom = 0;
1573 } else if(iface == myDevice->stencilBufferTarget) {
1574 FIXME("Depth Stencil buffer locking is not implemented\n");
1575 } else {
1576 /* The rest should be a normal texture */
1577 IWineD3DBaseTextureImpl *impl;
1578 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1579 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1580 * states need resetting
1582 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1583 if(impl->baseTexture.bindCount) {
1584 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1586 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1590 unlock_end:
1591 This->Flags &= ~SFLAG_LOCKED;
1592 memset(&This->lockedRect, 0, sizeof(RECT));
1594 /* Overlays have to be redrawn manually after changes with the GL implementation */
1595 if(This->overlay_dest) {
1596 IWineD3DSurface_DrawOverlay(iface);
1598 return WINED3D_OK;
1601 static HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC)
1603 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1604 WINED3DLOCKED_RECT lock;
1605 HRESULT hr;
1606 RGBQUAD col[256];
1608 TRACE("(%p)->(%p)\n",This,pHDC);
1610 if(This->Flags & SFLAG_USERPTR) {
1611 ERR("Not supported on surfaces with an application-provided surfaces\n");
1612 return WINEDDERR_NODC;
1615 /* Give more detailed info for ddraw */
1616 if (This->Flags & SFLAG_DCINUSE)
1617 return WINEDDERR_DCALREADYCREATED;
1619 /* Can't GetDC if the surface is locked */
1620 if (This->Flags & SFLAG_LOCKED)
1621 return WINED3DERR_INVALIDCALL;
1623 /* According to Direct3D9 docs, only these formats are supported */
1624 if (((IWineD3DImpl *)This->resource.wineD3DDevice->wineD3D)->dxVersion > 7) {
1625 if (This->resource.format_desc->format != WINED3DFMT_R5G6B5
1626 && This->resource.format_desc->format != WINED3DFMT_X1R5G5B5
1627 && This->resource.format_desc->format != WINED3DFMT_R8G8B8
1628 && This->resource.format_desc->format != WINED3DFMT_X8R8G8B8)
1629 return WINED3DERR_INVALIDCALL;
1632 memset(&lock, 0, sizeof(lock)); /* To be sure */
1634 /* Create a DIB section if there isn't a hdc yet */
1635 if(!This->hDC) {
1636 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1637 if(This->Flags & SFLAG_CLIENT) {
1638 surface_internal_preload(iface, SRGB_RGB);
1641 /* Use the dib section from now on if we are not using a PBO */
1642 if(!(This->Flags & SFLAG_PBO))
1643 This->resource.allocatedMemory = This->dib.bitmap_data;
1646 /* Lock the surface */
1647 hr = IWineD3DSurface_LockRect(iface,
1648 &lock,
1649 NULL,
1652 if(This->Flags & SFLAG_PBO) {
1653 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1654 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1657 if(FAILED(hr)) {
1658 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1659 /* keep the dib section */
1660 return hr;
1663 if (This->resource.format_desc->format == WINED3DFMT_P8
1664 || This->resource.format_desc->format == WINED3DFMT_A8P8)
1666 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1667 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1668 unsigned int n;
1669 const PALETTEENTRY *pal = NULL;
1671 if(This->palette) {
1672 pal = This->palette->palents;
1673 } else {
1674 IWineD3DSurfaceImpl *dds_primary;
1675 IWineD3DSwapChainImpl *swapchain;
1676 swapchain = (IWineD3DSwapChainImpl *)This->resource.wineD3DDevice->swapchains[0];
1677 dds_primary = (IWineD3DSurfaceImpl *)swapchain->frontBuffer;
1678 if (dds_primary && dds_primary->palette)
1679 pal = dds_primary->palette->palents;
1682 if (pal) {
1683 for (n=0; n<256; n++) {
1684 col[n].rgbRed = pal[n].peRed;
1685 col[n].rgbGreen = pal[n].peGreen;
1686 col[n].rgbBlue = pal[n].peBlue;
1687 col[n].rgbReserved = 0;
1689 SetDIBColorTable(This->hDC, 0, 256, col);
1693 *pHDC = This->hDC;
1694 TRACE("returning %p\n",*pHDC);
1695 This->Flags |= SFLAG_DCINUSE;
1697 return WINED3D_OK;
1700 static HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC)
1702 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1704 TRACE("(%p)->(%p)\n",This,hDC);
1706 if (!(This->Flags & SFLAG_DCINUSE))
1707 return WINEDDERR_NODC;
1709 if (This->hDC !=hDC) {
1710 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1711 return WINEDDERR_NODC;
1714 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1715 /* Copy the contents of the DIB over to the PBO */
1716 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1719 /* we locked first, so unlock now */
1720 IWineD3DSurface_UnlockRect(iface);
1722 This->Flags &= ~SFLAG_DCINUSE;
1724 return WINED3D_OK;
1727 /* ******************************************************
1728 IWineD3DSurface Internal (No mapping to directx api) parts follow
1729 ****************************************************** */
1731 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) {
1732 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1733 const struct GlPixelFormatDesc *glDesc = This->resource.format_desc;
1734 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1736 /* Default values: From the surface */
1737 *format = glDesc->glFormat;
1738 *type = glDesc->glType;
1739 *convert = NO_CONVERSION;
1740 *target_bpp = glDesc->byte_count;
1742 if(srgb_mode) {
1743 *internal = glDesc->glGammaInternal;
1745 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET
1746 && !(This->Flags & SFLAG_SWAPCHAIN))
1748 *internal = glDesc->rtInternal;
1749 } else {
1750 *internal = glDesc->glInternal;
1753 /* Ok, now look if we have to do any conversion */
1754 switch(This->resource.format_desc->format)
1756 case WINED3DFMT_P8:
1757 /* ****************
1758 Paletted Texture
1759 **************** */
1761 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1762 * of the two is available make sure texturing is requested as neither of the two works in
1763 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1764 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1765 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1766 * conflicts with this.
1768 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) ||
1769 (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) &&
1770 device->render_targets &&
1771 This == (IWineD3DSurfaceImpl*)device->render_targets[0])) ||
1772 colorkey_active || !use_texturing ) {
1773 *format = GL_RGBA;
1774 *internal = GL_RGBA;
1775 *type = GL_UNSIGNED_BYTE;
1776 *target_bpp = 4;
1777 if(colorkey_active) {
1778 *convert = CONVERT_PALETTED_CK;
1779 } else {
1780 *convert = CONVERT_PALETTED;
1783 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1784 *format = GL_ALPHA;
1785 *type = GL_UNSIGNED_BYTE;
1786 *target_bpp = 1;
1789 break;
1791 case WINED3DFMT_R3G3B2:
1792 /* **********************
1793 GL_UNSIGNED_BYTE_3_3_2
1794 ********************** */
1795 if (colorkey_active) {
1796 /* This texture format will never be used.. So do not care about color keying
1797 up until the point in time it will be needed :-) */
1798 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1800 break;
1802 case WINED3DFMT_R5G6B5:
1803 if (colorkey_active) {
1804 *convert = CONVERT_CK_565;
1805 *format = GL_RGBA;
1806 *internal = GL_RGB5_A1;
1807 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1809 break;
1811 case WINED3DFMT_X1R5G5B5:
1812 if (colorkey_active) {
1813 *convert = CONVERT_CK_5551;
1814 *format = GL_BGRA;
1815 *internal = GL_RGB5_A1;
1816 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1818 break;
1820 case WINED3DFMT_R8G8B8:
1821 if (colorkey_active) {
1822 *convert = CONVERT_CK_RGB24;
1823 *format = GL_RGBA;
1824 *internal = GL_RGBA8;
1825 *type = GL_UNSIGNED_INT_8_8_8_8;
1826 *target_bpp = 4;
1828 break;
1830 case WINED3DFMT_X8R8G8B8:
1831 if (colorkey_active) {
1832 *convert = CONVERT_RGB32_888;
1833 *format = GL_RGBA;
1834 *internal = GL_RGBA8;
1835 *type = GL_UNSIGNED_INT_8_8_8_8;
1837 break;
1839 case WINED3DFMT_R8G8_SNORM:
1840 if (GL_SUPPORT(NV_TEXTURE_SHADER)) break;
1841 *convert = CONVERT_V8U8;
1842 *format = GL_BGR;
1843 *type = GL_UNSIGNED_BYTE;
1844 *target_bpp = 3;
1845 break;
1847 case WINED3DFMT_L6V5U5:
1848 *convert = CONVERT_L6V5U5;
1849 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1850 *target_bpp = 3;
1851 /* Use format and types from table */
1852 } else {
1853 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1854 *target_bpp = 2;
1855 *format = GL_RGB;
1856 *type = GL_UNSIGNED_SHORT_5_6_5;
1858 break;
1860 case WINED3DFMT_X8L8V8U8:
1861 *convert = CONVERT_X8L8V8U8;
1862 *target_bpp = 4;
1863 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1864 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1865 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1866 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1867 * the needed type and format parameter, so the internal format contains a
1868 * 4th component, which is returned as alpha
1870 } else {
1871 *format = GL_BGRA;
1872 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1874 break;
1876 case WINED3DFMT_R8G8B8A8_SNORM:
1877 if (GL_SUPPORT(NV_TEXTURE_SHADER)) break;
1878 *convert = CONVERT_Q8W8V8U8;
1879 *format = GL_BGRA;
1880 *type = GL_UNSIGNED_BYTE;
1881 *target_bpp = 4;
1882 break;
1884 case WINED3DFMT_R16G16_SNORM:
1885 if (GL_SUPPORT(NV_TEXTURE_SHADER)) break;
1886 *convert = CONVERT_V16U16;
1887 *format = GL_BGR;
1888 *type = GL_UNSIGNED_SHORT;
1889 *target_bpp = 6;
1890 break;
1892 case WINED3DFMT_A4L4:
1893 /* A4L4 exists as an internal gl format, but for some reason there is not
1894 * format+type combination to load it. Thus convert it to A8L8, then load it
1895 * with A4L4 internal, but A8L8 format+type
1897 *convert = CONVERT_A4L4;
1898 *format = GL_LUMINANCE_ALPHA;
1899 *type = GL_UNSIGNED_BYTE;
1900 *target_bpp = 2;
1901 break;
1903 case WINED3DFMT_R16G16_UNORM:
1904 *convert = CONVERT_G16R16;
1905 *format = GL_RGB;
1906 *type = GL_UNSIGNED_SHORT;
1907 *target_bpp = 6;
1908 break;
1910 case WINED3DFMT_R16G16_FLOAT:
1911 *convert = CONVERT_R16G16F;
1912 *format = GL_RGB;
1913 *type = GL_HALF_FLOAT_ARB;
1914 *target_bpp = 6;
1915 break;
1917 case WINED3DFMT_R32G32_FLOAT:
1918 *convert = CONVERT_R32G32F;
1919 *format = GL_RGB;
1920 *type = GL_FLOAT;
1921 *target_bpp = 12;
1922 break;
1924 case WINED3DFMT_D15S1:
1925 if (GL_SUPPORT(EXT_PACKED_DEPTH_STENCIL))
1927 *convert = CONVERT_D15S1;
1928 *target_bpp = 4;
1930 break;
1932 case WINED3DFMT_D24X4S4:
1933 if (GL_SUPPORT(EXT_PACKED_DEPTH_STENCIL))
1935 *convert = CONVERT_D24X4S4;
1937 break;
1939 case WINED3DFMT_D24FS8:
1940 if (GL_SUPPORT(ARB_DEPTH_BUFFER_FLOAT))
1942 *convert = CONVERT_D24FS8;
1943 *target_bpp = 8;
1945 break;
1947 default:
1948 break;
1951 return WINED3D_OK;
1954 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey)
1956 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1957 IWineD3DPaletteImpl *pal = This->palette;
1958 BOOL index_in_alpha = FALSE;
1959 unsigned int i;
1961 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
1962 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
1963 * is slow. Further RGB->P8 conversion is not possible because palettes can have
1964 * duplicate entries. Store the color key in the unused alpha component to speed the
1965 * download up and to make conversion unneeded. */
1966 index_in_alpha = primary_render_target_is_p8(device);
1968 if (!pal)
1970 UINT dxVersion = ((IWineD3DImpl *)device->wineD3D)->dxVersion;
1972 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
1973 if (dxVersion <= 7)
1975 ERR("This code should never get entered for DirectDraw!, expect problems\n");
1976 if (index_in_alpha)
1978 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
1979 * there's no palette at this time. */
1980 for (i = 0; i < 256; i++) table[i][3] = i;
1983 else
1985 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
1986 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
1987 * capability flag is present (wine does advertise this capability) */
1988 for (i = 0; i < 256; ++i)
1990 table[i][0] = device->palettes[device->currentPalette][i].peRed;
1991 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
1992 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
1993 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
1997 else
1999 TRACE("Using surface palette %p\n", pal);
2000 /* Get the surface's palette */
2001 for (i = 0; i < 256; ++i)
2003 table[i][0] = pal->palents[i].peRed;
2004 table[i][1] = pal->palents[i].peGreen;
2005 table[i][2] = pal->palents[i].peBlue;
2007 /* When index_in_alpha is set the palette index is stored in the
2008 * alpha component. In case of a readback we can then read
2009 * GL_ALPHA. Color keying is handled in BltOverride using a
2010 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
2011 * color key itself is passed to glAlphaFunc in other cases the
2012 * alpha component of pixels that should be masked away is set to 0. */
2013 if (index_in_alpha)
2015 table[i][3] = i;
2017 else if (colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue)
2018 && (i <= This->SrcBltCKey.dwColorSpaceHighValue))
2020 table[i][3] = 0x00;
2022 else if(pal->Flags & WINEDDPCAPS_ALPHA)
2024 table[i][3] = pal->palents[i].peFlags;
2026 else
2028 table[i][3] = 0xFF;
2034 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
2035 UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This)
2037 const BYTE *source;
2038 BYTE *dest;
2039 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
2041 switch (convert) {
2042 case NO_CONVERSION:
2044 memcpy(dst, src, pitch * height);
2045 break;
2047 case CONVERT_PALETTED:
2048 case CONVERT_PALETTED_CK:
2050 IWineD3DPaletteImpl* pal = This->palette;
2051 BYTE table[256][4];
2052 unsigned int x, y;
2054 if( pal == NULL) {
2055 /* TODO: If we are a sublevel, try to get the palette from level 0 */
2058 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2060 for (y = 0; y < height; y++)
2062 source = src + pitch * y;
2063 dest = dst + outpitch * y;
2064 /* This is an 1 bpp format, using the width here is fine */
2065 for (x = 0; x < width; x++) {
2066 BYTE color = *source++;
2067 *dest++ = table[color][0];
2068 *dest++ = table[color][1];
2069 *dest++ = table[color][2];
2070 *dest++ = table[color][3];
2074 break;
2076 case CONVERT_CK_565:
2078 /* Converting the 565 format in 5551 packed to emulate color-keying.
2080 Note : in all these conversion, it would be best to average the averaging
2081 pixels to get the color of the pixel that will be color-keyed to
2082 prevent 'color bleeding'. This will be done later on if ever it is
2083 too visible.
2085 Note2: Nvidia documents say that their driver does not support alpha + color keying
2086 on the same surface and disables color keying in such a case
2088 unsigned int x, y;
2089 const WORD *Source;
2090 WORD *Dest;
2092 TRACE("Color keyed 565\n");
2094 for (y = 0; y < height; y++) {
2095 Source = (const WORD *)(src + y * pitch);
2096 Dest = (WORD *) (dst + y * outpitch);
2097 for (x = 0; x < width; x++ ) {
2098 WORD color = *Source++;
2099 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
2100 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2101 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2102 *Dest |= 0x0001;
2104 Dest++;
2108 break;
2110 case CONVERT_CK_5551:
2112 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
2113 unsigned int x, y;
2114 const WORD *Source;
2115 WORD *Dest;
2116 TRACE("Color keyed 5551\n");
2117 for (y = 0; y < height; y++) {
2118 Source = (const WORD *)(src + y * pitch);
2119 Dest = (WORD *) (dst + y * outpitch);
2120 for (x = 0; x < width; x++ ) {
2121 WORD color = *Source++;
2122 *Dest = color;
2123 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2124 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2125 *Dest |= (1 << 15);
2127 else {
2128 *Dest &= ~(1 << 15);
2130 Dest++;
2134 break;
2136 case CONVERT_CK_RGB24:
2138 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
2139 unsigned int x, y;
2140 for (y = 0; y < height; y++)
2142 source = src + pitch * y;
2143 dest = dst + outpitch * y;
2144 for (x = 0; x < width; x++) {
2145 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
2146 DWORD dstcolor = color << 8;
2147 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2148 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2149 dstcolor |= 0xff;
2151 *(DWORD*)dest = dstcolor;
2152 source += 3;
2153 dest += 4;
2157 break;
2159 case CONVERT_RGB32_888:
2161 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
2162 unsigned int x, y;
2163 for (y = 0; y < height; y++)
2165 source = src + pitch * y;
2166 dest = dst + outpitch * y;
2167 for (x = 0; x < width; x++) {
2168 DWORD color = 0xffffff & *(const DWORD*)source;
2169 DWORD dstcolor = color << 8;
2170 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2171 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2172 dstcolor |= 0xff;
2174 *(DWORD*)dest = dstcolor;
2175 source += 4;
2176 dest += 4;
2180 break;
2182 case CONVERT_V8U8:
2184 unsigned int x, y;
2185 const short *Source;
2186 unsigned char *Dest;
2187 for(y = 0; y < height; y++) {
2188 Source = (const short *)(src + y * pitch);
2189 Dest = dst + y * outpitch;
2190 for (x = 0; x < width; x++ ) {
2191 long color = (*Source++);
2192 /* B */ Dest[0] = 0xff;
2193 /* G */ Dest[1] = (color >> 8) + 128; /* V */
2194 /* R */ Dest[2] = (color) + 128; /* U */
2195 Dest += 3;
2198 break;
2201 case CONVERT_V16U16:
2203 unsigned int x, y;
2204 const DWORD *Source;
2205 unsigned short *Dest;
2206 for(y = 0; y < height; y++) {
2207 Source = (const DWORD *)(src + y * pitch);
2208 Dest = (unsigned short *) (dst + y * outpitch);
2209 for (x = 0; x < width; x++ ) {
2210 DWORD color = (*Source++);
2211 /* B */ Dest[0] = 0xffff;
2212 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
2213 /* R */ Dest[2] = (color ) + 32768; /* U */
2214 Dest += 3;
2217 break;
2220 case CONVERT_Q8W8V8U8:
2222 unsigned int x, y;
2223 const DWORD *Source;
2224 unsigned char *Dest;
2225 for(y = 0; y < height; y++) {
2226 Source = (const DWORD *)(src + y * pitch);
2227 Dest = dst + y * outpitch;
2228 for (x = 0; x < width; x++ ) {
2229 long color = (*Source++);
2230 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
2231 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2232 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2233 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
2234 Dest += 4;
2237 break;
2240 case CONVERT_L6V5U5:
2242 unsigned int x, y;
2243 const WORD *Source;
2244 unsigned char *Dest;
2246 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2247 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
2248 * fixed function and shaders without further conversion once the surface is
2249 * loaded
2251 for(y = 0; y < height; y++) {
2252 Source = (const WORD *)(src + y * pitch);
2253 Dest = dst + y * outpitch;
2254 for (x = 0; x < width; x++ ) {
2255 short color = (*Source++);
2256 unsigned char l = ((color >> 10) & 0xfc);
2257 char v = ((color >> 5) & 0x3e);
2258 char u = ((color ) & 0x1f);
2260 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
2261 * and doubles the positive range. Thus shift left only once, gl does the 2nd
2262 * shift. GL reads a signed value and converts it into an unsigned value.
2264 /* M */ Dest[2] = l << 1;
2266 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
2267 * from 5 bit values to 8 bit values.
2269 /* V */ Dest[1] = v << 3;
2270 /* U */ Dest[0] = u << 3;
2271 Dest += 3;
2274 } else {
2275 for(y = 0; y < height; y++) {
2276 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
2277 Source = (const WORD *)(src + y * pitch);
2278 for (x = 0; x < width; x++ ) {
2279 short color = (*Source++);
2280 unsigned char l = ((color >> 10) & 0xfc);
2281 short v = ((color >> 5) & 0x3e);
2282 short u = ((color ) & 0x1f);
2283 short v_conv = v + 16;
2284 short u_conv = u + 16;
2286 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
2287 Dest_s += 1;
2291 break;
2294 case CONVERT_X8L8V8U8:
2296 unsigned int x, y;
2297 const DWORD *Source;
2298 unsigned char *Dest;
2300 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2301 /* This implementation works with the fixed function pipeline and shaders
2302 * without further modification after converting the surface.
2304 for(y = 0; y < height; y++) {
2305 Source = (const DWORD *)(src + y * pitch);
2306 Dest = dst + y * outpitch;
2307 for (x = 0; x < width; x++ ) {
2308 long color = (*Source++);
2309 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
2310 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
2311 /* U */ Dest[0] = (color & 0xff); /* U */
2312 /* I */ Dest[3] = 255; /* X */
2313 Dest += 4;
2316 } else {
2317 /* Doesn't work correctly with the fixed function pipeline, but can work in
2318 * shaders if the shader is adjusted. (There's no use for this format in gl's
2319 * standard fixed function pipeline anyway).
2321 for(y = 0; y < height; y++) {
2322 Source = (const DWORD *)(src + y * pitch);
2323 Dest = dst + y * outpitch;
2324 for (x = 0; x < width; x++ ) {
2325 long color = (*Source++);
2326 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
2327 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2328 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2329 Dest += 4;
2333 break;
2336 case CONVERT_A4L4:
2338 unsigned int x, y;
2339 const unsigned char *Source;
2340 unsigned char *Dest;
2341 for(y = 0; y < height; y++) {
2342 Source = src + y * pitch;
2343 Dest = dst + y * outpitch;
2344 for (x = 0; x < width; x++ ) {
2345 unsigned char color = (*Source++);
2346 /* A */ Dest[1] = (color & 0xf0) << 0;
2347 /* L */ Dest[0] = (color & 0x0f) << 4;
2348 Dest += 2;
2351 break;
2354 case CONVERT_G16R16:
2355 case CONVERT_R16G16F:
2357 unsigned int x, y;
2358 const WORD *Source;
2359 WORD *Dest;
2361 for(y = 0; y < height; y++) {
2362 Source = (const WORD *)(src + y * pitch);
2363 Dest = (WORD *) (dst + y * outpitch);
2364 for (x = 0; x < width; x++ ) {
2365 WORD green = (*Source++);
2366 WORD red = (*Source++);
2367 Dest[0] = green;
2368 Dest[1] = red;
2369 /* Strictly speaking not correct for R16G16F, but it doesn't matter because the
2370 * shader overwrites it anyway
2372 Dest[2] = 0xffff;
2373 Dest += 3;
2376 break;
2379 case CONVERT_R32G32F:
2381 unsigned int x, y;
2382 const float *Source;
2383 float *Dest;
2384 for(y = 0; y < height; y++) {
2385 Source = (const float *)(src + y * pitch);
2386 Dest = (float *) (dst + y * outpitch);
2387 for (x = 0; x < width; x++ ) {
2388 float green = (*Source++);
2389 float red = (*Source++);
2390 Dest[0] = green;
2391 Dest[1] = red;
2392 Dest[2] = 1.0f;
2393 Dest += 3;
2396 break;
2399 case CONVERT_D15S1:
2401 unsigned int x, y;
2403 for (y = 0; y < height; ++y)
2405 const WORD *source = (const WORD *)(src + y * pitch);
2406 DWORD *dest = (DWORD *)(dst + y * outpitch);
2408 for (x = 0; x < width; ++x)
2410 /* The depth data is normalized, so needs to be scaled,
2411 * the stencil data isn't. Scale depth data by
2412 * (2^24-1)/(2^15-1) ~~ (2^9 + 2^-6). */
2413 WORD d15 = source[x] >> 1;
2414 DWORD d24 = (d15 << 9) + (d15 >> 6);
2415 dest[x] = (d24 << 8) | (source[x] & 0x1);
2418 break;
2421 case CONVERT_D24X4S4:
2423 unsigned int x, y;
2425 for (y = 0; y < height; ++y)
2427 const DWORD *source = (const DWORD *)(src + y * pitch);
2428 DWORD *dest = (DWORD *)(dst + y * outpitch);
2430 for (x = 0; x < width; ++x)
2432 /* Just need to clear out the X4 part. */
2433 dest[x] = source[x] & ~0xf0;
2436 break;
2439 case CONVERT_D24FS8:
2441 unsigned int x, y;
2443 for (y = 0; y < height; ++y)
2445 const DWORD *source = (const DWORD *)(src + y * pitch);
2446 float *dest_f = (float *)(dst + y * outpitch);
2447 DWORD *dest_s = (DWORD *)(dst + y * outpitch);
2449 for (x = 0; x < width; ++x)
2451 dest_f[x * 2] = float_24_to_32((source[x] & 0xffffff00) >> 8);
2452 dest_s[x * 2 + 1] = source[x] & 0xff;
2455 break;
2458 default:
2459 ERR("Unsupported conversion type %#x.\n", convert);
2461 return WINED3D_OK;
2464 /* This function is used in case of 8bit paletted textures to upload the palette.
2465 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2466 extensions like ATI_fragment_shaders is possible.
2468 /* Context activation is done by the caller. */
2469 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2470 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2471 BYTE table[256][4];
2472 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2474 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2476 /* Try to use the paletted texture extension */
2477 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2479 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2480 ENTER_GL();
2481 GL_EXTCALL(glColorTableEXT(This->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
2482 LEAVE_GL();
2484 else
2486 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2487 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2488 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2490 ENTER_GL();
2492 /* Create the fragment program if we don't have it */
2493 if(!device->paletteConversionShader)
2495 const char *fragment_palette_conversion =
2496 "!!ARBfp1.0\n"
2497 "TEMP index;\n"
2498 /* { 255/256, 0.5/255*255/256, 0, 0 } */
2499 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n"
2500 /* The alpha-component contains the palette index */
2501 "TEX index, fragment.texcoord[0], texture[0], 2D;\n"
2502 /* Scale the index by 255/256 and add a bias of '0.5' in order to sample in the middle */
2503 "MAD index.a, index.a, constants.x, constants.y;\n"
2504 /* Use the alpha-component as an index in the palette to get the final color */
2505 "TEX result.color, index.a, texture[1], 1D;\n"
2506 "END";
2508 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2509 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2510 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2511 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), fragment_palette_conversion));
2512 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2515 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2516 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2518 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2519 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2521 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2522 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2523 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2524 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2526 /* Switch back to unit 0 in which the 2D texture will be stored. */
2527 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2529 /* Rebind the texture because it isn't bound anymore */
2530 glBindTexture(This->texture_target, This->texture_name);
2532 LEAVE_GL();
2536 BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2537 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2539 if (This->palette || (This->resource.format_desc->format != WINED3DFMT_P8
2540 && This->resource.format_desc->format != WINED3DFMT_A8P8))
2542 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2543 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2545 return FALSE;
2548 if(This->palette9) {
2549 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2550 return FALSE;
2552 } else {
2553 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2555 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2556 return TRUE;
2559 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2560 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2561 DWORD flag = srgb_mode ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2563 if (!(This->Flags & flag)) {
2564 TRACE("Reloading because surface is dirty\n");
2565 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2566 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2567 /* Reload: vice versa OR */
2568 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2569 /* Also reload: Color key is active AND the color key has changed */
2570 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2571 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2572 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2573 TRACE("Reloading because of color keying\n");
2574 /* To perform the color key conversion we need a sysmem copy of
2575 * the surface. Make sure we have it
2578 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2579 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2580 /* TODO: This is not necessarily needed with hw palettized texture support */
2581 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2582 } else {
2583 TRACE("surface is already in texture\n");
2584 return WINED3D_OK;
2587 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2588 * These resources are not bound by device size or format restrictions. Because of this,
2589 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2590 * However, these resources can always be created, locked, and copied.
2592 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2594 FIXME("(%p) Operation not supported for scratch textures\n",This);
2595 return WINED3DERR_INVALIDCALL;
2598 IWineD3DSurface_LoadLocation(iface, flag, NULL /* no partial locking for textures yet */);
2600 #if 0
2602 static unsigned int gen = 0;
2603 char buffer[4096];
2604 ++gen;
2605 if ((gen % 10) == 0) {
2606 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm",
2607 This, This->texture_target, This->texture_level, gen);
2608 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2611 * debugging crash code
2612 if (gen == 250) {
2613 void** test = NULL;
2614 *test = 0;
2618 #endif
2620 if (!(This->Flags & SFLAG_DONOTFREE)) {
2621 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2622 This->resource.allocatedMemory = NULL;
2623 This->resource.heapMemory = NULL;
2624 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2627 return WINED3D_OK;
2630 /* Context activation is done by the caller. */
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->texture_name_srgb : &This->texture_name;
2647 if(!device->isInDraw) {
2648 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2651 ENTER_GL();
2653 if (!This->texture_level)
2655 if (!*name) {
2656 glGenTextures(1, name);
2657 checkGLcall("glGenTextures");
2658 TRACE("Surface %p given name %d\n", This, *name);
2660 glBindTexture(This->texture_target, *name);
2661 checkGLcall("glBindTexture");
2662 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2663 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2664 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2665 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2666 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2667 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2668 glTexParameteri(This->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2669 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2670 glTexParameteri(This->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2671 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2673 /* This is where we should be reducing the amount of GLMemoryUsed */
2674 } else if (*name) {
2675 /* Mipmap surfaces should have a base texture container */
2676 ERR("Mipmap surface has a glTexture bound to it!\n");
2679 glBindTexture(This->texture_target, *name);
2680 checkGLcall("glBindTexture");
2682 LEAVE_GL();
2684 return;
2687 #include <errno.h>
2688 #include <stdio.h>
2689 static HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename)
2691 FILE* f = NULL;
2692 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2693 char *allocatedMemory;
2694 const char *textureRow;
2695 IWineD3DSwapChain *swapChain = NULL;
2696 int width, height, i, y;
2697 GLuint tmpTexture = 0;
2698 DWORD color;
2699 /*FIXME:
2700 Textures may not be stored in ->allocatedgMemory and a GlTexture
2701 so we should lock the surface before saving a snapshot, or at least check that
2703 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2704 by calling GetTexImage and in compressed form by calling
2705 GetCompressedTexImageARB. Queried compressed images can be saved and
2706 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2707 texture images do not need to be processed by the GL and should
2708 significantly improve texture loading performance relative to uncompressed
2709 images. */
2711 /* Setup the width and height to be the internal texture width and height. */
2712 width = This->pow2Width;
2713 height = This->pow2Height;
2714 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2715 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2717 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2718 /* if were not a real texture then read the back buffer into a real texture */
2719 /* we don't want to interfere with the back buffer so read the data into a temporary
2720 * texture and then save the data out of the temporary texture
2722 GLint prevRead;
2723 ENTER_GL();
2724 TRACE("(%p) Reading render target into texture\n", This);
2726 glGenTextures(1, &tmpTexture);
2727 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2729 glTexImage2D(GL_TEXTURE_2D,
2731 GL_RGBA,
2732 width,
2733 height,
2734 0/*border*/,
2735 GL_RGBA,
2736 GL_UNSIGNED_INT_8_8_8_8_REV,
2737 NULL);
2739 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2740 checkGLcall("glGetIntegerv");
2741 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2742 checkGLcall("glReadBuffer");
2743 glCopyTexImage2D(GL_TEXTURE_2D,
2745 GL_RGBA,
2748 width,
2749 height,
2752 checkGLcall("glCopyTexImage2D");
2753 glReadBuffer(prevRead);
2754 LEAVE_GL();
2756 } else { /* bind the real texture, and make sure it up to date */
2757 surface_internal_preload(iface, SRGB_RGB);
2758 surface_bind_and_dirtify(This, FALSE);
2760 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2761 ENTER_GL();
2762 FIXME("Saving texture level %d width %d height %d\n", This->texture_level, width, height);
2763 glGetTexImage(GL_TEXTURE_2D, This->texture_level, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, allocatedMemory);
2764 checkGLcall("glTexImage2D");
2765 if (tmpTexture) {
2766 glBindTexture(GL_TEXTURE_2D, 0);
2767 glDeleteTextures(1, &tmpTexture);
2769 LEAVE_GL();
2771 f = fopen(filename, "w+");
2772 if (NULL == f) {
2773 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2774 return WINED3DERR_INVALIDCALL;
2776 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2777 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format_desc->format));
2778 /* TGA header */
2779 fputc(0,f);
2780 fputc(0,f);
2781 fputc(2,f);
2782 fputc(0,f);
2783 fputc(0,f);
2784 fputc(0,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 /* short width*/
2792 fwrite(&width,2,1,f);
2793 /* short height */
2794 fwrite(&height,2,1,f);
2795 /* format rgba */
2796 fputc(0x20,f);
2797 fputc(0x28,f);
2798 /* raw data */
2799 /* 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 */
2800 if(swapChain)
2801 textureRow = allocatedMemory + (width * (height - 1) *4);
2802 else
2803 textureRow = allocatedMemory;
2804 for (y = 0 ; y < height; y++) {
2805 for (i = 0; i < width; i++) {
2806 color = *((const DWORD*)textureRow);
2807 fputc((color >> 16) & 0xFF, f); /* B */
2808 fputc((color >> 8) & 0xFF, f); /* G */
2809 fputc((color >> 0) & 0xFF, f); /* R */
2810 fputc((color >> 24) & 0xFF, f); /* A */
2811 textureRow += 4;
2813 /* take two rows of the pointer to the texture memory */
2814 if(swapChain)
2815 (textureRow-= width << 3);
2818 TRACE("Closing file\n");
2819 fclose(f);
2821 if(swapChain) {
2822 IWineD3DSwapChain_Release(swapChain);
2824 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2825 return WINED3D_OK;
2828 static HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2829 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2830 HRESULT hr;
2832 TRACE("(%p) : Calling base function first\n", This);
2833 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2834 if(SUCCEEDED(hr)) {
2835 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2836 TRACE("(%p) : glFormat %d, glFormatInternal %d, glType %d\n", This, This->resource.format_desc->glFormat,
2837 This->resource.format_desc->glInternal, This->resource.format_desc->glType);
2839 return hr;
2842 static HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2843 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2845 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2846 WARN("Surface is locked or the HDC is in use\n");
2847 return WINED3DERR_INVALIDCALL;
2850 if(Mem && Mem != This->resource.allocatedMemory) {
2851 void *release = NULL;
2853 /* Do I have to copy the old surface content? */
2854 if(This->Flags & SFLAG_DIBSECTION) {
2855 /* Release the DC. No need to hold the critical section for the update
2856 * Thread because this thread runs only on front buffers, but this method
2857 * fails for render targets in the check above.
2859 SelectObject(This->hDC, This->dib.holdbitmap);
2860 DeleteDC(This->hDC);
2861 /* Release the DIB section */
2862 DeleteObject(This->dib.DIBsection);
2863 This->dib.bitmap_data = NULL;
2864 This->resource.allocatedMemory = NULL;
2865 This->hDC = NULL;
2866 This->Flags &= ~SFLAG_DIBSECTION;
2867 } else if(!(This->Flags & SFLAG_USERPTR)) {
2868 release = This->resource.heapMemory;
2869 This->resource.heapMemory = NULL;
2871 This->resource.allocatedMemory = Mem;
2872 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2874 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2875 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2877 /* For client textures opengl has to be notified */
2878 if(This->Flags & SFLAG_CLIENT) {
2879 DWORD oldFlags = This->Flags;
2880 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2881 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2882 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2883 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2886 /* Now free the old memory if any */
2887 HeapFree(GetProcessHeap(), 0, release);
2888 } else if(This->Flags & SFLAG_USERPTR) {
2889 /* LockRect and GetDC will re-create the dib section and allocated memory */
2890 This->resource.allocatedMemory = NULL;
2891 /* HeapMemory should be NULL already */
2892 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2893 This->Flags &= ~SFLAG_USERPTR;
2895 if(This->Flags & SFLAG_CLIENT) {
2896 DWORD oldFlags = This->Flags;
2897 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2898 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2899 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2900 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2903 return WINED3D_OK;
2906 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
2908 /* Flip the surface contents */
2909 /* Flip the DC */
2911 HDC tmp;
2912 tmp = front->hDC;
2913 front->hDC = back->hDC;
2914 back->hDC = tmp;
2917 /* Flip the DIBsection */
2919 HBITMAP tmp;
2920 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
2921 tmp = front->dib.DIBsection;
2922 front->dib.DIBsection = back->dib.DIBsection;
2923 back->dib.DIBsection = tmp;
2925 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
2926 else front->Flags &= ~SFLAG_DIBSECTION;
2927 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
2928 else back->Flags &= ~SFLAG_DIBSECTION;
2931 /* Flip the surface data */
2933 void* tmp;
2935 tmp = front->dib.bitmap_data;
2936 front->dib.bitmap_data = back->dib.bitmap_data;
2937 back->dib.bitmap_data = tmp;
2939 tmp = front->resource.allocatedMemory;
2940 front->resource.allocatedMemory = back->resource.allocatedMemory;
2941 back->resource.allocatedMemory = tmp;
2943 tmp = front->resource.heapMemory;
2944 front->resource.heapMemory = back->resource.heapMemory;
2945 back->resource.heapMemory = tmp;
2948 /* Flip the PBO */
2950 GLuint tmp_pbo = front->pbo;
2951 front->pbo = back->pbo;
2952 back->pbo = tmp_pbo;
2955 /* client_memory should not be different, but just in case */
2957 BOOL tmp;
2958 tmp = front->dib.client_memory;
2959 front->dib.client_memory = back->dib.client_memory;
2960 back->dib.client_memory = tmp;
2963 /* Flip the opengl texture */
2965 GLuint tmp;
2967 tmp = back->texture_name;
2968 back->texture_name = front->texture_name;
2969 front->texture_name = tmp;
2971 tmp = back->texture_name_srgb;
2972 back->texture_name_srgb = front->texture_name_srgb;
2973 front->texture_name_srgb = tmp;
2977 DWORD tmp_flags = back->Flags;
2978 back->Flags = front->Flags;
2979 front->Flags = tmp_flags;
2983 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2984 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2985 IWineD3DSwapChainImpl *swapchain = NULL;
2986 HRESULT hr;
2987 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2989 /* Flipping is only supported on RenderTargets and overlays*/
2990 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
2991 WARN("Tried to flip a non-render target, non-overlay surface\n");
2992 return WINEDDERR_NOTFLIPPABLE;
2995 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
2996 flip_surface(This, (IWineD3DSurfaceImpl *) override);
2998 /* Update the overlay if it is visible */
2999 if(This->overlay_dest) {
3000 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
3001 } else {
3002 return WINED3D_OK;
3006 if(override) {
3007 /* DDraw sets this for the X11 surfaces, so don't confuse the user
3008 * FIXME("(%p) Target override is not supported by now\n", This);
3009 * Additionally, it isn't really possible to support triple-buffering
3010 * properly on opengl at all
3014 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
3015 if(!swapchain) {
3016 ERR("Flipped surface is not on a swapchain\n");
3017 return WINEDDERR_NOTFLIPPABLE;
3020 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
3021 * and only d3d8 and d3d9 apps specify the presentation interval
3023 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
3024 /* Most common case first to avoid wasting time on all the other cases */
3025 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
3026 } else if(Flags & WINEDDFLIP_NOVSYNC) {
3027 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3028 } else if(Flags & WINEDDFLIP_INTERVAL2) {
3029 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
3030 } else if(Flags & WINEDDFLIP_INTERVAL3) {
3031 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
3032 } else {
3033 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
3036 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
3037 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
3038 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
3039 return hr;
3042 /* Does a direct frame buffer -> texture copy. Stretching is done
3043 * with single pixel copy calls
3045 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3046 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
3047 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
3049 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3050 float xrel, yrel;
3051 UINT row;
3052 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3055 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
3056 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3057 ENTER_GL();
3059 /* Bind the target texture */
3060 glBindTexture(This->texture_target, This->texture_name);
3061 checkGLcall("glBindTexture");
3062 if(!swapchain) {
3063 TRACE("Reading from an offscreen target\n");
3064 upsidedown = !upsidedown;
3065 glReadBuffer(myDevice->offscreenBuffer);
3066 } else {
3067 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
3068 glReadBuffer(buffer);
3070 checkGLcall("glReadBuffer");
3072 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
3073 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
3075 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3077 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3079 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
3080 ERR("Texture filtering not supported in direct blit\n");
3083 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
3084 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3086 ERR("Texture filtering not supported in direct blit\n");
3089 if (upsidedown
3090 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3091 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3093 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
3095 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3096 drect->x1 /*xoffset */, drect->y1 /* y offset */,
3097 srect->x1, Src->currentDesc.Height - srect->y2,
3098 drect->x2 - drect->x1, drect->y2 - drect->y1);
3099 } else {
3100 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
3101 /* I have to process this row by row to swap the image,
3102 * otherwise it would be upside down, so stretching in y direction
3103 * doesn't cost extra time
3105 * However, stretching in x direction can be avoided if not necessary
3107 for(row = drect->y1; row < drect->y2; row++) {
3108 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3110 /* Well, that stuff works, but it's very slow.
3111 * find a better way instead
3113 UINT col;
3115 for(col = drect->x1; col < drect->x2; col++) {
3116 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3117 drect->x1 + col /* x offset */, row /* y offset */,
3118 srect->x1 + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3120 } else {
3121 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3122 drect->x1 /* x offset */, row /* y offset */,
3123 srect->x1, yoffset - (int) (row * yrel), drect->x2-drect->x1, 1);
3127 checkGLcall("glCopyTexSubImage2D");
3129 LEAVE_GL();
3132 /* Uses the hardware to stretch and flip the image */
3133 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3134 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
3135 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
3137 GLuint src, backup = 0;
3138 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3139 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3140 float left, right, top, bottom; /* Texture coordinates */
3141 UINT fbwidth = Src->currentDesc.Width;
3142 UINT fbheight = Src->currentDesc.Height;
3143 GLenum drawBuffer = GL_BACK;
3144 GLenum texture_target;
3145 BOOL noBackBufferBackup;
3147 TRACE("Using hwstretch blit\n");
3148 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3149 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
3150 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3152 noBackBufferBackup = !swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3153 if (!noBackBufferBackup && !Src->texture_name)
3155 /* Get it a description */
3156 surface_internal_preload(SrcSurface, SRGB_RGB);
3158 ENTER_GL();
3160 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3161 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3163 if(myDevice->activeContext->aux_buffers >= 2) {
3164 /* Got more than one aux buffer? Use the 2nd aux buffer */
3165 drawBuffer = GL_AUX1;
3166 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && myDevice->activeContext->aux_buffers >= 1) {
3167 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3168 drawBuffer = GL_AUX0;
3171 if(noBackBufferBackup) {
3172 glGenTextures(1, &backup);
3173 checkGLcall("glGenTextures");
3174 glBindTexture(GL_TEXTURE_2D, backup);
3175 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3176 texture_target = GL_TEXTURE_2D;
3177 } else {
3178 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3179 * we are reading from the back buffer, the backup can be used as source texture
3181 texture_target = Src->texture_target;
3182 glBindTexture(texture_target, Src->texture_name);
3183 checkGLcall("glBindTexture(texture_target, Src->texture_name)");
3184 glEnable(texture_target);
3185 checkGLcall("glEnable(texture_target)");
3187 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3188 Src->Flags &= ~SFLAG_INTEXTURE;
3191 if(swapchain) {
3192 glReadBuffer(surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain));
3193 } else {
3194 TRACE("Reading from an offscreen target\n");
3195 upsidedown = !upsidedown;
3196 glReadBuffer(myDevice->offscreenBuffer);
3199 /* TODO: Only back up the part that will be overwritten */
3200 glCopyTexSubImage2D(texture_target, 0,
3201 0, 0 /* read offsets */,
3202 0, 0,
3203 fbwidth,
3204 fbheight);
3206 checkGLcall("glCopyTexSubImage2D");
3208 /* No issue with overriding these - the sampler is dirty due to blit usage */
3209 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3210 magLookup[Filter - WINED3DTEXF_NONE]);
3211 checkGLcall("glTexParameteri");
3212 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3213 minMipLookup[Filter].mip[WINED3DTEXF_NONE]);
3214 checkGLcall("glTexParameteri");
3216 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
3217 src = backup ? backup : Src->texture_name;
3218 } else {
3219 glReadBuffer(GL_FRONT);
3220 checkGLcall("glReadBuffer(GL_FRONT)");
3222 glGenTextures(1, &src);
3223 checkGLcall("glGenTextures(1, &src)");
3224 glBindTexture(GL_TEXTURE_2D, src);
3225 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
3227 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
3228 * out for power of 2 sizes
3230 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
3231 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3232 checkGLcall("glTexImage2D");
3233 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
3234 0, 0 /* read offsets */,
3235 0, 0,
3236 fbwidth,
3237 fbheight);
3239 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3240 checkGLcall("glTexParameteri");
3241 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3242 checkGLcall("glTexParameteri");
3244 glReadBuffer(GL_BACK);
3245 checkGLcall("glReadBuffer(GL_BACK)");
3247 if(texture_target != GL_TEXTURE_2D) {
3248 glDisable(texture_target);
3249 glEnable(GL_TEXTURE_2D);
3250 texture_target = GL_TEXTURE_2D;
3253 checkGLcall("glEnd and previous");
3255 left = srect->x1;
3256 right = srect->x2;
3258 if(upsidedown) {
3259 top = Src->currentDesc.Height - srect->y1;
3260 bottom = Src->currentDesc.Height - srect->y2;
3261 } else {
3262 top = Src->currentDesc.Height - srect->y2;
3263 bottom = Src->currentDesc.Height - srect->y1;
3266 if(Src->Flags & SFLAG_NORMCOORD) {
3267 left /= Src->pow2Width;
3268 right /= Src->pow2Width;
3269 top /= Src->pow2Height;
3270 bottom /= Src->pow2Height;
3273 /* draw the source texture stretched and upside down. The correct surface is bound already */
3274 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3275 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3277 glDrawBuffer(drawBuffer);
3278 glReadBuffer(drawBuffer);
3280 glBegin(GL_QUADS);
3281 /* bottom left */
3282 glTexCoord2f(left, bottom);
3283 glVertex2i(0, fbheight);
3285 /* top left */
3286 glTexCoord2f(left, top);
3287 glVertex2i(0, fbheight - drect->y2 - drect->y1);
3289 /* top right */
3290 glTexCoord2f(right, top);
3291 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
3293 /* bottom right */
3294 glTexCoord2f(right, bottom);
3295 glVertex2i(drect->x2 - drect->x1, fbheight);
3296 glEnd();
3297 checkGLcall("glEnd and previous");
3299 if (texture_target != This->texture_target)
3301 glDisable(texture_target);
3302 glEnable(This->texture_target);
3303 texture_target = This->texture_target;
3306 /* Now read the stretched and upside down image into the destination texture */
3307 glBindTexture(texture_target, This->texture_name);
3308 checkGLcall("glBindTexture");
3309 glCopyTexSubImage2D(texture_target,
3311 drect->x1, drect->y1, /* xoffset, yoffset */
3312 0, 0, /* We blitted the image to the origin */
3313 drect->x2 - drect->x1, drect->y2 - drect->y1);
3314 checkGLcall("glCopyTexSubImage2D");
3316 if(drawBuffer == GL_BACK) {
3317 /* Write the back buffer backup back */
3318 if(backup) {
3319 if(texture_target != GL_TEXTURE_2D) {
3320 glDisable(texture_target);
3321 glEnable(GL_TEXTURE_2D);
3322 texture_target = GL_TEXTURE_2D;
3324 glBindTexture(GL_TEXTURE_2D, backup);
3325 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3326 } else {
3327 if (texture_target != Src->texture_target)
3329 glDisable(texture_target);
3330 glEnable(Src->texture_target);
3331 texture_target = Src->texture_target;
3333 glBindTexture(Src->texture_target, Src->texture_name);
3334 checkGLcall("glBindTexture(Src->texture_target, Src->texture_name)");
3337 glBegin(GL_QUADS);
3338 /* top left */
3339 glTexCoord2f(0.0f, (float)fbheight / (float)Src->pow2Height);
3340 glVertex2i(0, 0);
3342 /* bottom left */
3343 glTexCoord2f(0.0f, 0.0f);
3344 glVertex2i(0, fbheight);
3346 /* bottom right */
3347 glTexCoord2f((float)fbwidth / (float)Src->pow2Width, 0.0f);
3348 glVertex2i(fbwidth, Src->currentDesc.Height);
3350 /* top right */
3351 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
3352 glVertex2i(fbwidth, 0);
3353 glEnd();
3354 } else {
3355 /* Restore the old draw buffer */
3356 glDrawBuffer(GL_BACK);
3358 glDisable(texture_target);
3359 checkGLcall("glDisable(texture_target)");
3361 /* Cleanup */
3362 if (src != Src->texture_name && src != backup)
3364 glDeleteTextures(1, &src);
3365 checkGLcall("glDeleteTextures(1, &src)");
3367 if(backup) {
3368 glDeleteTextures(1, &backup);
3369 checkGLcall("glDeleteTextures(1, &backup)");
3372 LEAVE_GL();
3375 /* Not called from the VTable */
3376 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3377 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx,
3378 WINED3DTEXTUREFILTERTYPE Filter)
3380 WINED3DRECT rect;
3381 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3382 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3383 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3385 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3387 /* Get the swapchain. One of the surfaces has to be a primary surface */
3388 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3389 WARN("Destination is in sysmem, rejecting gl blt\n");
3390 return WINED3DERR_INVALIDCALL;
3392 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3393 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3394 if(Src) {
3395 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3396 WARN("Src is in sysmem, rejecting gl blt\n");
3397 return WINED3DERR_INVALIDCALL;
3399 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3400 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3403 /* Early sort out of cases where no render target is used */
3404 if(!dstSwapchain && !srcSwapchain &&
3405 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3406 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3407 return WINED3DERR_INVALIDCALL;
3410 /* No destination color keying supported */
3411 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3412 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3413 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3414 return WINED3DERR_INVALIDCALL;
3417 if (DestRect) {
3418 rect.x1 = DestRect->left;
3419 rect.y1 = DestRect->top;
3420 rect.x2 = DestRect->right;
3421 rect.y2 = DestRect->bottom;
3422 } else {
3423 rect.x1 = 0;
3424 rect.y1 = 0;
3425 rect.x2 = This->currentDesc.Width;
3426 rect.y2 = This->currentDesc.Height;
3429 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3430 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3431 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3432 /* Half-life does a Blt from the back buffer to the front buffer,
3433 * Full surface size, no flags... Use present instead
3435 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3438 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3439 while(1)
3441 RECT mySrcRect;
3442 TRACE("Looking if a Present can be done...\n");
3443 /* Source Rectangle must be full surface */
3444 if( SrcRect ) {
3445 if(SrcRect->left != 0 || SrcRect->top != 0 ||
3446 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
3447 TRACE("No, Source rectangle doesn't match\n");
3448 break;
3451 mySrcRect.left = 0;
3452 mySrcRect.top = 0;
3453 mySrcRect.right = Src->currentDesc.Width;
3454 mySrcRect.bottom = Src->currentDesc.Height;
3456 /* No stretching may occur */
3457 if(mySrcRect.right != rect.x2 - rect.x1 ||
3458 mySrcRect.bottom != rect.y2 - rect.y1) {
3459 TRACE("No, stretching is done\n");
3460 break;
3463 /* Destination must be full surface or match the clipping rectangle */
3464 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3466 RECT cliprect;
3467 POINT pos[2];
3468 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3469 pos[0].x = rect.x1;
3470 pos[0].y = rect.y1;
3471 pos[1].x = rect.x2;
3472 pos[1].y = rect.y2;
3473 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3474 pos, 2);
3476 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3477 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3479 TRACE("No, dest rectangle doesn't match(clipper)\n");
3480 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3481 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3482 break;
3485 else
3487 if(rect.x1 != 0 || rect.y1 != 0 ||
3488 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3489 TRACE("No, dest rectangle doesn't match(surface size)\n");
3490 break;
3494 TRACE("Yes\n");
3496 /* These flags are unimportant for the flag check, remove them */
3497 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3498 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3500 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3501 * take very long, while a flip is fast.
3502 * This applies to Half-Life, which does such Blts every time it finished
3503 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3504 * menu. This is also used by all apps when they do windowed rendering
3506 * The problem is that flipping is not really the same as copying. After a
3507 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3508 * untouched. Therefore it's necessary to override the swap effect
3509 * and to set it back after the flip.
3511 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3512 * testcases.
3515 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3516 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3518 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3519 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3521 dstSwapchain->presentParms.SwapEffect = orig_swap;
3523 return WINED3D_OK;
3525 break;
3528 TRACE("Unsupported blit between buffers on the same swapchain\n");
3529 return WINED3DERR_INVALIDCALL;
3530 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3531 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3532 return WINED3DERR_INVALIDCALL;
3533 } else if(dstSwapchain && srcSwapchain) {
3534 FIXME("Implement hardware blit between two different swapchains\n");
3535 return WINED3DERR_INVALIDCALL;
3536 } else if(dstSwapchain) {
3537 if(SrcSurface == myDevice->render_targets[0]) {
3538 TRACE("Blit from active render target to a swapchain\n");
3539 /* Handled with regular texture -> swapchain blit */
3541 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3542 FIXME("Implement blit from a swapchain to the active render target\n");
3543 return WINED3DERR_INVALIDCALL;
3546 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3547 /* Blit from render target to texture */
3548 WINED3DRECT srect;
3549 BOOL upsideDown, stretchx;
3550 BOOL paletteOverride = FALSE;
3552 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3553 TRACE("Color keying not supported by frame buffer to texture blit\n");
3554 return WINED3DERR_INVALIDCALL;
3555 /* Destination color key is checked above */
3558 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3559 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3561 if(SrcRect) {
3562 if(SrcRect->top < SrcRect->bottom) {
3563 srect.y1 = SrcRect->top;
3564 srect.y2 = SrcRect->bottom;
3565 upsideDown = FALSE;
3566 } else {
3567 srect.y1 = SrcRect->bottom;
3568 srect.y2 = SrcRect->top;
3569 upsideDown = TRUE;
3571 srect.x1 = SrcRect->left;
3572 srect.x2 = SrcRect->right;
3573 } else {
3574 srect.x1 = 0;
3575 srect.y1 = 0;
3576 srect.x2 = Src->currentDesc.Width;
3577 srect.y2 = Src->currentDesc.Height;
3578 upsideDown = FALSE;
3580 if(rect.x1 > rect.x2) {
3581 UINT tmp = rect.x2;
3582 rect.x2 = rect.x1;
3583 rect.x1 = tmp;
3584 upsideDown = !upsideDown;
3587 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3588 stretchx = TRUE;
3589 } else {
3590 stretchx = FALSE;
3593 /* When blitting from a render target a texture, the texture isn't required to have a palette.
3594 * In this case grab the palette from the render target. */
3595 if ((This->resource.format_desc->format == WINED3DFMT_P8) && (This->palette == NULL))
3597 paletteOverride = TRUE;
3598 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3599 This->palette = Src->palette;
3602 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3603 * flip the image nor scale it.
3605 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3606 * -> If the app wants a image width an unscaled width, copy it line per line
3607 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3608 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3609 * back buffer. This is slower than reading line per line, thus not used for flipping
3610 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3611 * pixel by pixel
3613 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3614 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3615 * backends.
3617 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)
3618 && surface_can_stretch_rect(Src, This))
3620 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3621 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3622 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3623 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3624 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3625 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3626 } else {
3627 TRACE("Using hardware stretching to flip / stretch the texture\n");
3628 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3631 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3632 if(paletteOverride)
3633 This->palette = NULL;
3635 if(!(This->Flags & SFLAG_DONOTFREE)) {
3636 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3637 This->resource.allocatedMemory = NULL;
3638 This->resource.heapMemory = NULL;
3639 } else {
3640 This->Flags &= ~SFLAG_INSYSMEM;
3642 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3643 * path is never entered
3645 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3647 return WINED3D_OK;
3648 } else if(Src) {
3649 /* Blit from offscreen surface to render target */
3650 float glTexCoord[4];
3651 DWORD oldCKeyFlags = Src->CKeyFlags;
3652 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3653 RECT SourceRectangle;
3654 BOOL paletteOverride = FALSE;
3656 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3658 if(SrcRect) {
3659 SourceRectangle.left = SrcRect->left;
3660 SourceRectangle.right = SrcRect->right;
3661 SourceRectangle.top = SrcRect->top;
3662 SourceRectangle.bottom = SrcRect->bottom;
3663 } else {
3664 SourceRectangle.left = 0;
3665 SourceRectangle.right = Src->currentDesc.Width;
3666 SourceRectangle.top = 0;
3667 SourceRectangle.bottom = Src->currentDesc.Height;
3670 /* When blitting from an offscreen surface to a rendertarget, the source
3671 * surface is not required to have a palette. Our rendering / conversion
3672 * code further down the road retrieves the palette from the surface, so
3673 * it must have a palette set. */
3674 if ((Src->resource.format_desc->format == WINED3DFMT_P8) && (Src->palette == NULL))
3676 paletteOverride = TRUE;
3677 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3678 Src->palette = This->palette;
3681 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)
3682 && !(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3683 && surface_can_stretch_rect(Src, This))
3685 TRACE("Using stretch_rect_fbo\n");
3686 /* The source is always a texture, but never the currently active render target, and the texture
3687 * contents are never upside down
3689 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3690 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3692 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3693 if(paletteOverride)
3694 Src->palette = NULL;
3695 return WINED3D_OK;
3698 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3699 /* Fall back to software */
3700 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3701 SourceRectangle.left, SourceRectangle.top,
3702 SourceRectangle.right, SourceRectangle.bottom);
3703 return WINED3DERR_INVALIDCALL;
3706 /* Color keying: Check if we have to do a color keyed blt,
3707 * and if not check if a color key is activated.
3709 * Just modify the color keying parameters in the surface and restore them afterwards
3710 * The surface keeps track of the color key last used to load the opengl surface.
3711 * PreLoad will catch the change to the flags and color key and reload if necessary.
3713 if(Flags & WINEDDBLT_KEYSRC) {
3714 /* Use color key from surface */
3715 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3716 /* Use color key from DDBltFx */
3717 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3718 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3719 } else {
3720 /* Do not use color key */
3721 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3724 /* Now load the surface */
3725 surface_internal_preload((IWineD3DSurface *) Src, SRGB_RGB);
3727 /* Activate the destination context, set it up for blitting */
3728 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3730 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3731 * while OpenGL coordinates are window relative.
3732 * Also beware of the origin difference(top left vs bottom left).
3733 * Also beware that the front buffer's surface size is screen width x screen height,
3734 * whereas the real gl drawable size is the size of the window.
3736 if (dstSwapchain && (IWineD3DSurface *)This == dstSwapchain->frontBuffer) {
3737 RECT windowsize;
3738 POINT offset = {0,0};
3739 UINT h;
3740 ClientToScreen(dstSwapchain->win_handle, &offset);
3741 GetClientRect(dstSwapchain->win_handle, &windowsize);
3742 h = windowsize.bottom - windowsize.top;
3743 rect.x1 -= offset.x; rect.x2 -=offset.x;
3744 rect.y1 -= offset.y; rect.y2 -=offset.y;
3745 rect.y1 += This->currentDesc.Height - h; rect.y2 += This->currentDesc.Height - h;
3748 if (!is_identity_fixup(This->resource.format_desc->color_fixup))
3750 FIXME("Destination format %s has a fixup, this is not supported.\n",
3751 debug_d3dformat(This->resource.format_desc->format));
3752 dump_color_fixup_desc(This->resource.format_desc->color_fixup);
3755 if (!myDevice->blitter->color_fixup_supported(Src->resource.format_desc->color_fixup))
3757 FIXME("Source format %s has an unsupported fixup:\n",
3758 debug_d3dformat(Src->resource.format_desc->format));
3759 dump_color_fixup_desc(Src->resource.format_desc->color_fixup);
3762 myDevice->blitter->set_shader((IWineD3DDevice *) myDevice, Src->resource.format_desc,
3763 Src->texture_target, Src->pow2Width, Src->pow2Height);
3765 ENTER_GL();
3767 /* Bind the texture */
3768 glBindTexture(Src->texture_target, Src->texture_name);
3769 checkGLcall("glBindTexture");
3771 /* Filtering for StretchRect */
3772 glTexParameteri(Src->texture_target, GL_TEXTURE_MAG_FILTER, magLookup[Filter - WINED3DTEXF_NONE]);
3773 checkGLcall("glTexParameteri");
3774 glTexParameteri(Src->texture_target, GL_TEXTURE_MIN_FILTER, minMipLookup[Filter].mip[WINED3DTEXF_NONE]);
3775 checkGLcall("glTexParameteri");
3776 glTexParameteri(Src->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3777 glTexParameteri(Src->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3778 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3779 checkGLcall("glTexEnvi");
3781 /* This is for color keying */
3782 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3783 glEnable(GL_ALPHA_TEST);
3784 checkGLcall("glEnable GL_ALPHA_TEST");
3786 /* When the primary render target uses P8, the alpha component contains the palette index.
3787 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3788 * should be masked away have alpha set to 0. */
3789 if(primary_render_target_is_p8(myDevice))
3790 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
3791 else
3792 glAlphaFunc(GL_NOTEQUAL, 0.0f);
3793 checkGLcall("glAlphaFunc");
3794 } else {
3795 glDisable(GL_ALPHA_TEST);
3796 checkGLcall("glDisable GL_ALPHA_TEST");
3799 /* Draw a textured quad
3801 glBegin(GL_QUADS);
3803 glColor3f(1.0f, 1.0f, 1.0f);
3804 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3805 glVertex3f(rect.x1, rect.y1, 0.0f);
3807 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3808 glVertex3f(rect.x1, rect.y2, 0.0f);
3810 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3811 glVertex3f(rect.x2, rect.y2, 0.0f);
3813 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3814 glVertex3f(rect.x2, rect.y1, 0.0f);
3816 glEnd();
3817 checkGLcall("glEnd");
3819 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3820 glDisable(GL_ALPHA_TEST);
3821 checkGLcall("glDisable(GL_ALPHA_TEST)");
3824 glBindTexture(Src->texture_target, 0);
3825 checkGLcall("glBindTexture(Src->texture_target, 0)");
3827 /* Restore the color key parameters */
3828 Src->CKeyFlags = oldCKeyFlags;
3829 Src->SrcBltCKey = oldBltCKey;
3831 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3832 if(paletteOverride)
3833 Src->palette = NULL;
3835 LEAVE_GL();
3837 /* Leave the opengl state valid for blitting */
3838 myDevice->blitter->unset_shader((IWineD3DDevice *) myDevice);
3840 /* Flush in case the drawable is used by multiple GL contexts */
3841 if(dstSwapchain && (This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer || dstSwapchain->num_contexts >= 2))
3842 glFlush();
3844 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3845 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3846 * is outdated now
3848 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3850 return WINED3D_OK;
3851 } else {
3852 /* Source-Less Blit to render target */
3853 if (Flags & WINEDDBLT_COLORFILL) {
3854 /* This is easy to handle for the D3D Device... */
3855 DWORD color;
3857 TRACE("Colorfill\n");
3859 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3860 must be true if we are here */
3861 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3862 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3863 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3864 TRACE("Surface is higher back buffer, falling back to software\n");
3865 return WINED3DERR_INVALIDCALL;
3868 /* The color as given in the Blt function is in the format of the frame-buffer...
3869 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3871 if (This->resource.format_desc->format == WINED3DFMT_P8)
3873 DWORD alpha;
3875 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3876 else alpha = 0xFF000000;
3878 if (This->palette) {
3879 color = (alpha |
3880 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3881 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3882 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3883 } else {
3884 color = alpha;
3887 else if (This->resource.format_desc->format == WINED3DFMT_R5G6B5)
3889 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3890 color = 0xFFFFFFFF;
3891 } else {
3892 color = ((0xFF000000) |
3893 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3894 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3895 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3898 else if ((This->resource.format_desc->format == WINED3DFMT_R8G8B8)
3899 || (This->resource.format_desc->format == WINED3DFMT_X8R8G8B8))
3901 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3903 else if (This->resource.format_desc->format == WINED3DFMT_A8R8G8B8)
3905 color = DDBltFx->u5.dwFillColor;
3907 else {
3908 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3909 return WINED3DERR_INVALIDCALL;
3912 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3913 IWineD3DDeviceImpl_ClearSurface(myDevice, This, 1 /* Number of rectangles */,
3914 &rect, WINED3DCLEAR_TARGET, color, 0.0f /* Z */, 0 /* Stencil */);
3915 return WINED3D_OK;
3919 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3920 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3921 return WINED3DERR_INVALIDCALL;
3924 static HRESULT IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3925 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx)
3927 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3928 float depth;
3930 if (Flags & WINEDDBLT_DEPTHFILL) {
3931 switch(This->resource.format_desc->format)
3933 case WINED3DFMT_D16_UNORM:
3934 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3935 break;
3936 case WINED3DFMT_D15S1:
3937 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3938 break;
3939 case WINED3DFMT_D24S8:
3940 case WINED3DFMT_D24X8:
3941 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3942 break;
3943 case WINED3DFMT_D32:
3944 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3945 break;
3946 default:
3947 depth = 0.0f;
3948 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format_desc->format));
3951 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3952 DestRect == NULL ? 0 : 1,
3953 (const WINED3DRECT *)DestRect,
3954 WINED3DCLEAR_ZBUFFER,
3955 0x00000000,
3956 depth,
3957 0x00000000);
3960 FIXME("(%p): Unsupp depthstencil blit\n", This);
3961 return WINED3DERR_INVALIDCALL;
3964 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
3965 const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3966 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3967 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3968 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3969 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3970 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3972 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3974 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3975 return WINEDDERR_SURFACEBUSY;
3978 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3979 * except depth blits, which seem to work
3981 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3982 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3983 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3984 return WINED3DERR_INVALIDCALL;
3985 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3986 TRACE("Z Blit override handled the blit\n");
3987 return WINED3D_OK;
3991 /* Special cases for RenderTargets */
3992 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3993 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3994 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3997 /* For the rest call the X11 surface implementation.
3998 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3999 * other Blts are rather rare
4001 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
4004 static HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
4005 IWineD3DSurface *Source, const RECT *rsrc, DWORD trans)
4007 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4008 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
4009 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
4010 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
4012 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
4014 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
4015 return WINEDDERR_SURFACEBUSY;
4018 if(myDevice->inScene &&
4019 (iface == myDevice->stencilBufferTarget ||
4020 (Source && Source == myDevice->stencilBufferTarget))) {
4021 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
4022 return WINED3DERR_INVALIDCALL;
4025 /* Special cases for RenderTargets */
4026 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
4027 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
4029 RECT SrcRect, DstRect;
4030 DWORD Flags=0;
4032 if(rsrc) {
4033 SrcRect.left = rsrc->left;
4034 SrcRect.top= rsrc->top;
4035 SrcRect.bottom = rsrc->bottom;
4036 SrcRect.right = rsrc->right;
4037 } else {
4038 SrcRect.left = 0;
4039 SrcRect.top = 0;
4040 SrcRect.right = srcImpl->currentDesc.Width;
4041 SrcRect.bottom = srcImpl->currentDesc.Height;
4044 DstRect.left = dstx;
4045 DstRect.top=dsty;
4046 DstRect.right = dstx + SrcRect.right - SrcRect.left;
4047 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
4049 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
4050 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
4051 Flags |= WINEDDBLT_KEYSRC;
4052 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
4053 Flags |= WINEDDBLT_KEYDEST;
4054 if(trans & WINEDDBLTFAST_WAIT)
4055 Flags |= WINEDDBLT_WAIT;
4056 if(trans & WINEDDBLTFAST_DONOTWAIT)
4057 Flags |= WINEDDBLT_DONOTWAIT;
4059 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
4063 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
4066 static HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface)
4068 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4069 RGBQUAD col[256];
4070 IWineD3DPaletteImpl *pal = This->palette;
4071 unsigned int n;
4072 TRACE("(%p)\n", This);
4074 if (!pal) return WINED3D_OK;
4076 if (This->resource.format_desc->format == WINED3DFMT_P8
4077 || This->resource.format_desc->format == WINED3DFMT_A8P8)
4079 int bpp;
4080 GLenum format, internal, type;
4081 CONVERT_TYPES convert;
4083 /* Check if we are using a RTL mode which uses texturing for uploads */
4084 BOOL use_texture = (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX);
4086 /* Check if we have hardware palette conversion if we have convert is set to NO_CONVERSION */
4087 d3dfmt_get_conv(This, TRUE, use_texture, &format, &internal, &type, &convert, &bpp, FALSE);
4089 if((This->resource.usage & WINED3DUSAGE_RENDERTARGET) && (convert == NO_CONVERSION))
4091 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
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 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4101 d3dfmt_p8_upload_palette(iface, convert);
4102 } else {
4103 if(!(This->Flags & SFLAG_INSYSMEM)) {
4104 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
4105 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
4107 TRACE("Dirtifying surface\n");
4108 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
4112 if(This->Flags & SFLAG_DIBSECTION) {
4113 TRACE("(%p): Updating the hdc's palette\n", This);
4114 for (n=0; n<256; n++) {
4115 col[n].rgbRed = pal->palents[n].peRed;
4116 col[n].rgbGreen = pal->palents[n].peGreen;
4117 col[n].rgbBlue = pal->palents[n].peBlue;
4118 col[n].rgbReserved = 0;
4120 SetDIBColorTable(This->hDC, 0, 256, col);
4123 /* Propagate the changes to the drawable when we have a palette. */
4124 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
4125 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
4127 return WINED3D_OK;
4130 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
4131 /** Check against the maximum texture sizes supported by the video card **/
4132 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4133 unsigned int pow2Width, pow2Height;
4135 This->texture_name = 0;
4136 This->texture_target = GL_TEXTURE_2D;
4138 /* Non-power2 support */
4139 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO) || GL_SUPPORT(WINE_NORMALIZED_TEXRECT)) {
4140 pow2Width = This->currentDesc.Width;
4141 pow2Height = This->currentDesc.Height;
4142 } else {
4143 /* Find the nearest pow2 match */
4144 pow2Width = pow2Height = 1;
4145 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
4146 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
4148 This->pow2Width = pow2Width;
4149 This->pow2Height = pow2Height;
4151 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
4152 /** TODO: add support for non power two compressed textures **/
4153 if (This->resource.format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
4155 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
4156 This, This->currentDesc.Width, This->currentDesc.Height);
4157 return WINED3DERR_NOTAVAILABLE;
4161 if(pow2Width != This->currentDesc.Width ||
4162 pow2Height != This->currentDesc.Height) {
4163 This->Flags |= SFLAG_NONPOW2;
4166 TRACE("%p\n", This);
4167 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
4168 /* one of three options
4169 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)
4170 2: Set the texture to the maximum size (bad idea)
4171 3: WARN and return WINED3DERR_NOTAVAILABLE;
4172 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.
4174 WARN("(%p) Creating an oversized surface: %ux%u (texture is %ux%u)\n",
4175 This, This->pow2Width, This->pow2Height, This->currentDesc.Width, This->currentDesc.Height);
4176 This->Flags |= SFLAG_OVERSIZE;
4178 /* This will be initialized on the first blt */
4179 This->glRect.left = 0;
4180 This->glRect.top = 0;
4181 This->glRect.right = 0;
4182 This->glRect.bottom = 0;
4183 } else {
4184 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
4185 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
4186 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
4187 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
4189 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE)
4190 && !((This->resource.format_desc->format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE)
4191 && (wined3d_settings.rendertargetlock_mode == RTL_READTEX
4192 || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
4194 This->texture_target = GL_TEXTURE_RECTANGLE_ARB;
4195 This->pow2Width = This->currentDesc.Width;
4196 This->pow2Height = This->currentDesc.Height;
4197 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
4200 /* No oversize, gl rect is the full texture size */
4201 This->Flags &= ~SFLAG_OVERSIZE;
4202 This->glRect.left = 0;
4203 This->glRect.top = 0;
4204 This->glRect.right = This->pow2Width;
4205 This->glRect.bottom = This->pow2Height;
4208 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4209 switch(wined3d_settings.offscreen_rendering_mode) {
4210 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4211 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4212 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4216 This->Flags |= SFLAG_INSYSMEM;
4218 return WINED3D_OK;
4221 struct depth_blt_info
4223 GLenum binding;
4224 GLenum bind_target;
4225 enum tex_types tex_type;
4226 GLfloat coords[4][3];
4229 static void surface_get_depth_blt_info(GLenum target, GLsizei w, GLsizei h, struct depth_blt_info *info)
4231 GLfloat (*coords)[3] = info->coords;
4233 switch (target)
4235 default:
4236 FIXME("Unsupported texture target %#x\n", target);
4237 /* Fall back to GL_TEXTURE_2D */
4238 case GL_TEXTURE_2D:
4239 info->binding = GL_TEXTURE_BINDING_2D;
4240 info->bind_target = GL_TEXTURE_2D;
4241 info->tex_type = tex_2d;
4242 coords[0][0] = 0.0f; coords[0][1] = 1.0f; coords[0][2] = 0.0f;
4243 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 0.0f;
4244 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4245 coords[3][0] = 1.0f; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4246 break;
4248 case GL_TEXTURE_RECTANGLE_ARB:
4249 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
4250 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
4251 info->tex_type = tex_rect;
4252 coords[0][0] = 0.0f; coords[0][1] = h; coords[0][2] = 0.0f;
4253 coords[1][0] = w; coords[1][1] = h; coords[1][2] = 0.0f;
4254 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4255 coords[3][0] = w; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4256 break;
4258 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4259 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4260 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4261 info->tex_type = tex_cube;
4262 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4263 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4264 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4265 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4267 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4268 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4269 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4270 info->tex_type = tex_cube;
4271 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4272 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4273 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4274 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4276 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4277 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4278 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4279 info->tex_type = tex_cube;
4280 coords[0][0] = -1.0f; coords[0][1] = 1.0f; coords[0][2] = 1.0f;
4281 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 1.0f;
4282 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4283 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4285 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4286 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4287 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4288 info->tex_type = tex_cube;
4289 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4290 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4291 coords[2][0] = -1.0f; coords[2][1] = -1.0f; coords[2][2] = 1.0f;
4292 coords[3][0] = 1.0f; coords[3][1] = -1.0f; coords[3][2] = 1.0f;
4294 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4295 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4296 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4297 info->tex_type = tex_cube;
4298 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4299 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4300 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4301 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4303 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4304 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4305 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4306 info->tex_type = tex_cube;
4307 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4308 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4309 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4310 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4314 /* GL locking is done by the caller */
4315 static void surface_depth_blt(IWineD3DSurfaceImpl *This, GLuint texture, GLsizei w, GLsizei h, GLenum target)
4317 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4318 struct depth_blt_info info;
4319 GLint old_binding = 0;
4321 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4323 glDisable(GL_CULL_FACE);
4324 glDisable(GL_BLEND);
4325 glDisable(GL_ALPHA_TEST);
4326 glDisable(GL_SCISSOR_TEST);
4327 glDisable(GL_STENCIL_TEST);
4328 glEnable(GL_DEPTH_TEST);
4329 glDepthFunc(GL_ALWAYS);
4330 glDepthMask(GL_TRUE);
4331 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4332 glViewport(0, 0, w, h);
4334 surface_get_depth_blt_info(target, w, h, &info);
4335 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4336 glGetIntegerv(info.binding, &old_binding);
4337 glBindTexture(info.bind_target, texture);
4339 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4341 glBegin(GL_TRIANGLE_STRIP);
4342 glTexCoord3fv(info.coords[0]);
4343 glVertex2f(-1.0f, -1.0f);
4344 glTexCoord3fv(info.coords[1]);
4345 glVertex2f(1.0f, -1.0f);
4346 glTexCoord3fv(info.coords[2]);
4347 glVertex2f(-1.0f, 1.0f);
4348 glTexCoord3fv(info.coords[3]);
4349 glVertex2f(1.0f, 1.0f);
4350 glEnd();
4352 glBindTexture(info.bind_target, old_binding);
4354 glPopAttrib();
4356 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4359 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
4360 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4362 TRACE("(%p) New location %#x\n", This, location);
4364 if (location & ~SFLAG_DS_LOCATIONS) {
4365 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
4368 This->Flags &= ~SFLAG_DS_LOCATIONS;
4369 This->Flags |= location;
4372 /* Context activation is done by the caller. */
4373 void surface_load_ds_location(IWineD3DSurface *iface, DWORD location) {
4374 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4375 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4377 TRACE("(%p) New location %#x\n", This, location);
4379 /* TODO: Make this work for modes other than FBO */
4380 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4382 if (This->Flags & location) {
4383 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
4384 return;
4387 if (This->current_renderbuffer) {
4388 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
4389 return;
4392 if (location == SFLAG_DS_OFFSCREEN) {
4393 if (This->Flags & SFLAG_DS_ONSCREEN) {
4394 GLint old_binding = 0;
4395 GLenum bind_target;
4397 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
4399 ENTER_GL();
4401 if (!device->depth_blt_texture) {
4402 glGenTextures(1, &device->depth_blt_texture);
4405 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4406 * directly on the FBO texture. That's because we need to flip. */
4407 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4408 if (This->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4410 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4411 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4412 } else {
4413 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4414 bind_target = GL_TEXTURE_2D;
4416 glBindTexture(bind_target, device->depth_blt_texture);
4417 glCopyTexImage2D(bind_target, This->texture_level, This->resource.format_desc->glInternal,
4418 0, 0, This->currentDesc.Width, This->currentDesc.Height, 0);
4419 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4420 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4421 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4422 glBindTexture(bind_target, old_binding);
4424 /* Setup the destination */
4425 if (!device->depth_blt_rb) {
4426 GL_EXTCALL(glGenRenderbuffersEXT(1, &device->depth_blt_rb));
4427 checkGLcall("glGenRenderbuffersEXT");
4429 if (device->depth_blt_rb_w != This->currentDesc.Width
4430 || device->depth_blt_rb_h != This->currentDesc.Height) {
4431 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4432 checkGLcall("glBindRenderbufferEXT");
4433 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, This->currentDesc.Width, This->currentDesc.Height));
4434 checkGLcall("glRenderbufferStorageEXT");
4435 device->depth_blt_rb_w = This->currentDesc.Width;
4436 device->depth_blt_rb_h = This->currentDesc.Height;
4439 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->dst_fbo);
4440 GL_EXTCALL(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4441 checkGLcall("glFramebufferRenderbufferEXT");
4442 context_attach_depth_stencil_fbo(device, GL_FRAMEBUFFER_EXT, iface, FALSE);
4444 /* Do the actual blit */
4445 surface_depth_blt(This, device->depth_blt_texture, This->currentDesc.Width, This->currentDesc.Height, bind_target);
4446 checkGLcall("depth_blt");
4448 if (device->activeContext->current_fbo) {
4449 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->current_fbo->id);
4450 } else {
4451 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4452 checkGLcall("glBindFramebuffer()");
4455 LEAVE_GL();
4456 } else {
4457 FIXME("No up to date depth stencil location\n");
4459 } else if (location == SFLAG_DS_ONSCREEN) {
4460 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4461 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4463 ENTER_GL();
4465 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4466 checkGLcall("glBindFramebuffer()");
4467 surface_depth_blt(This, This->texture_name, This->currentDesc.Width,
4468 This->currentDesc.Height, This->texture_target);
4469 checkGLcall("depth_blt");
4471 if (device->activeContext->current_fbo) {
4472 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, device->activeContext->current_fbo->id));
4473 checkGLcall("glBindFramebuffer()");
4476 LEAVE_GL();
4477 } else {
4478 FIXME("No up to date depth stencil location\n");
4480 } else {
4481 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4484 This->Flags |= location;
4487 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4488 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4489 IWineD3DBaseTexture *texture;
4490 IWineD3DSurfaceImpl *overlay;
4492 TRACE("(%p)->(%s, %s)\n", iface, debug_surflocation(flag),
4493 persistent ? "TRUE" : "FALSE");
4495 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4496 if (This->Flags & SFLAG_SWAPCHAIN)
4498 TRACE("Surface %p is an onscreen surface\n", iface);
4499 } else {
4500 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4501 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4505 if(persistent) {
4506 if(((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) ||
4507 ((This->Flags & SFLAG_INSRGBTEX) && !(flag & SFLAG_INSRGBTEX))) {
4508 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4509 TRACE("Passing to container\n");
4510 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4511 IWineD3DBaseTexture_Release(texture);
4514 This->Flags &= ~SFLAG_LOCATIONS;
4515 This->Flags |= flag;
4517 /* Redraw emulated overlays, if any */
4518 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4519 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4520 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4523 } else {
4524 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))) {
4525 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4526 TRACE("Passing to container\n");
4527 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4528 IWineD3DBaseTexture_Release(texture);
4531 This->Flags &= ~flag;
4534 if(!(This->Flags & SFLAG_LOCATIONS)) {
4535 ERR("%p: Surface does not have any up to date location\n", This);
4539 struct coords {
4540 GLfloat x, y, z;
4543 struct float_rect
4545 float l;
4546 float t;
4547 float r;
4548 float b;
4551 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
4553 f->l = ((r->left * 2.0f) / w) - 1.0f;
4554 f->t = ((r->top * 2.0f) / h) - 1.0f;
4555 f->r = ((r->right * 2.0f) / w) - 1.0f;
4556 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
4559 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
4560 struct coords coords[4];
4561 RECT rect;
4562 IWineD3DSwapChain *swapchain;
4563 IWineD3DBaseTexture *texture;
4564 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4565 GLenum bind_target;
4566 struct float_rect f;
4568 if(rect_in) {
4569 rect = *rect_in;
4570 } else {
4571 rect.left = 0;
4572 rect.top = 0;
4573 rect.right = This->currentDesc.Width;
4574 rect.bottom = This->currentDesc.Height;
4577 switch (This->texture_target)
4579 case GL_TEXTURE_2D:
4580 bind_target = GL_TEXTURE_2D;
4582 coords[0].x = (float)rect.left / This->pow2Width;
4583 coords[0].y = (float)rect.top / This->pow2Height;
4584 coords[0].z = 0;
4586 coords[1].x = (float)rect.left / This->pow2Width;
4587 coords[1].y = (float)rect.bottom / This->pow2Height;
4588 coords[1].z = 0;
4590 coords[2].x = (float)rect.right / This->pow2Width;
4591 coords[2].y = (float)rect.bottom / This->pow2Height;
4592 coords[2].z = 0;
4594 coords[3].x = (float)rect.right / This->pow2Width;
4595 coords[3].y = (float)rect.top / This->pow2Height;
4596 coords[3].z = 0;
4597 break;
4599 case GL_TEXTURE_RECTANGLE_ARB:
4600 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4601 coords[0].x = rect.left; coords[0].y = rect.top; coords[0].z = 0;
4602 coords[1].x = rect.left; coords[1].y = rect.bottom; coords[1].z = 0;
4603 coords[2].x = rect.right; coords[2].y = rect.bottom; coords[2].z = 0;
4604 coords[3].x = rect.right; coords[3].y = rect.top; coords[3].z = 0;
4605 break;
4607 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4608 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4609 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4610 coords[0].x = 1; coords[0].y = -f.t; coords[0].z = -f.l;
4611 coords[1].x = 1; coords[1].y = -f.b; coords[1].z = -f.l;
4612 coords[2].x = 1; coords[2].y = -f.b; coords[2].z = -f.r;
4613 coords[3].x = 1; coords[3].y = -f.t; coords[3].z = -f.r;
4614 break;
4616 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4617 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4618 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4619 coords[0].x = -1; coords[0].y = -f.t; coords[0].z = f.l;
4620 coords[1].x = -1; coords[1].y = -f.b; coords[1].z = f.l;
4621 coords[2].x = -1; coords[2].y = -f.b; coords[2].z = f.r;
4622 coords[3].x = -1; coords[3].y = -f.t; coords[3].z = f.r;
4623 break;
4625 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4626 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4627 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4628 coords[0].x = f.l; coords[0].y = 1; coords[0].z = f.t;
4629 coords[1].x = f.l; coords[1].y = 1; coords[1].z = f.b;
4630 coords[2].x = f.r; coords[2].y = 1; coords[2].z = f.b;
4631 coords[3].x = f.r; coords[3].y = 1; coords[3].z = f.t;
4632 break;
4634 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4635 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4636 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4637 coords[0].x = f.l; coords[0].y = -1; coords[0].z = -f.t;
4638 coords[1].x = f.l; coords[1].y = -1; coords[1].z = -f.b;
4639 coords[2].x = f.r; coords[2].y = -1; coords[2].z = -f.b;
4640 coords[3].x = f.r; coords[3].y = -1; coords[3].z = -f.t;
4641 break;
4643 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4644 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4645 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4646 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1;
4647 coords[1].x = f.l; coords[1].y = -f.b; coords[1].z = 1;
4648 coords[2].x = f.r; coords[2].y = -f.b; coords[2].z = 1;
4649 coords[3].x = f.r; coords[3].y = -f.t; coords[3].z = 1;
4650 break;
4652 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4653 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4654 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4655 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1;
4656 coords[1].x = -f.l; coords[1].y = -f.b; coords[1].z = -1;
4657 coords[2].x = -f.r; coords[2].y = -f.b; coords[2].z = -1;
4658 coords[3].x = -f.r; coords[3].y = -f.t; coords[3].z = -1;
4659 break;
4661 default:
4662 ERR("Unexpected texture target %#x\n", This->texture_target);
4663 return;
4666 ActivateContext(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4667 ENTER_GL();
4669 glEnable(bind_target);
4670 checkGLcall("glEnable(bind_target)");
4671 glBindTexture(bind_target, This->texture_name);
4672 checkGLcall("bind_target, This->texture_name)");
4673 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4674 checkGLcall("glTexParameteri");
4675 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4676 checkGLcall("glTexParameteri");
4678 if (device->render_offscreen)
4680 LONG tmp = rect.top;
4681 rect.top = rect.bottom;
4682 rect.bottom = tmp;
4685 glBegin(GL_QUADS);
4686 glTexCoord3fv(&coords[0].x);
4687 glVertex2i(rect.left, rect.top);
4689 glTexCoord3fv(&coords[1].x);
4690 glVertex2i(rect.left, rect.bottom);
4692 glTexCoord3fv(&coords[2].x);
4693 glVertex2i(rect.right, rect.bottom);
4695 glTexCoord3fv(&coords[3].x);
4696 glVertex2i(rect.right, rect.top);
4697 glEnd();
4698 checkGLcall("glEnd");
4700 glDisable(bind_target);
4701 checkGLcall("glDisable(bind_target)");
4703 LEAVE_GL();
4705 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain)))
4707 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
4708 if(((IWineD3DSwapChainImpl*)swapchain)->frontBuffer == (IWineD3DSurface*)This ||
4709 ((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
4710 glFlush();
4712 IWineD3DSwapChain_Release(swapchain);
4713 } else {
4714 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
4715 * reset properly next draw
4717 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture)))
4719 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
4720 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
4721 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
4722 IWineD3DBaseTexture_Release(texture);
4727 /*****************************************************************************
4728 * IWineD3DSurface::LoadLocation
4730 * Copies the current surface data from wherever it is to the requested
4731 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4732 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4733 * multiple locations, the gl texture is preferred over the drawable, which is
4734 * preferred over system memory. The PBO counts as system memory. If rect is
4735 * not NULL, only the specified rectangle is copied (only supported for
4736 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4737 * location is marked up to date after the copy.
4739 * Parameters:
4740 * flag: Surface location flag to be updated
4741 * rect: rectangle to be copied
4743 * Returns:
4744 * WINED3D_OK on success
4745 * WINED3DERR_DEVICELOST on an internal error
4747 *****************************************************************************/
4748 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4749 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4750 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4751 GLenum format, internal, type;
4752 CONVERT_TYPES convert;
4753 int bpp;
4754 int width, pitch, outpitch;
4755 BYTE *mem;
4756 BOOL drawable_read_ok = TRUE;
4758 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4759 if (This->Flags & SFLAG_SWAPCHAIN)
4761 TRACE("Surface %p is an onscreen surface\n", iface);
4762 } else {
4763 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4764 * Prefer SFLAG_INTEXTURE. */
4765 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4766 drawable_read_ok = FALSE;
4770 TRACE("(%p)->(%s, %p)\n", iface, debug_surflocation(flag), rect);
4771 if(rect) {
4772 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4775 if(This->Flags & flag) {
4776 TRACE("Location already up to date\n");
4777 return WINED3D_OK;
4780 if(!(This->Flags & SFLAG_LOCATIONS)) {
4781 ERR("%p: Surface does not have any up to date location\n", This);
4782 This->Flags |= SFLAG_LOST;
4783 return WINED3DERR_DEVICELOST;
4786 if(flag == SFLAG_INSYSMEM) {
4787 surface_prepare_system_memory(This);
4789 /* Download the surface to system memory */
4790 if(This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) {
4791 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4792 surface_bind_and_dirtify(This, !(This->Flags & SFLAG_INTEXTURE));
4794 surface_download_data(This);
4795 } else {
4796 /* Note: It might be faster to download into a texture first. */
4797 read_from_framebuffer(This, rect,
4798 This->resource.allocatedMemory,
4799 IWineD3DSurface_GetPitch(iface));
4801 } else if(flag == SFLAG_INDRAWABLE) {
4802 if(This->Flags & SFLAG_INTEXTURE) {
4803 surface_blt_to_drawable(This, rect);
4804 } else {
4805 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4806 /* This needs a shader to convert the srgb data sampled from the GL texture into RGB
4807 * values, otherwise we get incorrect values in the target. For now go the slow way
4808 * via a system memory copy
4810 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4813 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, FALSE);
4815 /* The width is in 'length' not in bytes */
4816 width = This->currentDesc.Width;
4817 pitch = IWineD3DSurface_GetPitch(iface);
4819 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4820 * but it isn't set (yet) in all cases it is getting called. */
4821 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4822 TRACE("Removing the pbo attached to surface %p\n", This);
4823 if (!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4824 surface_remove_pbo(This);
4827 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4828 int height = This->currentDesc.Height;
4830 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4831 outpitch = width * bpp;
4832 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4834 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4835 if(!mem) {
4836 ERR("Out of memory %d, %d!\n", outpitch, height);
4837 return WINED3DERR_OUTOFVIDEOMEMORY;
4839 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4841 This->Flags |= SFLAG_CONVERTED;
4842 } else {
4843 This->Flags &= ~SFLAG_CONVERTED;
4844 mem = This->resource.allocatedMemory;
4847 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4849 /* Don't delete PBO memory */
4850 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4851 HeapFree(GetProcessHeap(), 0, mem);
4853 } else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */ {
4854 if (drawable_read_ok && (This->Flags & SFLAG_INDRAWABLE)) {
4855 read_from_framebuffer_texture(This, flag == SFLAG_INSRGBTEX);
4856 } else { /* Upload from system memory */
4857 BOOL srgb = flag == SFLAG_INSRGBTEX;
4858 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4859 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
4861 if(srgb) {
4862 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE) {
4863 /* Performance warning ... */
4864 FIXME("%p: Downloading rgb texture to reload it as srgb\n", This);
4865 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4867 } else {
4868 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4869 /* Performance warning ... */
4870 FIXME("%p: Downloading srgb texture to reload it as rgb\n", This);
4871 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4875 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4876 surface_bind_and_dirtify(This, srgb);
4878 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4879 This->Flags |= SFLAG_GLCKEY;
4880 This->glCKey = This->SrcBltCKey;
4882 else This->Flags &= ~SFLAG_GLCKEY;
4884 /* The width is in 'length' not in bytes */
4885 width = This->currentDesc.Width;
4886 pitch = IWineD3DSurface_GetPitch(iface);
4888 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4889 * but it isn't set (yet) in all cases it is getting called. */
4890 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4891 TRACE("Removing the pbo attached to surface %p\n", This);
4892 surface_remove_pbo(This);
4895 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4896 int height = This->currentDesc.Height;
4898 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4899 outpitch = width * bpp;
4900 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4902 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4903 if(!mem) {
4904 ERR("Out of memory %d, %d!\n", outpitch, height);
4905 return WINED3DERR_OUTOFVIDEOMEMORY;
4907 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4909 This->Flags |= SFLAG_CONVERTED;
4911 else if ((This->resource.format_desc->format == WINED3DFMT_P8)
4912 && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)))
4914 d3dfmt_p8_upload_palette(iface, convert);
4915 This->Flags &= ~SFLAG_CONVERTED;
4916 mem = This->resource.allocatedMemory;
4917 } else {
4918 This->Flags &= ~SFLAG_CONVERTED;
4919 mem = This->resource.allocatedMemory;
4922 /* Make sure the correct pitch is used */
4923 ENTER_GL();
4924 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4925 LEAVE_GL();
4927 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4928 TRACE("non power of two support\n");
4929 if(!(This->Flags & alloc_flag)) {
4930 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4931 This->Flags |= alloc_flag;
4933 if (mem || (This->Flags & SFLAG_PBO)) {
4934 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4936 } else {
4937 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4938 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4940 if(!(This->Flags & alloc_flag)) {
4941 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4942 This->Flags |= alloc_flag;
4944 if (mem || (This->Flags & SFLAG_PBO)) {
4945 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4949 /* Restore the default pitch */
4950 ENTER_GL();
4951 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4952 LEAVE_GL();
4954 /* Don't delete PBO memory */
4955 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4956 HeapFree(GetProcessHeap(), 0, mem);
4960 if(rect == NULL) {
4961 This->Flags |= flag;
4964 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !(This->Flags & SFLAG_SWAPCHAIN)
4965 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4966 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4967 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4970 return WINED3D_OK;
4973 static HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container)
4975 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4976 IWineD3DSwapChain *swapchain = NULL;
4978 /* Update the drawable size method */
4979 if(container) {
4980 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4982 if(swapchain) {
4983 This->get_drawable_size = get_drawable_size_swapchain;
4984 IWineD3DSwapChain_Release(swapchain);
4985 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4986 switch(wined3d_settings.offscreen_rendering_mode) {
4987 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4988 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4989 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4993 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4996 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4997 return SURFACE_OPENGL;
5000 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
5001 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
5002 HRESULT hr;
5004 /* If there's no destination surface there is nothing to do */
5005 if(!This->overlay_dest) return WINED3D_OK;
5007 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
5008 * update the overlay. Prevent an endless recursion
5010 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
5011 return WINED3D_OK;
5013 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
5014 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
5015 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
5016 NULL, WINED3DTEXF_LINEAR);
5017 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
5019 return hr;
5022 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
5024 /* IUnknown */
5025 IWineD3DBaseSurfaceImpl_QueryInterface,
5026 IWineD3DBaseSurfaceImpl_AddRef,
5027 IWineD3DSurfaceImpl_Release,
5028 /* IWineD3DResource */
5029 IWineD3DBaseSurfaceImpl_GetParent,
5030 IWineD3DBaseSurfaceImpl_GetDevice,
5031 IWineD3DBaseSurfaceImpl_SetPrivateData,
5032 IWineD3DBaseSurfaceImpl_GetPrivateData,
5033 IWineD3DBaseSurfaceImpl_FreePrivateData,
5034 IWineD3DBaseSurfaceImpl_SetPriority,
5035 IWineD3DBaseSurfaceImpl_GetPriority,
5036 IWineD3DSurfaceImpl_PreLoad,
5037 IWineD3DSurfaceImpl_UnLoad,
5038 IWineD3DBaseSurfaceImpl_GetType,
5039 /* IWineD3DSurface */
5040 IWineD3DBaseSurfaceImpl_GetContainer,
5041 IWineD3DBaseSurfaceImpl_GetDesc,
5042 IWineD3DSurfaceImpl_LockRect,
5043 IWineD3DSurfaceImpl_UnlockRect,
5044 IWineD3DSurfaceImpl_GetDC,
5045 IWineD3DSurfaceImpl_ReleaseDC,
5046 IWineD3DSurfaceImpl_Flip,
5047 IWineD3DSurfaceImpl_Blt,
5048 IWineD3DBaseSurfaceImpl_GetBltStatus,
5049 IWineD3DBaseSurfaceImpl_GetFlipStatus,
5050 IWineD3DBaseSurfaceImpl_IsLost,
5051 IWineD3DBaseSurfaceImpl_Restore,
5052 IWineD3DSurfaceImpl_BltFast,
5053 IWineD3DBaseSurfaceImpl_GetPalette,
5054 IWineD3DBaseSurfaceImpl_SetPalette,
5055 IWineD3DSurfaceImpl_RealizePalette,
5056 IWineD3DBaseSurfaceImpl_SetColorKey,
5057 IWineD3DBaseSurfaceImpl_GetPitch,
5058 IWineD3DSurfaceImpl_SetMem,
5059 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
5060 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
5061 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
5062 IWineD3DBaseSurfaceImpl_UpdateOverlay,
5063 IWineD3DBaseSurfaceImpl_SetClipper,
5064 IWineD3DBaseSurfaceImpl_GetClipper,
5065 /* Internal use: */
5066 IWineD3DSurfaceImpl_LoadTexture,
5067 IWineD3DSurfaceImpl_BindTexture,
5068 IWineD3DSurfaceImpl_SaveSnapshot,
5069 IWineD3DSurfaceImpl_SetContainer,
5070 IWineD3DBaseSurfaceImpl_GetData,
5071 IWineD3DSurfaceImpl_SetFormat,
5072 IWineD3DSurfaceImpl_PrivateSetup,
5073 IWineD3DSurfaceImpl_ModifyLocation,
5074 IWineD3DSurfaceImpl_LoadLocation,
5075 IWineD3DSurfaceImpl_GetImplType,
5076 IWineD3DSurfaceImpl_DrawOverlay
5078 #undef GLINFO_LOCATION
5080 #define GLINFO_LOCATION device->adapter->gl_info
5081 static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
5082 /* Context activation is done by the caller. */
5083 static void ffp_blit_free(IWineD3DDevice *iface) { }
5085 /* Context activation is done by the caller. */
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 /* Context activation is done by the caller. */
5097 static void ffp_blit_unset(IWineD3DDevice *iface) {
5098 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
5099 ENTER_GL();
5100 glDisable(GL_TEXTURE_2D);
5101 checkGLcall("glDisable(GL_TEXTURE_2D)");
5102 if(GL_SUPPORT(ARB_TEXTURE_CUBE_MAP)) {
5103 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
5104 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
5106 if(GL_SUPPORT(ARB_TEXTURE_RECTANGLE)) {
5107 glDisable(GL_TEXTURE_RECTANGLE_ARB);
5108 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
5110 LEAVE_GL();
5113 static BOOL ffp_blit_color_fixup_supported(struct color_fixup_desc fixup)
5115 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
5117 TRACE("Checking support for fixup:\n");
5118 dump_color_fixup_desc(fixup);
5121 /* We only support identity conversions. */
5122 if (is_identity_fixup(fixup))
5124 TRACE("[OK]\n");
5125 return TRUE;
5128 TRACE("[FAILED]\n");
5129 return FALSE;
5132 const struct blit_shader ffp_blit = {
5133 ffp_blit_alloc,
5134 ffp_blit_free,
5135 ffp_blit_set,
5136 ffp_blit_unset,
5137 ffp_blit_color_fixup_supported