push ca931951dc5f3f050707cce013d2412130f45f11
[wine/hacks.git] / dlls / wined3d / surface.c
blob454a86ff779d4fd2acf621ce110e10166083dc55
1 /*
2 * IWineD3DSurface Implementation
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006-2008 Stefan Dösinger for CodeWeavers
11 * Copyright 2007-2008 Henri Verbeet
12 * Copyright 2006-2008 Roderick Colenbrander
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d);
36 #define GLINFO_LOCATION (*gl_info)
38 static void surface_cleanup(IWineD3DSurfaceImpl *This)
40 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
41 const WineD3D_GL_Info *gl_info = &device->adapter->gl_info;
42 renderbuffer_entry_t *entry, *entry2;
44 TRACE("(%p) : Cleaning up.\n", This);
46 /* Need a context to destroy the texture. Use the currently active render
47 * target, but only if the primary render target exists. Otherwise
48 * lastActiveRenderTarget is garbage. When destroying the primary render
49 * target, Uninit3D() will activate a context before doing anything. */
50 if (device->render_targets && device->render_targets[0])
52 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
55 ENTER_GL();
57 if (This->glDescription.textureName)
59 /* Release the OpenGL texture. */
60 TRACE("Deleting texture %u.\n", This->glDescription.textureName);
61 glDeleteTextures(1, &This->glDescription.textureName);
64 if (This->Flags & SFLAG_PBO)
66 /* Delete the PBO. */
67 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
70 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry)
72 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
73 HeapFree(GetProcessHeap(), 0, entry);
76 LEAVE_GL();
78 if (This->Flags & SFLAG_DIBSECTION)
80 /* Release the DC. */
81 SelectObject(This->hDC, This->dib.holdbitmap);
82 DeleteDC(This->hDC);
83 /* Release the DIB section. */
84 DeleteObject(This->dib.DIBsection);
85 This->dib.bitmap_data = NULL;
86 This->resource.allocatedMemory = NULL;
89 if (This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem((IWineD3DSurface *)This, NULL);
90 if (This->overlay_dest) list_remove(&This->overlay_entry);
92 HeapFree(GetProcessHeap(), 0, This->palette9);
94 resource_cleanup((IWineD3DResource *)This);
97 UINT surface_calculate_size(const struct GlPixelFormatDesc *format_desc, UINT alignment, UINT width, UINT height)
99 UINT size;
101 if (format_desc->format == WINED3DFMT_UNKNOWN)
103 size = 0;
105 else if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
107 UINT row_block_count = (width + format_desc->block_width - 1) / format_desc->block_width;
108 UINT row_count = (height + format_desc->block_height - 1) / format_desc->block_height;
109 size = row_count * row_block_count * format_desc->block_byte_count;
111 else
113 /* The pitch is a multiple of 4 bytes. */
114 size = height * (((width * format_desc->byte_count) + alignment - 1) & ~(alignment - 1));
117 if (format_desc->heightscale != 0.0) size *= format_desc->heightscale;
119 return size;
122 HRESULT surface_init(IWineD3DSurfaceImpl *surface, WINED3DSURFTYPE surface_type, UINT alignment,
123 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
124 UINT multisample_quality, IWineD3DDeviceImpl *device, DWORD usage, WINED3DFORMAT format,
125 WINED3DPOOL pool, IUnknown *parent)
127 const WineD3D_GL_Info *gl_info = &device->adapter->gl_info;
128 const struct GlPixelFormatDesc *format_desc = getFormatDescEntry(format, &GLINFO_LOCATION);
129 void (*cleanup)(IWineD3DSurfaceImpl *This);
130 unsigned int resource_size;
131 HRESULT hr;
133 if (multisample_quality > 0)
135 FIXME("multisample_quality set to %u, substituting 0\n", multisample_quality);
136 multisample_quality = 0;
139 /* FIXME: Check that the format is supported by the device. */
141 resource_size = surface_calculate_size(format_desc, alignment, width, height);
143 /* Look at the implementation and set the correct Vtable. */
144 switch (surface_type)
146 case SURFACE_OPENGL:
147 surface->lpVtbl = &IWineD3DSurface_Vtbl;
148 cleanup = surface_cleanup;
149 break;
151 case SURFACE_GDI:
152 surface->lpVtbl = &IWineGDISurface_Vtbl;
153 cleanup = surface_gdi_cleanup;
154 break;
156 default:
157 ERR("Requested unknown surface implementation %#x.\n", surface_type);
158 return WINED3DERR_INVALIDCALL;
161 hr = resource_init((IWineD3DResource *)surface, WINED3DRTYPE_SURFACE,
162 device, resource_size, usage, format_desc, pool, parent);
163 if (FAILED(hr))
165 WARN("Failed to initialize resource, returning %#x.\n", hr);
166 return hr;
169 /* "Standalone" surface. */
170 IWineD3DSurface_SetContainer((IWineD3DSurface *)surface, NULL);
172 surface->currentDesc.Width = width;
173 surface->currentDesc.Height = height;
174 surface->currentDesc.MultiSampleType = multisample_type;
175 surface->currentDesc.MultiSampleQuality = multisample_quality;
176 surface->glDescription.level = level;
177 list_init(&surface->overlays);
179 /* Flags */
180 surface->Flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
181 if (discard) surface->Flags |= SFLAG_DISCARD;
182 if (lockable || format == WINED3DFMT_D16_LOCKABLE) surface->Flags |= SFLAG_LOCKABLE;
184 /* Quick lockable sanity check.
185 * TODO: remove this after surfaces, usage and lockability have been debugged properly
186 * this function is too deep to need to care about things like this.
187 * Levels need to be checked too, since they all affect what can be done. */
188 switch (pool)
190 case WINED3DPOOL_SCRATCH:
191 if(!lockable)
193 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
194 "which are mutually exclusive, setting lockable to TRUE.\n");
195 lockable = TRUE;
197 break;
199 case WINED3DPOOL_SYSTEMMEM:
200 if (!lockable)
201 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
202 break;
204 case WINED3DPOOL_MANAGED:
205 if (usage & WINED3DUSAGE_DYNAMIC)
206 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
207 break;
209 case WINED3DPOOL_DEFAULT:
210 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
211 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
212 break;
214 default:
215 FIXME("Unknown pool %#x.\n", pool);
216 break;
219 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
221 FIXME("Trying to create a render target that isn't in the default pool.\n");
224 /* Mark the texture as dirty so that it gets loaded first time around. */
225 surface_add_dirty_rect((IWineD3DSurface *)surface, NULL);
226 list_init(&surface->renderbuffers);
228 TRACE("surface %p, memory %p, size %u\n", surface, surface->resource.allocatedMemory, surface->resource.size);
230 /* Call the private setup routine */
231 hr = IWineD3DSurface_PrivateSetup((IWineD3DSurface *)surface);
232 if (FAILED(hr))
234 ERR("Private setup failed, returning %#x\n", hr);
235 cleanup(surface);
236 return hr;
239 return hr;
242 static void surface_force_reload(IWineD3DSurface *iface)
244 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
246 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
249 void surface_set_texture_name(IWineD3DSurface *iface, GLuint new_name, BOOL srgb)
251 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
252 GLuint *name;
253 DWORD flag;
255 if(srgb)
257 name = &This->glDescription.srgbTextureName;
258 flag = SFLAG_INSRGBTEX;
260 else
262 name = &This->glDescription.textureName;
263 flag = SFLAG_INTEXTURE;
266 TRACE("(%p) : setting texture name %u\n", This, new_name);
268 if (!*name && new_name)
270 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
271 * surface has no texture name yet. See if we can get rid of this. */
272 if (This->Flags & flag)
273 ERR("Surface has SFLAG_INTEXTURE set, but no texture name\n");
274 IWineD3DSurface_ModifyLocation(iface, flag, FALSE);
277 *name = new_name;
278 surface_force_reload(iface);
281 void surface_set_texture_target(IWineD3DSurface *iface, GLenum target)
283 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
285 TRACE("(%p) : setting target %#x\n", This, target);
287 if (This->glDescription.target != target)
289 if (target == GL_TEXTURE_RECTANGLE_ARB)
291 This->Flags &= ~SFLAG_NORMCOORD;
293 else if (This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB)
295 This->Flags |= SFLAG_NORMCOORD;
298 This->glDescription.target = target;
299 surface_force_reload(iface);
302 /* 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->glDescription.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->glDescription.target, This->glDescription.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->glDescription.target,
378 This->glDescription.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", This, This->glDescription.level,
408 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->glDescription.target, This->glDescription.level, format,
415 type, NULL);
416 checkGLcall("glGetTexImage()");
418 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
419 checkGLcall("glBindBufferARB");
420 } else {
421 glGetTexImage(This->glDescription.target, This->glDescription.level, format,
422 type, mem);
423 checkGLcall("glGetTexImage()");
425 LEAVE_GL();
427 if (This->Flags & SFLAG_NONPOW2) {
428 const BYTE *src_data;
429 BYTE *dst_data;
430 UINT y;
432 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
433 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
434 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
436 * We're doing this...
438 * instead of boxing the texture :
439 * |<-texture width ->| -->pow2width| /\
440 * |111111111111111111| | |
441 * |222 Texture 222222| boxed empty | texture height
442 * |3333 Data 33333333| | |
443 * |444444444444444444| | \/
444 * ----------------------------------- |
445 * | boxed empty | boxed empty | pow2height
446 * | | | \/
447 * -----------------------------------
450 * we're repacking the data to the expected texture width
452 * |<-texture width ->| -->pow2width| /\
453 * |111111111111111111222222222222222| |
454 * |222333333333333333333444444444444| texture height
455 * |444444 | |
456 * | | \/
457 * | | |
458 * | empty | pow2height
459 * | | \/
460 * -----------------------------------
462 * == is the same as
464 * |<-texture width ->| /\
465 * |111111111111111111|
466 * |222222222222222222|texture height
467 * |333333333333333333|
468 * |444444444444444444| \/
469 * --------------------
471 * this also means that any references to allocatedMemory should work with the data as if were a
472 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
474 * internally the texture is still stored in a boxed format so any references to textureName will
475 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
477 * Performance should not be an issue, because applications normally do not lock the surfaces when
478 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
479 * and doesn't have to be re-read.
481 src_data = mem;
482 dst_data = This->resource.allocatedMemory;
483 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
484 for (y = 1 ; y < This->currentDesc.Height; y++) {
485 /* skip the first row */
486 src_data += src_pitch;
487 dst_data += dst_pitch;
488 memcpy(dst_data, src_data, dst_pitch);
491 HeapFree(GetProcessHeap(), 0, mem);
495 /* Surface has now been downloaded */
496 This->Flags |= SFLAG_INSYSMEM;
499 /* This call just uploads data, the caller is responsible for binding the
500 * correct texture. */
501 /* Context activation is done by the caller. */
502 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
503 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
505 if (format_desc->heightscale != 1.0 && format_desc->heightscale != 0.0) height *= format_desc->heightscale;
507 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
509 /* glCompressedTexSubImage2D() for uploading and glTexImage2D() for
510 * allocating does not work well on some drivers (r200 dri, MacOS ATI
511 * driver). glCompressedTexImage2D() does not accept NULL pointers. So
512 * for compressed textures surface_allocate_surface() does nothing,
513 * and this function uses glCompressedTexImage2D() instead of
514 * glCompressedTexSubImage2D(). */
515 TRACE("(%p) : Calling glCompressedTexImage2DARB w %u, h %u, data %p.\n", This, width, height, data);
517 ENTER_GL();
519 if (This->Flags & SFLAG_PBO)
521 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
522 checkGLcall("glBindBufferARB");
524 TRACE("(%p) pbo: %#x, data: %p.\n", This, This->pbo, data);
526 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level,
527 internal, width, height, 0 /* border */, This->resource.size, NULL));
528 checkGLcall("glCompressedTexImage2DARB");
530 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
531 checkGLcall("glBindBufferARB");
533 else
535 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level,
536 internal, width, height, 0 /* border */, This->resource.size, data));
537 checkGLcall("glCompressedTexSubImage2D");
540 LEAVE_GL();
542 else
544 TRACE("(%p) : Calling glTexSubImage2D w %d, h %d, data, %p\n", This, width, height, data);
545 ENTER_GL();
547 if(This->Flags & SFLAG_PBO) {
548 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
549 checkGLcall("glBindBufferARB");
550 TRACE("(%p) pbo: %#x, data: %p\n", This, This->pbo, data);
552 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, NULL);
553 checkGLcall("glTexSubImage2D");
555 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
556 checkGLcall("glBindBufferARB");
558 else {
559 glTexSubImage2D(This->glDescription.target, This->glDescription.level, 0, 0, width, height, format, type, data);
560 checkGLcall("glTexSubImage2D");
563 LEAVE_GL();
567 /* This call just allocates the texture, the caller is responsible for binding
568 * the correct texture. */
569 /* Context activation is done by the caller. */
570 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
571 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
572 BOOL enable_client_storage = FALSE;
573 const BYTE *mem = NULL;
575 if (format_desc->heightscale != 1.0 && format_desc->heightscale != 0.0) height *= format_desc->heightscale;
577 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",
578 This, This->glDescription.target, This->glDescription.level, debug_d3dformat(format_desc->format),
579 internal, width, height, format, type);
581 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
583 /* glCompressedTexImage2D does not accept NULL pointers, so we cannot allocate a compressed texture without uploading data */
584 TRACE("Not allocating compressed surfaces, surface_upload_data will specify them\n");
586 /* We have to point GL to the client storage memory here, because upload_data might use a PBO. This means a double upload
587 * once, unfortunately
589 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
590 /* Neither NONPOW2, DIBSECTION nor OVERSIZE flags can be set on compressed textures */
591 This->Flags |= SFLAG_CLIENT;
592 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
593 ENTER_GL();
594 GL_EXTCALL(glCompressedTexImage2DARB(This->glDescription.target, This->glDescription.level, internal,
595 width, height, 0 /* border */, This->resource.size, mem));
596 LEAVE_GL();
599 return;
602 ENTER_GL();
604 if(GL_SUPPORT(APPLE_CLIENT_STORAGE)) {
605 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_OVERSIZE | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
606 /* In some cases we want to disable client storage.
607 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
608 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
609 * SFLAG_OVERSIZE: The gl texture is smaller than the allocated memory
610 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
611 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
613 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
614 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
615 This->Flags &= ~SFLAG_CLIENT;
616 enable_client_storage = TRUE;
617 } else {
618 This->Flags |= SFLAG_CLIENT;
620 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
621 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
623 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
626 glTexImage2D(This->glDescription.target, This->glDescription.level, internal, width, height, 0, format, type, mem);
627 checkGLcall("glTexImage2D");
629 if(enable_client_storage) {
630 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
631 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
633 LEAVE_GL();
636 /* In D3D the depth stencil dimensions have to be greater than or equal to the
637 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
638 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
639 /* GL locking is done by the caller */
640 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
641 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
642 renderbuffer_entry_t *entry;
643 GLuint renderbuffer = 0;
644 unsigned int src_width, src_height;
646 src_width = This->pow2Width;
647 src_height = This->pow2Height;
649 /* A depth stencil smaller than the render target is not valid */
650 if (width > src_width || height > src_height) return;
652 /* Remove any renderbuffer set if the sizes match */
653 if (width == src_width && height == src_height) {
654 This->current_renderbuffer = NULL;
655 return;
658 /* Look if we've already got a renderbuffer of the correct dimensions */
659 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
660 if (entry->width == width && entry->height == height) {
661 renderbuffer = entry->id;
662 This->current_renderbuffer = entry;
663 break;
667 if (!renderbuffer) {
668 GL_EXTCALL(glGenRenderbuffersEXT(1, &renderbuffer));
669 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer));
670 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,
671 This->resource.format_desc->glInternal, width, height));
673 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
674 entry->width = width;
675 entry->height = height;
676 entry->id = renderbuffer;
677 list_add_head(&This->renderbuffers, &entry->entry);
679 This->current_renderbuffer = entry;
682 checkGLcall("set_compatible_renderbuffer");
685 GLenum surface_get_gl_buffer(IWineD3DSurface *iface, IWineD3DSwapChain *swapchain) {
686 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
687 IWineD3DSwapChainImpl *swapchain_impl = (IWineD3DSwapChainImpl *)swapchain;
689 TRACE("(%p) : swapchain %p\n", This, swapchain);
691 if (swapchain_impl->backBuffer && swapchain_impl->backBuffer[0] == iface) {
692 TRACE("Returning GL_BACK\n");
693 return GL_BACK;
694 } else if (swapchain_impl->frontBuffer == iface) {
695 TRACE("Returning GL_FRONT\n");
696 return GL_FRONT;
699 FIXME("Higher back buffer, returning GL_BACK\n");
700 return GL_BACK;
703 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
704 void surface_add_dirty_rect(IWineD3DSurface *iface, const RECT *dirty_rect)
706 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
707 IWineD3DBaseTexture *baseTexture = NULL;
709 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
710 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
712 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
713 if (dirty_rect)
715 This->dirtyRect.left = min(This->dirtyRect.left, dirty_rect->left);
716 This->dirtyRect.top = min(This->dirtyRect.top, dirty_rect->top);
717 This->dirtyRect.right = max(This->dirtyRect.right, dirty_rect->right);
718 This->dirtyRect.bottom = max(This->dirtyRect.bottom, dirty_rect->bottom);
720 else
722 This->dirtyRect.left = 0;
723 This->dirtyRect.top = 0;
724 This->dirtyRect.right = This->currentDesc.Width;
725 This->dirtyRect.bottom = This->currentDesc.Height;
728 TRACE("(%p) : Dirty: yes, Rect:(%d, %d, %d, %d)\n", This, This->dirtyRect.left,
729 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
731 /* if the container is a basetexture then mark it dirty. */
732 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture)))
734 TRACE("Passing to container\n");
735 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
736 IWineD3DBaseTexture_Release(baseTexture);
740 static inline BOOL surface_can_stretch_rect(IWineD3DSurfaceImpl *src, IWineD3DSurfaceImpl *dst)
742 return ((src->resource.format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE)
743 || (src->resource.usage & WINED3DUSAGE_RENDERTARGET))
744 && ((dst->resource.format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE)
745 || (dst->resource.usage & WINED3DUSAGE_RENDERTARGET))
746 && (src->resource.format_desc->format == dst->resource.format_desc->format
747 || (is_identity_fixup(src->resource.format_desc->color_fixup)
748 && is_identity_fixup(dst->resource.format_desc->color_fixup)));
751 static ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface)
753 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
754 ULONG ref = InterlockedDecrement(&This->resource.ref);
755 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
757 if (!ref)
759 surface_cleanup(This);
761 TRACE("(%p) Released.\n", This);
762 HeapFree(GetProcessHeap(), 0, This);
765 return ref;
768 /* ****************************************************
769 IWineD3DSurface IWineD3DResource parts follow
770 **************************************************** */
772 void surface_internal_preload(IWineD3DSurface *iface, enum WINED3DSRGB srgb)
774 /* TODO: check for locks */
775 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
776 IWineD3DBaseTexture *baseTexture = NULL;
777 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
779 TRACE("(%p)Checking to see if the container is a base texture\n", This);
780 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
781 IWineD3DBaseTextureImpl *tex_impl = (IWineD3DBaseTextureImpl *) baseTexture;
782 TRACE("Passing to container\n");
783 tex_impl->baseTexture.internal_preload(baseTexture, SRGB_RGB);
784 IWineD3DBaseTexture_Release(baseTexture);
785 } else {
786 TRACE("(%p) : About to load surface\n", This);
788 if(!device->isInDraw) {
789 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
792 if (This->resource.format_desc->format == WINED3DFMT_P8
793 || This->resource.format_desc->format == WINED3DFMT_A8P8)
795 if(palette9_changed(This)) {
796 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
797 /* TODO: This is not necessarily needed with hw palettized texture support */
798 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
799 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
800 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
804 IWineD3DSurface_LoadTexture(iface, srgb == SRGB_SRGB ? TRUE : FALSE);
806 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
807 /* Tell opengl to try and keep this texture in video ram (well mostly) */
808 GLclampf tmp;
809 tmp = 0.9f;
810 ENTER_GL();
811 glPrioritizeTextures(1, &This->glDescription.textureName, &tmp);
812 LEAVE_GL();
815 return;
818 static void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
819 surface_internal_preload(iface, SRGB_ANY);
822 /* Context activation is done by the caller. */
823 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
824 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
825 This->resource.allocatedMemory =
826 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
828 ENTER_GL();
829 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
830 checkGLcall("glBindBuffer(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
831 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
832 checkGLcall("glGetBufferSubData");
833 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
834 checkGLcall("glDeleteBuffers");
835 LEAVE_GL();
837 This->pbo = 0;
838 This->Flags &= ~SFLAG_PBO;
841 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
842 IWineD3DBaseTexture *texture = NULL;
843 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
844 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
845 renderbuffer_entry_t *entry, *entry2;
846 TRACE("(%p)\n", iface);
848 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
849 /* Default pool resources are supposed to be destroyed before Reset is called.
850 * Implicit resources stay however. So this means we have an implicit render target
851 * or depth stencil. The content may be destroyed, but we still have to tear down
852 * opengl resources, so we cannot leave early.
854 * Put the most up to date surface location into the drawable. D3D-wise this content
855 * is undefined, so it would be nowhere, but that would make the location management
856 * more complicated. The drawable is a sane location, because if we mark sysmem or
857 * texture up to date, drawPrim will copy the uninitialized texture or sysmem to the
858 * uninitialized drawable. That's pointless and we'd have to allocate the texture /
859 * sysmem copy here.
861 if (This->resource.usage & WINED3DUSAGE_DEPTHSTENCIL) {
862 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
863 } else {
864 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, TRUE);
866 } else {
867 /* Load the surface into system memory */
868 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
869 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
871 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
872 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSRGBTEX, FALSE);
873 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
875 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
877 /* Destroy PBOs, but load them into real sysmem before */
878 if(This->Flags & SFLAG_PBO) {
879 surface_remove_pbo(This);
882 /* Destroy fbo render buffers. This is needed for implicit render targets, for
883 * all application-created targets the application has to release the surface
884 * before calling _Reset
886 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
887 ENTER_GL();
888 GL_EXTCALL(glDeleteRenderbuffersEXT(1, &entry->id));
889 LEAVE_GL();
890 list_remove(&entry->entry);
891 HeapFree(GetProcessHeap(), 0, entry);
893 list_init(&This->renderbuffers);
894 This->current_renderbuffer = NULL;
896 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
897 * destroy it
899 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
900 if(!texture) {
901 ENTER_GL();
902 glDeleteTextures(1, &This->glDescription.textureName);
903 This->glDescription.textureName = 0;
904 glDeleteTextures(1, &This->glDescription.srgbTextureName);
905 This->glDescription.srgbTextureName = 0;
906 LEAVE_GL();
907 } else {
908 IWineD3DBaseTexture_Release(texture);
910 return;
913 /* ******************************************************
914 IWineD3DSurface IWineD3DSurface parts follow
915 ****************************************************** */
917 static void WINAPI IWineD3DSurfaceImpl_GetGlDesc(IWineD3DSurface *iface, glDescriptor **glDescription)
919 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
920 TRACE("(%p) : returning %p\n", This, &This->glDescription);
921 *glDescription = &This->glDescription;
924 /* Read the framebuffer back into the surface */
925 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, CONST RECT *rect, void *dest, UINT pitch) {
926 IWineD3DSwapChainImpl *swapchain;
927 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
928 BYTE *mem;
929 GLint fmt;
930 GLint type;
931 BYTE *row, *top, *bottom;
932 int i;
933 BOOL bpp;
934 RECT local_rect;
935 BOOL srcIsUpsideDown;
936 GLint rowLen = 0;
937 GLint skipPix = 0;
938 GLint skipRow = 0;
940 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
941 static BOOL warned = FALSE;
942 if(!warned) {
943 ERR("The application tries to lock the render target, but render target locking is disabled\n");
944 warned = TRUE;
946 return;
949 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
950 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
951 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
952 * context->last_was_blit set on the unlock.
954 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
955 ENTER_GL();
957 /* Select the correct read buffer, and give some debug output.
958 * There is no need to keep track of the current read buffer or reset it, every part of the code
959 * that reads sets the read buffer as desired.
961 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
963 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
964 TRACE("Locking %#x buffer\n", buffer);
965 glReadBuffer(buffer);
966 checkGLcall("glReadBuffer");
968 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
969 srcIsUpsideDown = FALSE;
970 } else {
971 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
972 * Read from the back buffer
974 TRACE("Locking offscreen render target\n");
975 glReadBuffer(myDevice->offscreenBuffer);
976 srcIsUpsideDown = TRUE;
979 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
980 if(!rect) {
981 local_rect.left = 0;
982 local_rect.top = 0;
983 local_rect.right = This->currentDesc.Width;
984 local_rect.bottom = This->currentDesc.Height;
985 } else {
986 local_rect = *rect;
988 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
990 switch(This->resource.format_desc->format)
992 case WINED3DFMT_P8:
994 if(primary_render_target_is_p8(myDevice)) {
995 /* In case of P8 render targets the index is stored in the alpha component */
996 fmt = GL_ALPHA;
997 type = GL_UNSIGNED_BYTE;
998 mem = dest;
999 bpp = This->resource.format_desc->byte_count;
1000 } else {
1001 /* GL can't return palettized data, so read ARGB pixels into a
1002 * separate block of memory and convert them into palettized format
1003 * in software. Slow, but if the app means to use palettized render
1004 * targets and locks it...
1006 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
1007 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
1008 * for the color channels when palettizing the colors.
1010 fmt = GL_RGB;
1011 type = GL_UNSIGNED_BYTE;
1012 pitch *= 3;
1013 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
1014 if(!mem) {
1015 ERR("Out of memory\n");
1016 LEAVE_GL();
1017 return;
1019 bpp = This->resource.format_desc->byte_count * 3;
1022 break;
1024 default:
1025 mem = dest;
1026 fmt = This->resource.format_desc->glFormat;
1027 type = This->resource.format_desc->glType;
1028 bpp = This->resource.format_desc->byte_count;
1031 if(This->Flags & SFLAG_PBO) {
1032 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
1033 checkGLcall("glBindBufferARB");
1034 if(mem != NULL) {
1035 ERR("mem not null for pbo -- unexpected\n");
1036 mem = NULL;
1040 /* Save old pixel store pack state */
1041 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
1042 checkGLcall("glIntegerv");
1043 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
1044 checkGLcall("glIntegerv");
1045 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
1046 checkGLcall("glIntegerv");
1048 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1049 glPixelStorei(GL_PACK_ROW_LENGTH, This->currentDesc.Width);
1050 checkGLcall("glPixelStorei");
1051 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
1052 checkGLcall("glPixelStorei");
1053 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
1054 checkGLcall("glPixelStorei");
1056 glReadPixels(local_rect.left, (!srcIsUpsideDown) ? (This->currentDesc.Height - local_rect.bottom) : local_rect.top ,
1057 local_rect.right - local_rect.left,
1058 local_rect.bottom - local_rect.top,
1059 fmt, type, mem);
1060 checkGLcall("glReadPixels");
1062 /* Reset previous pixel store pack state */
1063 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
1064 checkGLcall("glPixelStorei");
1065 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
1066 checkGLcall("glPixelStorei");
1067 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
1068 checkGLcall("glPixelStorei");
1070 if(This->Flags & SFLAG_PBO) {
1071 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1072 checkGLcall("glBindBufferARB");
1074 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
1075 * to get a pointer to it and perform the flipping in software. This is a lot
1076 * faster than calling glReadPixels for each line. In case we want more speed
1077 * we should rerender it flipped in a FBO and read the data back from the FBO. */
1078 if(!srcIsUpsideDown) {
1079 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1080 checkGLcall("glBindBufferARB");
1082 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1083 checkGLcall("glMapBufferARB");
1087 /* TODO: Merge this with the palettization loop below for P8 targets */
1088 if(!srcIsUpsideDown) {
1089 UINT len, off;
1090 /* glReadPixels returns the image upside down, and there is no way to prevent this.
1091 Flip the lines in software */
1092 len = (local_rect.right - local_rect.left) * bpp;
1093 off = local_rect.left * bpp;
1095 row = HeapAlloc(GetProcessHeap(), 0, len);
1096 if(!row) {
1097 ERR("Out of memory\n");
1098 if (This->resource.format_desc->format == WINED3DFMT_P8) HeapFree(GetProcessHeap(), 0, mem);
1099 LEAVE_GL();
1100 return;
1103 top = mem + pitch * local_rect.top;
1104 bottom = mem + pitch * (local_rect.bottom - 1);
1105 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
1106 memcpy(row, top + off, len);
1107 memcpy(top + off, bottom + off, len);
1108 memcpy(bottom + off, row, len);
1109 top += pitch;
1110 bottom -= pitch;
1112 HeapFree(GetProcessHeap(), 0, row);
1114 /* Unmap the temp PBO buffer */
1115 if(This->Flags & SFLAG_PBO) {
1116 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1117 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1121 LEAVE_GL();
1123 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
1124 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
1125 * the same color but we have no choice.
1126 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
1128 if ((This->resource.format_desc->format == WINED3DFMT_P8) && !primary_render_target_is_p8(myDevice))
1130 const PALETTEENTRY *pal = NULL;
1131 DWORD width = pitch / 3;
1132 int x, y, c;
1134 if(This->palette) {
1135 pal = This->palette->palents;
1136 } else {
1137 ERR("Palette is missing, cannot perform inverse palette lookup\n");
1138 HeapFree(GetProcessHeap(), 0, mem);
1139 return ;
1142 for(y = local_rect.top; y < local_rect.bottom; y++) {
1143 for(x = local_rect.left; x < local_rect.right; x++) {
1144 /* start lines pixels */
1145 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
1146 const BYTE *green = blue + 1;
1147 const BYTE *red = green + 1;
1149 for(c = 0; c < 256; c++) {
1150 if(*red == pal[c].peRed &&
1151 *green == pal[c].peGreen &&
1152 *blue == pal[c].peBlue)
1154 *((BYTE *) dest + y * width + x) = c;
1155 break;
1160 HeapFree(GetProcessHeap(), 0, mem);
1164 /* Read the framebuffer contents into a texture */
1165 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This, BOOL srgb)
1167 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1168 IWineD3DSwapChainImpl *swapchain;
1169 int bpp;
1170 GLenum format, internal, type;
1171 CONVERT_TYPES convert;
1172 GLint prevRead;
1173 BOOL alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
1175 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
1177 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
1178 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
1179 * states in the stateblock, and no driver was found yet that had bugs in that regard.
1181 ActivateContext(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
1182 surface_bind_and_dirtify(This, srgb);
1184 ENTER_GL();
1185 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1186 LEAVE_GL();
1188 /* Select the correct read buffer, and give some debug output.
1189 * There is no need to keep track of the current read buffer or reset it, every part of the code
1190 * that reads sets the read buffer as desired.
1192 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain)))
1194 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1195 TRACE("Locking %#x buffer\n", buffer);
1197 ENTER_GL();
1198 glReadBuffer(buffer);
1199 checkGLcall("glReadBuffer");
1200 LEAVE_GL();
1202 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
1203 } else {
1204 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1205 * Read from the back buffer
1207 TRACE("Locking offscreen render target\n");
1208 ENTER_GL();
1209 glReadBuffer(device->offscreenBuffer);
1210 checkGLcall("glReadBuffer");
1211 LEAVE_GL();
1214 if(!(This->Flags & alloc_flag)) {
1215 surface_allocate_surface(This, internal, This->pow2Width,
1216 This->pow2Height, format, type);
1217 This->Flags |= alloc_flag;
1220 ENTER_GL();
1221 /* If !SrcIsUpsideDown we should flip the surface.
1222 * This can be done using glCopyTexSubImage2D but this
1223 * is VERY slow, so don't do that. We should prevent
1224 * this code from getting called in such cases or perhaps
1225 * we can use FBOs */
1227 glCopyTexSubImage2D(This->glDescription.target,
1228 This->glDescription.level,
1229 0, 0, 0, 0,
1230 This->currentDesc.Width,
1231 This->currentDesc.Height);
1232 checkGLcall("glCopyTexSubImage2D");
1234 glReadBuffer(prevRead);
1235 checkGLcall("glReadBuffer");
1237 LEAVE_GL();
1238 TRACE("Updated target %d\n", This->glDescription.target);
1241 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This) {
1242 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
1243 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
1244 * changed
1246 if(!(This->Flags & SFLAG_DYNLOCK)) {
1247 This->lockCount++;
1248 /* MAXLOCKCOUNT is defined in wined3d_private.h */
1249 if(This->lockCount > MAXLOCKCOUNT) {
1250 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
1251 This->Flags |= SFLAG_DYNLOCK;
1255 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
1256 * Also don't create a PBO for systemmem surfaces.
1258 if(GL_SUPPORT(ARB_PIXEL_BUFFER_OBJECT) && (This->Flags & SFLAG_DYNLOCK) && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2)) && (This->resource.pool != WINED3DPOOL_SYSTEMMEM)) {
1259 GLenum error;
1260 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1262 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1263 ENTER_GL();
1265 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
1266 error = glGetError();
1267 if(This->pbo == 0 || error != GL_NO_ERROR) {
1268 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
1271 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
1273 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1274 checkGLcall("glBindBufferARB");
1276 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1277 checkGLcall("glBufferDataARB");
1279 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1280 checkGLcall("glBindBufferARB");
1282 /* We don't need the system memory anymore and we can't even use it for PBOs */
1283 if(!(This->Flags & SFLAG_CLIENT)) {
1284 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1285 This->resource.heapMemory = NULL;
1287 This->resource.allocatedMemory = NULL;
1288 This->Flags |= SFLAG_PBO;
1289 LEAVE_GL();
1290 } else if(!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO)) {
1291 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1292 * or a pbo to map
1294 if(!This->resource.heapMemory) {
1295 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1297 This->resource.allocatedMemory =
1298 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1299 if(This->Flags & SFLAG_INSYSMEM) {
1300 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1305 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1306 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1307 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1308 const RECT *pass_rect = pRect;
1310 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1312 /* This is also done in the base class, but we have to verify this before loading any data from
1313 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1314 * may interfere, and all other bad things may happen
1316 if (This->Flags & SFLAG_LOCKED) {
1317 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1318 return WINED3DERR_INVALIDCALL;
1320 This->Flags |= SFLAG_LOCKED;
1322 if (!(This->Flags & SFLAG_LOCKABLE))
1324 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1327 if (Flags & WINED3DLOCK_DISCARD) {
1328 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1329 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1330 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1331 This->Flags |= SFLAG_INSYSMEM;
1332 goto lock_end;
1335 if (This->Flags & SFLAG_INSYSMEM) {
1336 TRACE("Local copy is up to date, not downloading data\n");
1337 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1338 goto lock_end;
1341 /* IWineD3DSurface_LoadLocation() does not check if the rectangle specifies
1342 * the full surface. Most callers don't need that, so do it here. */
1343 if (pRect && pRect->top == 0 && pRect->left == 0
1344 && pRect->right == This->currentDesc.Width
1345 && pRect->bottom == This->currentDesc.Height)
1347 pass_rect = NULL;
1350 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
1351 && ((This->Flags & SFLAG_SWAPCHAIN) || iface == myDevice->render_targets[0])))
1353 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1356 lock_end:
1357 if(This->Flags & SFLAG_PBO) {
1358 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1359 ENTER_GL();
1360 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1361 checkGLcall("glBindBufferARB");
1363 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1364 if(This->resource.allocatedMemory) {
1365 ERR("The surface already has PBO memory allocated!\n");
1368 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1369 checkGLcall("glMapBufferARB");
1371 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1372 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1373 checkGLcall("glBindBufferARB");
1375 LEAVE_GL();
1378 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1379 /* Don't dirtify */
1380 } else {
1381 IWineD3DBaseTexture *pBaseTexture;
1383 * Dirtify on lock
1384 * as seen in msdn docs
1386 surface_add_dirty_rect(iface, pRect);
1388 /** Dirtify Container if needed */
1389 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1390 TRACE("Making container dirty\n");
1391 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1392 IWineD3DBaseTexture_Release(pBaseTexture);
1393 } else {
1394 TRACE("Surface is standalone, no need to dirty the container\n");
1398 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1401 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1402 GLint prev_store;
1403 GLint prev_rasterpos[4];
1404 GLint skipBytes = 0;
1405 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1406 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1407 IWineD3DSwapChainImpl *swapchain;
1409 /* Activate the correct context for the render target */
1410 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1411 ENTER_GL();
1413 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DSwapChain, (void **)&swapchain))) {
1414 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *) This, (IWineD3DSwapChain *)swapchain);
1415 TRACE("Unlocking %#x buffer\n", buffer);
1416 glDrawBuffer(buffer);
1417 checkGLcall("glDrawBuffer");
1419 IWineD3DSwapChain_Release((IWineD3DSwapChain *)swapchain);
1420 } else {
1421 /* Primary offscreen render target */
1422 TRACE("Offscreen render target\n");
1423 glDrawBuffer(myDevice->offscreenBuffer);
1424 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1427 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1428 checkGLcall("glIntegerv");
1429 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1430 checkGLcall("glIntegerv");
1431 glPixelZoom(1.0, -1.0);
1432 checkGLcall("glPixelZoom");
1434 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1435 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1436 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1438 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1439 checkGLcall("glRasterPos2f");
1441 /* Some drivers(radeon dri, others?) don't like exceptions during
1442 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1443 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1444 * catch to put the dib section in InSync mode, which leads to a crash
1445 * and a blocked x server on my radeon card.
1447 * The following lines read the dib section so it is put in InSync mode
1448 * before glDrawPixels is called and the crash is prevented. There won't
1449 * be any interfering gdi accesses, because UnlockRect is called from
1450 * ReleaseDC, and the app won't use the dc any more afterwards.
1452 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1453 volatile BYTE read;
1454 read = This->resource.allocatedMemory[0];
1457 if(This->Flags & SFLAG_PBO) {
1458 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1459 checkGLcall("glBindBufferARB");
1462 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1463 if(This->Flags & SFLAG_LOCKED) {
1464 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1465 (This->lockedRect.bottom - This->lockedRect.top)-1,
1466 fmt, type,
1467 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1468 checkGLcall("glDrawPixels");
1469 } else {
1470 glDrawPixels(This->currentDesc.Width,
1471 This->currentDesc.Height,
1472 fmt, type, mem);
1473 checkGLcall("glDrawPixels");
1476 if(This->Flags & SFLAG_PBO) {
1477 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1478 checkGLcall("glBindBufferARB");
1481 glPixelZoom(1.0,1.0);
1482 checkGLcall("glPixelZoom");
1484 glRasterPos3iv(&prev_rasterpos[0]);
1485 checkGLcall("glRasterPos3iv");
1487 /* Reset to previous pack row length */
1488 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1489 checkGLcall("glPixelStorei GL_UNPACK_ROW_LENGTH");
1491 if(!swapchain) {
1492 glDrawBuffer(myDevice->offscreenBuffer);
1493 checkGLcall("glDrawBuffer(myDevice->offscreenBuffer)");
1494 } else if(swapchain->backBuffer) {
1495 glDrawBuffer(GL_BACK);
1496 checkGLcall("glDrawBuffer(GL_BACK)");
1497 } else {
1498 glDrawBuffer(GL_FRONT);
1499 checkGLcall("glDrawBuffer(GL_FRONT)");
1501 LEAVE_GL();
1503 return;
1506 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1507 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1508 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
1509 BOOL fullsurface;
1511 if (!(This->Flags & SFLAG_LOCKED)) {
1512 WARN("trying to Unlock an unlocked surf@%p\n", This);
1513 return WINEDDERR_NOTLOCKED;
1516 if (This->Flags & SFLAG_PBO) {
1517 TRACE("Freeing PBO memory\n");
1518 ActivateContext(myDevice, myDevice->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
1519 ENTER_GL();
1520 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1521 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1522 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1523 checkGLcall("glUnmapBufferARB");
1524 LEAVE_GL();
1525 This->resource.allocatedMemory = NULL;
1528 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1530 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1531 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1532 goto unlock_end;
1535 if ((This->Flags & SFLAG_SWAPCHAIN) || (myDevice->render_targets && iface == myDevice->render_targets[0]))
1537 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1538 static BOOL warned = FALSE;
1539 if(!warned) {
1540 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1541 warned = TRUE;
1543 goto unlock_end;
1546 if(This->dirtyRect.left == 0 &&
1547 This->dirtyRect.top == 0 &&
1548 This->dirtyRect.right == This->currentDesc.Width &&
1549 This->dirtyRect.bottom == This->currentDesc.Height) {
1550 fullsurface = TRUE;
1551 } else {
1552 /* TODO: Proper partial rectangle tracking */
1553 fullsurface = FALSE;
1554 This->Flags |= SFLAG_INSYSMEM;
1557 switch(wined3d_settings.rendertargetlock_mode) {
1558 case RTL_READTEX:
1559 case RTL_TEXTEX:
1560 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1561 /* drop through */
1563 case RTL_AUTO:
1564 case RTL_READDRAW:
1565 case RTL_TEXDRAW:
1566 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1567 break;
1570 if(!fullsurface) {
1571 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1572 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1573 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1574 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1575 * not fully up to date because only a subrectangle was read in LockRect.
1577 This->Flags &= ~SFLAG_INSYSMEM;
1578 This->Flags |= SFLAG_INDRAWABLE;
1581 This->dirtyRect.left = This->currentDesc.Width;
1582 This->dirtyRect.top = This->currentDesc.Height;
1583 This->dirtyRect.right = 0;
1584 This->dirtyRect.bottom = 0;
1585 } else if(iface == myDevice->stencilBufferTarget) {
1586 FIXME("Depth Stencil buffer locking is not implemented\n");
1587 } else {
1588 /* The rest should be a normal texture */
1589 IWineD3DBaseTextureImpl *impl;
1590 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1591 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1592 * states need resetting
1594 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1595 if(impl->baseTexture.bindCount) {
1596 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1598 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1602 unlock_end:
1603 This->Flags &= ~SFLAG_LOCKED;
1604 memset(&This->lockedRect, 0, sizeof(RECT));
1606 /* Overlays have to be redrawn manually after changes with the GL implementation */
1607 if(This->overlay_dest) {
1608 IWineD3DSurface_DrawOverlay(iface);
1610 return WINED3D_OK;
1613 static HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC)
1615 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1616 WINED3DLOCKED_RECT lock;
1617 HRESULT hr;
1618 RGBQUAD col[256];
1620 TRACE("(%p)->(%p)\n",This,pHDC);
1622 if(This->Flags & SFLAG_USERPTR) {
1623 ERR("Not supported on surfaces with an application-provided surfaces\n");
1624 return WINEDDERR_NODC;
1627 /* Give more detailed info for ddraw */
1628 if (This->Flags & SFLAG_DCINUSE)
1629 return WINEDDERR_DCALREADYCREATED;
1631 /* Can't GetDC if the surface is locked */
1632 if (This->Flags & SFLAG_LOCKED)
1633 return WINED3DERR_INVALIDCALL;
1635 /* According to Direct3D9 docs, only these formats are supported */
1636 if (((IWineD3DImpl *)This->resource.wineD3DDevice->wineD3D)->dxVersion > 7) {
1637 if (This->resource.format_desc->format != WINED3DFMT_R5G6B5
1638 && This->resource.format_desc->format != WINED3DFMT_X1R5G5B5
1639 && This->resource.format_desc->format != WINED3DFMT_R8G8B8
1640 && This->resource.format_desc->format != WINED3DFMT_X8R8G8B8)
1641 return WINED3DERR_INVALIDCALL;
1644 memset(&lock, 0, sizeof(lock)); /* To be sure */
1646 /* Create a DIB section if there isn't a hdc yet */
1647 if(!This->hDC) {
1648 IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1649 if(This->Flags & SFLAG_CLIENT) {
1650 surface_internal_preload(iface, SRGB_RGB);
1653 /* Use the dib section from now on if we are not using a PBO */
1654 if(!(This->Flags & SFLAG_PBO))
1655 This->resource.allocatedMemory = This->dib.bitmap_data;
1658 /* Lock the surface */
1659 hr = IWineD3DSurface_LockRect(iface,
1660 &lock,
1661 NULL,
1664 if(This->Flags & SFLAG_PBO) {
1665 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1666 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1669 if(FAILED(hr)) {
1670 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1671 /* keep the dib section */
1672 return hr;
1675 if (This->resource.format_desc->format == WINED3DFMT_P8
1676 || This->resource.format_desc->format == WINED3DFMT_A8P8)
1678 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1679 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1680 unsigned int n;
1681 const PALETTEENTRY *pal = NULL;
1683 if(This->palette) {
1684 pal = This->palette->palents;
1685 } else {
1686 IWineD3DSurfaceImpl *dds_primary;
1687 IWineD3DSwapChainImpl *swapchain;
1688 swapchain = (IWineD3DSwapChainImpl *)This->resource.wineD3DDevice->swapchains[0];
1689 dds_primary = (IWineD3DSurfaceImpl *)swapchain->frontBuffer;
1690 if (dds_primary && dds_primary->palette)
1691 pal = dds_primary->palette->palents;
1694 if (pal) {
1695 for (n=0; n<256; n++) {
1696 col[n].rgbRed = pal[n].peRed;
1697 col[n].rgbGreen = pal[n].peGreen;
1698 col[n].rgbBlue = pal[n].peBlue;
1699 col[n].rgbReserved = 0;
1701 SetDIBColorTable(This->hDC, 0, 256, col);
1705 *pHDC = This->hDC;
1706 TRACE("returning %p\n",*pHDC);
1707 This->Flags |= SFLAG_DCINUSE;
1709 return WINED3D_OK;
1712 static HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC)
1714 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1716 TRACE("(%p)->(%p)\n",This,hDC);
1718 if (!(This->Flags & SFLAG_DCINUSE))
1719 return WINEDDERR_NODC;
1721 if (This->hDC !=hDC) {
1722 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1723 return WINEDDERR_NODC;
1726 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1727 /* Copy the contents of the DIB over to the PBO */
1728 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1731 /* we locked first, so unlock now */
1732 IWineD3DSurface_UnlockRect(iface);
1734 This->Flags &= ~SFLAG_DCINUSE;
1736 return WINED3D_OK;
1739 /* ******************************************************
1740 IWineD3DSurface Internal (No mapping to directx api) parts follow
1741 ****************************************************** */
1743 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) {
1744 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1745 const struct GlPixelFormatDesc *glDesc = This->resource.format_desc;
1746 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1748 /* Default values: From the surface */
1749 *format = glDesc->glFormat;
1750 *type = glDesc->glType;
1751 *convert = NO_CONVERSION;
1752 *target_bpp = glDesc->byte_count;
1754 if(srgb_mode) {
1755 *internal = glDesc->glGammaInternal;
1757 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET
1758 && !(This->Flags & SFLAG_SWAPCHAIN))
1760 *internal = glDesc->rtInternal;
1761 } else {
1762 *internal = glDesc->glInternal;
1765 /* Ok, now look if we have to do any conversion */
1766 switch(This->resource.format_desc->format)
1768 case WINED3DFMT_P8:
1769 /* ****************
1770 Paletted Texture
1771 **************** */
1773 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
1774 * of the two is available make sure texturing is requested as neither of the two works in
1775 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
1776 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
1777 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
1778 * conflicts with this.
1780 if( !(GL_SUPPORT(EXT_PALETTED_TEXTURE) ||
1781 (GL_SUPPORT(ARB_FRAGMENT_PROGRAM) &&
1782 device->render_targets &&
1783 This == (IWineD3DSurfaceImpl*)device->render_targets[0])) ||
1784 colorkey_active || !use_texturing ) {
1785 *format = GL_RGBA;
1786 *internal = GL_RGBA;
1787 *type = GL_UNSIGNED_BYTE;
1788 *target_bpp = 4;
1789 if(colorkey_active) {
1790 *convert = CONVERT_PALETTED_CK;
1791 } else {
1792 *convert = CONVERT_PALETTED;
1795 else if(!GL_SUPPORT(EXT_PALETTED_TEXTURE) && GL_SUPPORT(ARB_FRAGMENT_PROGRAM)) {
1796 *format = GL_ALPHA;
1797 *type = GL_UNSIGNED_BYTE;
1798 *target_bpp = 1;
1801 break;
1803 case WINED3DFMT_R3G3B2:
1804 /* **********************
1805 GL_UNSIGNED_BYTE_3_3_2
1806 ********************** */
1807 if (colorkey_active) {
1808 /* This texture format will never be used.. So do not care about color keying
1809 up until the point in time it will be needed :-) */
1810 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
1812 break;
1814 case WINED3DFMT_R5G6B5:
1815 if (colorkey_active) {
1816 *convert = CONVERT_CK_565;
1817 *format = GL_RGBA;
1818 *internal = GL_RGB5_A1;
1819 *type = GL_UNSIGNED_SHORT_5_5_5_1;
1821 break;
1823 case WINED3DFMT_X1R5G5B5:
1824 if (colorkey_active) {
1825 *convert = CONVERT_CK_5551;
1826 *format = GL_BGRA;
1827 *internal = GL_RGB5_A1;
1828 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
1830 break;
1832 case WINED3DFMT_R8G8B8:
1833 if (colorkey_active) {
1834 *convert = CONVERT_CK_RGB24;
1835 *format = GL_RGBA;
1836 *internal = GL_RGBA8;
1837 *type = GL_UNSIGNED_INT_8_8_8_8;
1838 *target_bpp = 4;
1840 break;
1842 case WINED3DFMT_X8R8G8B8:
1843 if (colorkey_active) {
1844 *convert = CONVERT_RGB32_888;
1845 *format = GL_RGBA;
1846 *internal = GL_RGBA8;
1847 *type = GL_UNSIGNED_INT_8_8_8_8;
1849 break;
1851 case WINED3DFMT_R8G8_SNORM:
1852 if (GL_SUPPORT(NV_TEXTURE_SHADER)) break;
1853 *convert = CONVERT_V8U8;
1854 *format = GL_BGR;
1855 *type = GL_UNSIGNED_BYTE;
1856 *target_bpp = 3;
1857 break;
1859 case WINED3DFMT_L6V5U5:
1860 *convert = CONVERT_L6V5U5;
1861 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1862 *target_bpp = 3;
1863 /* Use format and types from table */
1864 } else {
1865 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
1866 *target_bpp = 2;
1867 *format = GL_RGB;
1868 *type = GL_UNSIGNED_SHORT_5_6_5;
1870 break;
1872 case WINED3DFMT_X8L8V8U8:
1873 *convert = CONVERT_X8L8V8U8;
1874 *target_bpp = 4;
1875 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
1876 /* Use formats from gl table. It is a bit unfortunate, but the conversion
1877 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
1878 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
1879 * the needed type and format parameter, so the internal format contains a
1880 * 4th component, which is returned as alpha
1882 } else {
1883 *format = GL_BGRA;
1884 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
1886 break;
1888 case WINED3DFMT_R8G8B8A8_SNORM:
1889 if (GL_SUPPORT(NV_TEXTURE_SHADER)) break;
1890 *convert = CONVERT_Q8W8V8U8;
1891 *format = GL_BGRA;
1892 *type = GL_UNSIGNED_BYTE;
1893 *target_bpp = 4;
1894 break;
1896 case WINED3DFMT_R16G16_SNORM:
1897 if (GL_SUPPORT(NV_TEXTURE_SHADER)) break;
1898 *convert = CONVERT_V16U16;
1899 *format = GL_BGR;
1900 *type = GL_UNSIGNED_SHORT;
1901 *target_bpp = 6;
1902 break;
1904 case WINED3DFMT_A4L4:
1905 /* A4L4 exists as an internal gl format, but for some reason there is not
1906 * format+type combination to load it. Thus convert it to A8L8, then load it
1907 * with A4L4 internal, but A8L8 format+type
1909 *convert = CONVERT_A4L4;
1910 *format = GL_LUMINANCE_ALPHA;
1911 *type = GL_UNSIGNED_BYTE;
1912 *target_bpp = 2;
1913 break;
1915 case WINED3DFMT_R16G16_UNORM:
1916 *convert = CONVERT_G16R16;
1917 *format = GL_RGB;
1918 *type = GL_UNSIGNED_SHORT;
1919 *target_bpp = 6;
1920 break;
1922 case WINED3DFMT_R16G16_FLOAT:
1923 *convert = CONVERT_R16G16F;
1924 *format = GL_RGB;
1925 *type = GL_HALF_FLOAT_ARB;
1926 *target_bpp = 6;
1927 break;
1929 case WINED3DFMT_R32G32_FLOAT:
1930 *convert = CONVERT_R32G32F;
1931 *format = GL_RGB;
1932 *type = GL_FLOAT;
1933 *target_bpp = 12;
1934 break;
1936 case WINED3DFMT_D15S1:
1937 if (GL_SUPPORT(EXT_PACKED_DEPTH_STENCIL))
1939 *convert = CONVERT_D15S1;
1940 *target_bpp = 4;
1942 break;
1944 case WINED3DFMT_D24X4S4:
1945 if (GL_SUPPORT(EXT_PACKED_DEPTH_STENCIL))
1947 *convert = CONVERT_D24X4S4;
1949 break;
1951 case WINED3DFMT_D24FS8:
1952 if (GL_SUPPORT(ARB_DEPTH_BUFFER_FLOAT))
1954 *convert = CONVERT_D24FS8;
1955 *target_bpp = 8;
1957 break;
1959 default:
1960 break;
1963 return WINED3D_OK;
1966 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey)
1968 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
1969 IWineD3DPaletteImpl *pal = This->palette;
1970 BOOL index_in_alpha = FALSE;
1971 unsigned int i;
1973 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
1974 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
1975 * is slow. Further RGB->P8 conversion is not possible because palettes can have
1976 * duplicate entries. Store the color key in the unused alpha component to speed the
1977 * download up and to make conversion unneeded. */
1978 index_in_alpha = primary_render_target_is_p8(device);
1980 if (!pal)
1982 UINT dxVersion = ((IWineD3DImpl *)device->wineD3D)->dxVersion;
1984 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
1985 if (dxVersion <= 7)
1987 ERR("This code should never get entered for DirectDraw!, expect problems\n");
1988 if (index_in_alpha)
1990 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
1991 * there's no palette at this time. */
1992 for (i = 0; i < 256; i++) table[i][3] = i;
1995 else
1997 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
1998 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
1999 * capability flag is present (wine does advertise this capability) */
2000 for (i = 0; i < 256; ++i)
2002 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2003 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2004 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2005 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2009 else
2011 TRACE("Using surface palette %p\n", pal);
2012 /* Get the surface's palette */
2013 for (i = 0; i < 256; ++i)
2015 table[i][0] = pal->palents[i].peRed;
2016 table[i][1] = pal->palents[i].peGreen;
2017 table[i][2] = pal->palents[i].peBlue;
2019 /* When index_in_alpha is set the palette index is stored in the
2020 * alpha component. In case of a readback we can then read
2021 * GL_ALPHA. Color keying is handled in BltOverride using a
2022 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
2023 * color key itself is passed to glAlphaFunc in other cases the
2024 * alpha component of pixels that should be masked away is set to 0. */
2025 if (index_in_alpha)
2027 table[i][3] = i;
2029 else if (colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue)
2030 && (i <= This->SrcBltCKey.dwColorSpaceHighValue))
2032 table[i][3] = 0x00;
2034 else if(pal->Flags & WINEDDPCAPS_ALPHA)
2036 table[i][3] = pal->palents[i].peFlags;
2038 else
2040 table[i][3] = 0xFF;
2046 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
2047 UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This)
2049 const BYTE *source;
2050 BYTE *dest;
2051 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
2053 switch (convert) {
2054 case NO_CONVERSION:
2056 memcpy(dst, src, pitch * height);
2057 break;
2059 case CONVERT_PALETTED:
2060 case CONVERT_PALETTED_CK:
2062 IWineD3DPaletteImpl* pal = This->palette;
2063 BYTE table[256][4];
2064 unsigned int x, y;
2066 if( pal == NULL) {
2067 /* TODO: If we are a sublevel, try to get the palette from level 0 */
2070 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2072 for (y = 0; y < height; y++)
2074 source = src + pitch * y;
2075 dest = dst + outpitch * y;
2076 /* This is an 1 bpp format, using the width here is fine */
2077 for (x = 0; x < width; x++) {
2078 BYTE color = *source++;
2079 *dest++ = table[color][0];
2080 *dest++ = table[color][1];
2081 *dest++ = table[color][2];
2082 *dest++ = table[color][3];
2086 break;
2088 case CONVERT_CK_565:
2090 /* Converting the 565 format in 5551 packed to emulate color-keying.
2092 Note : in all these conversion, it would be best to average the averaging
2093 pixels to get the color of the pixel that will be color-keyed to
2094 prevent 'color bleeding'. This will be done later on if ever it is
2095 too visible.
2097 Note2: Nvidia documents say that their driver does not support alpha + color keying
2098 on the same surface and disables color keying in such a case
2100 unsigned int x, y;
2101 const WORD *Source;
2102 WORD *Dest;
2104 TRACE("Color keyed 565\n");
2106 for (y = 0; y < height; y++) {
2107 Source = (const WORD *)(src + y * pitch);
2108 Dest = (WORD *) (dst + y * outpitch);
2109 for (x = 0; x < width; x++ ) {
2110 WORD color = *Source++;
2111 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
2112 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2113 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2114 *Dest |= 0x0001;
2116 Dest++;
2120 break;
2122 case CONVERT_CK_5551:
2124 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
2125 unsigned int x, y;
2126 const WORD *Source;
2127 WORD *Dest;
2128 TRACE("Color keyed 5551\n");
2129 for (y = 0; y < height; y++) {
2130 Source = (const WORD *)(src + y * pitch);
2131 Dest = (WORD *) (dst + y * outpitch);
2132 for (x = 0; x < width; x++ ) {
2133 WORD color = *Source++;
2134 *Dest = color;
2135 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2136 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2137 *Dest |= (1 << 15);
2139 else {
2140 *Dest &= ~(1 << 15);
2142 Dest++;
2146 break;
2148 case CONVERT_CK_RGB24:
2150 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
2151 unsigned int x, y;
2152 for (y = 0; y < height; y++)
2154 source = src + pitch * y;
2155 dest = dst + outpitch * y;
2156 for (x = 0; x < width; x++) {
2157 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
2158 DWORD dstcolor = color << 8;
2159 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2160 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2161 dstcolor |= 0xff;
2163 *(DWORD*)dest = dstcolor;
2164 source += 3;
2165 dest += 4;
2169 break;
2171 case CONVERT_RGB32_888:
2173 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
2174 unsigned int x, y;
2175 for (y = 0; y < height; y++)
2177 source = src + pitch * y;
2178 dest = dst + outpitch * y;
2179 for (x = 0; x < width; x++) {
2180 DWORD color = 0xffffff & *(const DWORD*)source;
2181 DWORD dstcolor = color << 8;
2182 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2183 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2184 dstcolor |= 0xff;
2186 *(DWORD*)dest = dstcolor;
2187 source += 4;
2188 dest += 4;
2192 break;
2194 case CONVERT_V8U8:
2196 unsigned int x, y;
2197 const short *Source;
2198 unsigned char *Dest;
2199 for(y = 0; y < height; y++) {
2200 Source = (const short *)(src + y * pitch);
2201 Dest = dst + y * outpitch;
2202 for (x = 0; x < width; x++ ) {
2203 long color = (*Source++);
2204 /* B */ Dest[0] = 0xff;
2205 /* G */ Dest[1] = (color >> 8) + 128; /* V */
2206 /* R */ Dest[2] = (color) + 128; /* U */
2207 Dest += 3;
2210 break;
2213 case CONVERT_V16U16:
2215 unsigned int x, y;
2216 const DWORD *Source;
2217 unsigned short *Dest;
2218 for(y = 0; y < height; y++) {
2219 Source = (const DWORD *)(src + y * pitch);
2220 Dest = (unsigned short *) (dst + y * outpitch);
2221 for (x = 0; x < width; x++ ) {
2222 DWORD color = (*Source++);
2223 /* B */ Dest[0] = 0xffff;
2224 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
2225 /* R */ Dest[2] = (color ) + 32768; /* U */
2226 Dest += 3;
2229 break;
2232 case CONVERT_Q8W8V8U8:
2234 unsigned int x, y;
2235 const DWORD *Source;
2236 unsigned char *Dest;
2237 for(y = 0; y < height; y++) {
2238 Source = (const DWORD *)(src + y * pitch);
2239 Dest = dst + y * outpitch;
2240 for (x = 0; x < width; x++ ) {
2241 long color = (*Source++);
2242 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
2243 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2244 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2245 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
2246 Dest += 4;
2249 break;
2252 case CONVERT_L6V5U5:
2254 unsigned int x, y;
2255 const WORD *Source;
2256 unsigned char *Dest;
2258 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2259 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
2260 * fixed function and shaders without further conversion once the surface is
2261 * loaded
2263 for(y = 0; y < height; y++) {
2264 Source = (const WORD *)(src + y * pitch);
2265 Dest = dst + y * outpitch;
2266 for (x = 0; x < width; x++ ) {
2267 short color = (*Source++);
2268 unsigned char l = ((color >> 10) & 0xfc);
2269 char v = ((color >> 5) & 0x3e);
2270 char u = ((color ) & 0x1f);
2272 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
2273 * and doubles the positive range. Thus shift left only once, gl does the 2nd
2274 * shift. GL reads a signed value and converts it into an unsigned value.
2276 /* M */ Dest[2] = l << 1;
2278 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
2279 * from 5 bit values to 8 bit values.
2281 /* V */ Dest[1] = v << 3;
2282 /* U */ Dest[0] = u << 3;
2283 Dest += 3;
2286 } else {
2287 for(y = 0; y < height; y++) {
2288 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
2289 Source = (const WORD *)(src + y * pitch);
2290 for (x = 0; x < width; x++ ) {
2291 short color = (*Source++);
2292 unsigned char l = ((color >> 10) & 0xfc);
2293 short v = ((color >> 5) & 0x3e);
2294 short u = ((color ) & 0x1f);
2295 short v_conv = v + 16;
2296 short u_conv = u + 16;
2298 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
2299 Dest_s += 1;
2303 break;
2306 case CONVERT_X8L8V8U8:
2308 unsigned int x, y;
2309 const DWORD *Source;
2310 unsigned char *Dest;
2312 if(GL_SUPPORT(NV_TEXTURE_SHADER)) {
2313 /* This implementation works with the fixed function pipeline and shaders
2314 * without further modification after converting the surface.
2316 for(y = 0; y < height; y++) {
2317 Source = (const DWORD *)(src + y * pitch);
2318 Dest = dst + y * outpitch;
2319 for (x = 0; x < width; x++ ) {
2320 long color = (*Source++);
2321 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
2322 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
2323 /* U */ Dest[0] = (color & 0xff); /* U */
2324 /* I */ Dest[3] = 255; /* X */
2325 Dest += 4;
2328 } else {
2329 /* Doesn't work correctly with the fixed function pipeline, but can work in
2330 * shaders if the shader is adjusted. (There's no use for this format in gl's
2331 * standard fixed function pipeline anyway).
2333 for(y = 0; y < height; y++) {
2334 Source = (const DWORD *)(src + y * pitch);
2335 Dest = dst + y * outpitch;
2336 for (x = 0; x < width; x++ ) {
2337 long color = (*Source++);
2338 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
2339 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2340 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2341 Dest += 4;
2345 break;
2348 case CONVERT_A4L4:
2350 unsigned int x, y;
2351 const unsigned char *Source;
2352 unsigned char *Dest;
2353 for(y = 0; y < height; y++) {
2354 Source = src + y * pitch;
2355 Dest = dst + y * outpitch;
2356 for (x = 0; x < width; x++ ) {
2357 unsigned char color = (*Source++);
2358 /* A */ Dest[1] = (color & 0xf0) << 0;
2359 /* L */ Dest[0] = (color & 0x0f) << 4;
2360 Dest += 2;
2363 break;
2366 case CONVERT_G16R16:
2367 case CONVERT_R16G16F:
2369 unsigned int x, y;
2370 const WORD *Source;
2371 WORD *Dest;
2373 for(y = 0; y < height; y++) {
2374 Source = (const WORD *)(src + y * pitch);
2375 Dest = (WORD *) (dst + y * outpitch);
2376 for (x = 0; x < width; x++ ) {
2377 WORD green = (*Source++);
2378 WORD red = (*Source++);
2379 Dest[0] = green;
2380 Dest[1] = red;
2381 /* Strictly speaking not correct for R16G16F, but it doesn't matter because the
2382 * shader overwrites it anyway
2384 Dest[2] = 0xffff;
2385 Dest += 3;
2388 break;
2391 case CONVERT_R32G32F:
2393 unsigned int x, y;
2394 const float *Source;
2395 float *Dest;
2396 for(y = 0; y < height; y++) {
2397 Source = (const float *)(src + y * pitch);
2398 Dest = (float *) (dst + y * outpitch);
2399 for (x = 0; x < width; x++ ) {
2400 float green = (*Source++);
2401 float red = (*Source++);
2402 Dest[0] = green;
2403 Dest[1] = red;
2404 Dest[2] = 1.0;
2405 Dest += 3;
2408 break;
2411 case CONVERT_D15S1:
2413 unsigned int x, y;
2415 for (y = 0; y < height; ++y)
2417 const WORD *source = (const WORD *)(src + y * pitch);
2418 DWORD *dest = (DWORD *)(dst + y * outpitch);
2420 for (x = 0; x < width; ++x)
2422 /* The depth data is normalized, so needs to be scaled,
2423 * the stencil data isn't. Scale depth data by
2424 * (2^24-1)/(2^15-1) ~~ (2^9 + 2^-6). */
2425 WORD d15 = source[x] >> 1;
2426 DWORD d24 = (d15 << 9) + (d15 >> 6);
2427 dest[x] = (d24 << 8) | (source[x] & 0x1);
2430 break;
2433 case CONVERT_D24X4S4:
2435 unsigned int x, y;
2437 for (y = 0; y < height; ++y)
2439 const DWORD *source = (const DWORD *)(src + y * pitch);
2440 DWORD *dest = (DWORD *)(dst + y * outpitch);
2442 for (x = 0; x < width; ++x)
2444 /* Just need to clear out the X4 part. */
2445 dest[x] = source[x] & ~0xf0;
2448 break;
2451 case CONVERT_D24FS8:
2453 unsigned int x, y;
2455 for (y = 0; y < height; ++y)
2457 const DWORD *source = (const DWORD *)(src + y * pitch);
2458 float *dest_f = (float *)(dst + y * outpitch);
2459 DWORD *dest_s = (DWORD *)(dst + y * outpitch);
2461 for (x = 0; x < width; ++x)
2463 dest_f[x * 2] = float_24_to_32((source[x] & 0xffffff00) >> 8);
2464 dest_s[x * 2 + 1] = source[x] & 0xff;
2467 break;
2470 default:
2471 ERR("Unsupported conversion type %#x.\n", convert);
2473 return WINED3D_OK;
2476 /* This function is used in case of 8bit paletted textures to upload the palette.
2477 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2478 extensions like ATI_fragment_shaders is possible.
2480 /* Context activation is done by the caller. */
2481 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2482 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2483 BYTE table[256][4];
2484 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2486 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2488 /* Try to use the paletted texture extension */
2489 if(GL_SUPPORT(EXT_PALETTED_TEXTURE))
2491 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2492 ENTER_GL();
2493 GL_EXTCALL(glColorTableEXT(This->glDescription.target,GL_RGBA,256,GL_RGBA,GL_UNSIGNED_BYTE, table));
2494 LEAVE_GL();
2496 else
2498 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2499 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2500 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2502 ENTER_GL();
2504 /* Create the fragment program if we don't have it */
2505 if(!device->paletteConversionShader)
2507 const char *fragment_palette_conversion =
2508 "!!ARBfp1.0\n"
2509 "TEMP index;\n"
2510 /* { 255/256, 0.5/255*255/256, 0, 0 } */
2511 "PARAM constants = { 0.996, 0.00195, 0, 0 };\n"
2512 /* The alpha-component contains the palette index */
2513 "TEX index, fragment.texcoord[0], texture[0], 2D;\n"
2514 /* Scale the index by 255/256 and add a bias of '0.5' in order to sample in the middle */
2515 "MAD index.a, index.a, constants.x, constants.y;\n"
2516 /* Use the alpha-component as an index in the palette to get the final color */
2517 "TEX result.color, index.a, texture[1], 1D;\n"
2518 "END";
2520 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2521 GL_EXTCALL(glGenProgramsARB(1, &device->paletteConversionShader));
2522 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2523 GL_EXTCALL(glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragment_palette_conversion), fragment_palette_conversion));
2524 glDisable(GL_FRAGMENT_PROGRAM_ARB);
2527 glEnable(GL_FRAGMENT_PROGRAM_ARB);
2528 GL_EXTCALL(glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, device->paletteConversionShader));
2530 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2531 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2533 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2534 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2535 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2536 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2538 /* Switch back to unit 0 in which the 2D texture will be stored. */
2539 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2541 /* Rebind the texture because it isn't bound anymore */
2542 glBindTexture(This->glDescription.target, This->glDescription.textureName);
2544 LEAVE_GL();
2548 BOOL palette9_changed(IWineD3DSurfaceImpl *This) {
2549 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2551 if (This->palette || (This->resource.format_desc->format != WINED3DFMT_P8
2552 && This->resource.format_desc->format != WINED3DFMT_A8P8))
2554 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2555 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2557 return FALSE;
2560 if(This->palette9) {
2561 if(memcmp(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256) == 0) {
2562 return FALSE;
2564 } else {
2565 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2567 memcpy(This->palette9, &device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2568 return TRUE;
2571 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2572 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2573 DWORD flag = srgb_mode ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2575 if (!(This->Flags & flag)) {
2576 TRACE("Reloading because surface is dirty\n");
2577 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2578 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2579 /* Reload: vice versa OR */
2580 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2581 /* Also reload: Color key is active AND the color key has changed */
2582 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2583 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2584 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2585 TRACE("Reloading because of color keying\n");
2586 /* To perform the color key conversion we need a sysmem copy of
2587 * the surface. Make sure we have it
2590 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2591 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2592 /* TODO: This is not necessarily needed with hw palettized texture support */
2593 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2594 } else {
2595 TRACE("surface is already in texture\n");
2596 return WINED3D_OK;
2599 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2600 * These resources are not bound by device size or format restrictions. Because of this,
2601 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2602 * However, these resources can always be created, locked, and copied.
2604 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2606 FIXME("(%p) Operation not supported for scratch textures\n",This);
2607 return WINED3DERR_INVALIDCALL;
2610 IWineD3DSurface_LoadLocation(iface, flag, NULL /* no partial locking for textures yet */);
2612 #if 0
2614 static unsigned int gen = 0;
2615 char buffer[4096];
2616 ++gen;
2617 if ((gen % 10) == 0) {
2618 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm", This, This->glDescription.target, This->glDescription.level, gen);
2619 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2622 * debugging crash code
2623 if (gen == 250) {
2624 void** test = NULL;
2625 *test = 0;
2629 #endif
2631 if (!(This->Flags & SFLAG_DONOTFREE)) {
2632 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2633 This->resource.allocatedMemory = NULL;
2634 This->resource.heapMemory = NULL;
2635 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2638 return WINED3D_OK;
2641 /* Context activation is done by the caller. */
2642 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface, BOOL srgb) {
2643 /* TODO: check for locks */
2644 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2645 IWineD3DBaseTexture *baseTexture = NULL;
2646 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
2648 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2649 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2650 TRACE("Passing to container\n");
2651 IWineD3DBaseTexture_BindTexture(baseTexture, srgb);
2652 IWineD3DBaseTexture_Release(baseTexture);
2653 } else {
2654 GLuint *name;
2655 TRACE("(%p) : Binding surface\n", This);
2657 name = srgb ? &This->glDescription.srgbTextureName : &This->glDescription.textureName;
2658 if(!device->isInDraw) {
2659 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
2662 ENTER_GL();
2664 if (!This->glDescription.level) {
2665 if (!*name) {
2666 glGenTextures(1, name);
2667 checkGLcall("glGenTextures");
2668 TRACE("Surface %p given name %d\n", This, *name);
2670 glBindTexture(This->glDescription.target, *name);
2671 checkGLcall("glBindTexture");
2672 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2673 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2674 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2675 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2676 glTexParameteri(This->glDescription.target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2677 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2678 glTexParameteri(This->glDescription.target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2679 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2680 glTexParameteri(This->glDescription.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2681 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2683 /* This is where we should be reducing the amount of GLMemoryUsed */
2684 } else if (*name) {
2685 /* Mipmap surfaces should have a base texture container */
2686 ERR("Mipmap surface has a glTexture bound to it!\n");
2689 glBindTexture(This->glDescription.target, *name);
2690 checkGLcall("glBindTexture");
2692 LEAVE_GL();
2694 return;
2697 #include <errno.h>
2698 #include <stdio.h>
2699 static HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename)
2701 FILE* f = NULL;
2702 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2703 char *allocatedMemory;
2704 const char *textureRow;
2705 IWineD3DSwapChain *swapChain = NULL;
2706 int width, height, i, y;
2707 GLuint tmpTexture = 0;
2708 DWORD color;
2709 /*FIXME:
2710 Textures may not be stored in ->allocatedgMemory and a GlTexture
2711 so we should lock the surface before saving a snapshot, or at least check that
2713 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2714 by calling GetTexImage and in compressed form by calling
2715 GetCompressedTexImageARB. Queried compressed images can be saved and
2716 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2717 texture images do not need to be processed by the GL and should
2718 significantly improve texture loading performance relative to uncompressed
2719 images. */
2721 /* Setup the width and height to be the internal texture width and height. */
2722 width = This->pow2Width;
2723 height = This->pow2Height;
2724 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2725 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2727 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2728 /* if were not a real texture then read the back buffer into a real texture */
2729 /* we don't want to interfere with the back buffer so read the data into a temporary
2730 * texture and then save the data out of the temporary texture
2732 GLint prevRead;
2733 ENTER_GL();
2734 TRACE("(%p) Reading render target into texture\n", This);
2736 glGenTextures(1, &tmpTexture);
2737 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2739 glTexImage2D(GL_TEXTURE_2D,
2741 GL_RGBA,
2742 width,
2743 height,
2744 0/*border*/,
2745 GL_RGBA,
2746 GL_UNSIGNED_INT_8_8_8_8_REV,
2747 NULL);
2749 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2750 checkGLcall("glGetIntegerv");
2751 glReadBuffer(swapChain ? GL_BACK : This->resource.wineD3DDevice->offscreenBuffer);
2752 checkGLcall("glReadBuffer");
2753 glCopyTexImage2D(GL_TEXTURE_2D,
2755 GL_RGBA,
2758 width,
2759 height,
2762 checkGLcall("glCopyTexImage2D");
2763 glReadBuffer(prevRead);
2764 LEAVE_GL();
2766 } else { /* bind the real texture, and make sure it up to date */
2767 surface_internal_preload(iface, SRGB_RGB);
2768 surface_bind_and_dirtify(This, FALSE);
2770 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2771 ENTER_GL();
2772 FIXME("Saving texture level %d width %d height %d\n", This->glDescription.level, width, height);
2773 glGetTexImage(GL_TEXTURE_2D,
2774 This->glDescription.level,
2775 GL_RGBA,
2776 GL_UNSIGNED_INT_8_8_8_8_REV,
2777 allocatedMemory);
2778 checkGLcall("glTexImage2D");
2779 if (tmpTexture) {
2780 glBindTexture(GL_TEXTURE_2D, 0);
2781 glDeleteTextures(1, &tmpTexture);
2783 LEAVE_GL();
2785 f = fopen(filename, "w+");
2786 if (NULL == f) {
2787 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2788 return WINED3DERR_INVALIDCALL;
2790 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2791 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format_desc->format));
2792 /* TGA header */
2793 fputc(0,f);
2794 fputc(0,f);
2795 fputc(2,f);
2796 fputc(0,f);
2797 fputc(0,f);
2798 fputc(0,f);
2799 fputc(0,f);
2800 fputc(0,f);
2801 fputc(0,f);
2802 fputc(0,f);
2803 fputc(0,f);
2804 fputc(0,f);
2805 /* short width*/
2806 fwrite(&width,2,1,f);
2807 /* short height */
2808 fwrite(&height,2,1,f);
2809 /* format rgba */
2810 fputc(0x20,f);
2811 fputc(0x28,f);
2812 /* raw data */
2813 /* 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 */
2814 if(swapChain)
2815 textureRow = allocatedMemory + (width * (height - 1) *4);
2816 else
2817 textureRow = allocatedMemory;
2818 for (y = 0 ; y < height; y++) {
2819 for (i = 0; i < width; i++) {
2820 color = *((const DWORD*)textureRow);
2821 fputc((color >> 16) & 0xFF, f); /* B */
2822 fputc((color >> 8) & 0xFF, f); /* G */
2823 fputc((color >> 0) & 0xFF, f); /* R */
2824 fputc((color >> 24) & 0xFF, f); /* A */
2825 textureRow += 4;
2827 /* take two rows of the pointer to the texture memory */
2828 if(swapChain)
2829 (textureRow-= width << 3);
2832 TRACE("Closing file\n");
2833 fclose(f);
2835 if(swapChain) {
2836 IWineD3DSwapChain_Release(swapChain);
2838 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2839 return WINED3D_OK;
2842 static HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2843 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2844 HRESULT hr;
2846 TRACE("(%p) : Calling base function first\n", This);
2847 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2848 if(SUCCEEDED(hr)) {
2849 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2850 TRACE("(%p) : glFormat %d, glFormatInternal %d, glType %d\n", This, This->resource.format_desc->glFormat,
2851 This->resource.format_desc->glInternal, This->resource.format_desc->glType);
2853 return hr;
2856 static HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2857 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2859 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2860 WARN("Surface is locked or the HDC is in use\n");
2861 return WINED3DERR_INVALIDCALL;
2864 if(Mem && Mem != This->resource.allocatedMemory) {
2865 void *release = NULL;
2867 /* Do I have to copy the old surface content? */
2868 if(This->Flags & SFLAG_DIBSECTION) {
2869 /* Release the DC. No need to hold the critical section for the update
2870 * Thread because this thread runs only on front buffers, but this method
2871 * fails for render targets in the check above.
2873 SelectObject(This->hDC, This->dib.holdbitmap);
2874 DeleteDC(This->hDC);
2875 /* Release the DIB section */
2876 DeleteObject(This->dib.DIBsection);
2877 This->dib.bitmap_data = NULL;
2878 This->resource.allocatedMemory = NULL;
2879 This->hDC = NULL;
2880 This->Flags &= ~SFLAG_DIBSECTION;
2881 } else if(!(This->Flags & SFLAG_USERPTR)) {
2882 release = This->resource.heapMemory;
2883 This->resource.heapMemory = NULL;
2885 This->resource.allocatedMemory = Mem;
2886 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2888 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2889 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2891 /* For client textures opengl has to be notified */
2892 if(This->Flags & SFLAG_CLIENT) {
2893 DWORD oldFlags = This->Flags;
2894 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2895 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2896 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2897 /* And hope that the app behaves correctly and did not free the old surface memory before setting a new pointer */
2900 /* Now free the old memory if any */
2901 HeapFree(GetProcessHeap(), 0, release);
2902 } else if(This->Flags & SFLAG_USERPTR) {
2903 /* LockRect and GetDC will re-create the dib section and allocated memory */
2904 This->resource.allocatedMemory = NULL;
2905 /* HeapMemory should be NULL already */
2906 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2907 This->Flags &= ~SFLAG_USERPTR;
2909 if(This->Flags & SFLAG_CLIENT) {
2910 DWORD oldFlags = This->Flags;
2911 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2912 /* This respecifies an empty texture and opengl knows that the old memory is gone */
2913 if(oldFlags & SFLAG_ALLOCATED) surface_internal_preload(iface, SRGB_RGB);
2914 if(oldFlags & SFLAG_SRGBALLOCATED) surface_internal_preload(iface, SRGB_SRGB);
2917 return WINED3D_OK;
2920 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
2922 /* Flip the surface contents */
2923 /* Flip the DC */
2925 HDC tmp;
2926 tmp = front->hDC;
2927 front->hDC = back->hDC;
2928 back->hDC = tmp;
2931 /* Flip the DIBsection */
2933 HBITMAP tmp;
2934 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
2935 tmp = front->dib.DIBsection;
2936 front->dib.DIBsection = back->dib.DIBsection;
2937 back->dib.DIBsection = tmp;
2939 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
2940 else front->Flags &= ~SFLAG_DIBSECTION;
2941 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
2942 else back->Flags &= ~SFLAG_DIBSECTION;
2945 /* Flip the surface data */
2947 void* tmp;
2949 tmp = front->dib.bitmap_data;
2950 front->dib.bitmap_data = back->dib.bitmap_data;
2951 back->dib.bitmap_data = tmp;
2953 tmp = front->resource.allocatedMemory;
2954 front->resource.allocatedMemory = back->resource.allocatedMemory;
2955 back->resource.allocatedMemory = tmp;
2957 tmp = front->resource.heapMemory;
2958 front->resource.heapMemory = back->resource.heapMemory;
2959 back->resource.heapMemory = tmp;
2962 /* Flip the PBO */
2964 GLuint tmp_pbo = front->pbo;
2965 front->pbo = back->pbo;
2966 back->pbo = tmp_pbo;
2969 /* client_memory should not be different, but just in case */
2971 BOOL tmp;
2972 tmp = front->dib.client_memory;
2973 front->dib.client_memory = back->dib.client_memory;
2974 back->dib.client_memory = tmp;
2977 /* Flip the opengl texture */
2979 glDescriptor tmp_desc = back->glDescription;
2980 back->glDescription = front->glDescription;
2981 front->glDescription = tmp_desc;
2985 DWORD tmp_flags = back->Flags;
2986 back->Flags = front->Flags;
2987 front->Flags = tmp_flags;
2991 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2992 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2993 IWineD3DSwapChainImpl *swapchain = NULL;
2994 HRESULT hr;
2995 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2997 /* Flipping is only supported on RenderTargets and overlays*/
2998 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
2999 WARN("Tried to flip a non-render target, non-overlay surface\n");
3000 return WINEDDERR_NOTFLIPPABLE;
3003 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
3004 flip_surface(This, (IWineD3DSurfaceImpl *) override);
3006 /* Update the overlay if it is visible */
3007 if(This->overlay_dest) {
3008 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
3009 } else {
3010 return WINED3D_OK;
3014 if(override) {
3015 /* DDraw sets this for the X11 surfaces, so don't confuse the user
3016 * FIXME("(%p) Target override is not supported by now\n", This);
3017 * Additionally, it isn't really possible to support triple-buffering
3018 * properly on opengl at all
3022 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
3023 if(!swapchain) {
3024 ERR("Flipped surface is not on a swapchain\n");
3025 return WINEDDERR_NOTFLIPPABLE;
3028 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
3029 * and only d3d8 and d3d9 apps specify the presentation interval
3031 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
3032 /* Most common case first to avoid wasting time on all the other cases */
3033 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
3034 } else if(Flags & WINEDDFLIP_NOVSYNC) {
3035 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3036 } else if(Flags & WINEDDFLIP_INTERVAL2) {
3037 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
3038 } else if(Flags & WINEDDFLIP_INTERVAL3) {
3039 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
3040 } else {
3041 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
3044 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
3045 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
3046 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
3047 return hr;
3050 /* Does a direct frame buffer -> texture copy. Stretching is done
3051 * with single pixel copy calls
3053 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3054 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
3055 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
3057 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3058 float xrel, yrel;
3059 UINT row;
3060 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3063 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
3064 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3065 ENTER_GL();
3067 /* Bind the target texture */
3068 glBindTexture(This->glDescription.target, This->glDescription.textureName);
3069 checkGLcall("glBindTexture");
3070 if(!swapchain) {
3071 TRACE("Reading from an offscreen target\n");
3072 upsidedown = !upsidedown;
3073 glReadBuffer(myDevice->offscreenBuffer);
3074 } else {
3075 GLenum buffer = surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain);
3076 glReadBuffer(buffer);
3078 checkGLcall("glReadBuffer");
3080 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
3081 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
3083 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
3084 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3086 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
3087 ERR("Texture filtering not supported in direct blit\n");
3089 } else if((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) && ((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
3090 ERR("Texture filtering not supported in direct blit\n");
3093 if(upsidedown &&
3094 !((xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) &&
3095 !((yrel - 1.0 < -eps) || (yrel - 1.0 > eps))) {
3096 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
3098 glCopyTexSubImage2D(This->glDescription.target,
3099 This->glDescription.level,
3100 drect->x1, drect->y1, /* xoffset, yoffset */
3101 srect->x1, Src->currentDesc.Height - srect->y2,
3102 drect->x2 - drect->x1, drect->y2 - drect->y1);
3103 } else {
3104 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
3105 /* I have to process this row by row to swap the image,
3106 * otherwise it would be upside down, so stretching in y direction
3107 * doesn't cost extra time
3109 * However, stretching in x direction can be avoided if not necessary
3111 for(row = drect->y1; row < drect->y2; row++) {
3112 if( (xrel - 1.0 < -eps) || (xrel - 1.0 > eps)) {
3113 /* Well, that stuff works, but it's very slow.
3114 * find a better way instead
3116 UINT col;
3118 for(col = drect->x1; col < drect->x2; col++) {
3119 glCopyTexSubImage2D(This->glDescription.target,
3120 This->glDescription.level,
3121 drect->x1 + col, row, /* xoffset, yoffset */
3122 srect->x1 + col * xrel, yoffset - (int) (row * yrel),
3123 1, 1);
3125 } else {
3126 glCopyTexSubImage2D(This->glDescription.target,
3127 This->glDescription.level,
3128 drect->x1, row, /* xoffset, yoffset */
3129 srect->x1, yoffset - (int) (row * yrel),
3130 drect->x2-drect->x1, 1);
3134 checkGLcall("glCopyTexSubImage2D");
3136 LEAVE_GL();
3139 /* Uses the hardware to stretch and flip the image */
3140 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3141 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
3142 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
3144 GLuint src, backup = 0;
3145 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3146 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3147 float left, right, top, bottom; /* Texture coordinates */
3148 UINT fbwidth = Src->currentDesc.Width;
3149 UINT fbheight = Src->currentDesc.Height;
3150 GLenum drawBuffer = GL_BACK;
3151 GLenum texture_target;
3152 BOOL noBackBufferBackup;
3154 TRACE("Using hwstretch blit\n");
3155 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3156 ActivateContext(myDevice, SrcSurface, CTXUSAGE_BLIT);
3157 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3159 noBackBufferBackup = !swapchain && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3160 if(!noBackBufferBackup && Src->glDescription.textureName == 0) {
3161 /* Get it a description */
3162 surface_internal_preload(SrcSurface, SRGB_RGB);
3164 ENTER_GL();
3166 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3167 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3169 if(myDevice->activeContext->aux_buffers >= 2) {
3170 /* Got more than one aux buffer? Use the 2nd aux buffer */
3171 drawBuffer = GL_AUX1;
3172 } else if((swapchain || myDevice->offscreenBuffer == GL_BACK) && myDevice->activeContext->aux_buffers >= 1) {
3173 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3174 drawBuffer = GL_AUX0;
3177 if(noBackBufferBackup) {
3178 glGenTextures(1, &backup);
3179 checkGLcall("glGenTextures\n");
3180 glBindTexture(GL_TEXTURE_2D, backup);
3181 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
3182 texture_target = GL_TEXTURE_2D;
3183 } else {
3184 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3185 * we are reading from the back buffer, the backup can be used as source texture
3187 texture_target = Src->glDescription.target;
3188 glBindTexture(texture_target, Src->glDescription.textureName);
3189 checkGLcall("glBindTexture(texture_target, Src->glDescription.textureName)");
3190 glEnable(texture_target);
3191 checkGLcall("glEnable(texture_target)");
3193 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3194 Src->Flags &= ~SFLAG_INTEXTURE;
3197 if(swapchain) {
3198 glReadBuffer(surface_get_gl_buffer(SrcSurface, (IWineD3DSwapChain *)swapchain));
3199 } else {
3200 TRACE("Reading from an offscreen target\n");
3201 upsidedown = !upsidedown;
3202 glReadBuffer(myDevice->offscreenBuffer);
3205 /* TODO: Only back up the part that will be overwritten */
3206 glCopyTexSubImage2D(texture_target, 0,
3207 0, 0 /* read offsets */,
3208 0, 0,
3209 fbwidth,
3210 fbheight);
3212 checkGLcall("glCopyTexSubImage2D");
3214 /* No issue with overriding these - the sampler is dirty due to blit usage */
3215 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3216 magLookup[Filter - WINED3DTEXF_NONE]);
3217 checkGLcall("glTexParameteri");
3218 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3219 minMipLookup[Filter].mip[WINED3DTEXF_NONE]);
3220 checkGLcall("glTexParameteri");
3222 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
3223 src = backup ? backup : Src->glDescription.textureName;
3224 } else {
3225 glReadBuffer(GL_FRONT);
3226 checkGLcall("glReadBuffer(GL_FRONT)");
3228 glGenTextures(1, &src);
3229 checkGLcall("glGenTextures(1, &src)");
3230 glBindTexture(GL_TEXTURE_2D, src);
3231 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
3233 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
3234 * out for power of 2 sizes
3236 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
3237 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3238 checkGLcall("glTexImage2D");
3239 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
3240 0, 0 /* read offsets */,
3241 0, 0,
3242 fbwidth,
3243 fbheight);
3245 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3246 checkGLcall("glTexParameteri");
3247 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3248 checkGLcall("glTexParameteri");
3250 glReadBuffer(GL_BACK);
3251 checkGLcall("glReadBuffer(GL_BACK)");
3253 if(texture_target != GL_TEXTURE_2D) {
3254 glDisable(texture_target);
3255 glEnable(GL_TEXTURE_2D);
3256 texture_target = GL_TEXTURE_2D;
3259 checkGLcall("glEnd and previous");
3261 left = srect->x1;
3262 right = srect->x2;
3264 if(upsidedown) {
3265 top = Src->currentDesc.Height - srect->y1;
3266 bottom = Src->currentDesc.Height - srect->y2;
3267 } else {
3268 top = Src->currentDesc.Height - srect->y2;
3269 bottom = Src->currentDesc.Height - srect->y1;
3272 if(Src->Flags & SFLAG_NORMCOORD) {
3273 left /= Src->pow2Width;
3274 right /= Src->pow2Width;
3275 top /= Src->pow2Height;
3276 bottom /= Src->pow2Height;
3279 /* draw the source texture stretched and upside down. The correct surface is bound already */
3280 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3281 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3283 glDrawBuffer(drawBuffer);
3284 glReadBuffer(drawBuffer);
3286 glBegin(GL_QUADS);
3287 /* bottom left */
3288 glTexCoord2f(left, bottom);
3289 glVertex2i(0, fbheight);
3291 /* top left */
3292 glTexCoord2f(left, top);
3293 glVertex2i(0, fbheight - drect->y2 - drect->y1);
3295 /* top right */
3296 glTexCoord2f(right, top);
3297 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
3299 /* bottom right */
3300 glTexCoord2f(right, bottom);
3301 glVertex2i(drect->x2 - drect->x1, fbheight);
3302 glEnd();
3303 checkGLcall("glEnd and previous");
3305 if(texture_target != This->glDescription.target) {
3306 glDisable(texture_target);
3307 glEnable(This->glDescription.target);
3308 texture_target = This->glDescription.target;
3311 /* Now read the stretched and upside down image into the destination texture */
3312 glBindTexture(texture_target, This->glDescription.textureName);
3313 checkGLcall("glBindTexture");
3314 glCopyTexSubImage2D(texture_target,
3316 drect->x1, drect->y1, /* xoffset, yoffset */
3317 0, 0, /* We blitted the image to the origin */
3318 drect->x2 - drect->x1, drect->y2 - drect->y1);
3319 checkGLcall("glCopyTexSubImage2D");
3321 if(drawBuffer == GL_BACK) {
3322 /* Write the back buffer backup back */
3323 if(backup) {
3324 if(texture_target != GL_TEXTURE_2D) {
3325 glDisable(texture_target);
3326 glEnable(GL_TEXTURE_2D);
3327 texture_target = GL_TEXTURE_2D;
3329 glBindTexture(GL_TEXTURE_2D, backup);
3330 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3331 } else {
3332 if(texture_target != Src->glDescription.target) {
3333 glDisable(texture_target);
3334 glEnable(Src->glDescription.target);
3335 texture_target = Src->glDescription.target;
3337 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3338 checkGLcall("glBindTexture(Src->glDescription.target, Src->glDescription.textureName)");
3341 glBegin(GL_QUADS);
3342 /* top left */
3343 glTexCoord2f(0.0, (float) fbheight / (float) Src->pow2Height);
3344 glVertex2i(0, 0);
3346 /* bottom left */
3347 glTexCoord2f(0.0, 0.0);
3348 glVertex2i(0, fbheight);
3350 /* bottom right */
3351 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, 0.0);
3352 glVertex2i(fbwidth, Src->currentDesc.Height);
3354 /* top right */
3355 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
3356 glVertex2i(fbwidth, 0);
3357 glEnd();
3358 } else {
3359 /* Restore the old draw buffer */
3360 glDrawBuffer(GL_BACK);
3362 glDisable(texture_target);
3363 checkGLcall("glDisable(texture_target)");
3365 /* Cleanup */
3366 if(src != Src->glDescription.textureName && src != backup) {
3367 glDeleteTextures(1, &src);
3368 checkGLcall("glDeleteTextures(1, &src)");
3370 if(backup) {
3371 glDeleteTextures(1, &backup);
3372 checkGLcall("glDeleteTextures(1, &backup)");
3375 LEAVE_GL();
3378 /* Not called from the VTable */
3379 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3380 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx,
3381 WINED3DTEXTUREFILTERTYPE Filter)
3383 WINED3DRECT rect;
3384 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3385 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3386 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3388 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3390 /* Get the swapchain. One of the surfaces has to be a primary surface */
3391 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3392 WARN("Destination is in sysmem, rejecting gl blt\n");
3393 return WINED3DERR_INVALIDCALL;
3395 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3396 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3397 if(Src) {
3398 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3399 WARN("Src is in sysmem, rejecting gl blt\n");
3400 return WINED3DERR_INVALIDCALL;
3402 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3403 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3406 /* Early sort out of cases where no render target is used */
3407 if(!dstSwapchain && !srcSwapchain &&
3408 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3409 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3410 return WINED3DERR_INVALIDCALL;
3413 /* No destination color keying supported */
3414 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3415 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3416 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3417 return WINED3DERR_INVALIDCALL;
3420 if (DestRect) {
3421 rect.x1 = DestRect->left;
3422 rect.y1 = DestRect->top;
3423 rect.x2 = DestRect->right;
3424 rect.y2 = DestRect->bottom;
3425 } else {
3426 rect.x1 = 0;
3427 rect.y1 = 0;
3428 rect.x2 = This->currentDesc.Width;
3429 rect.y2 = This->currentDesc.Height;
3432 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3433 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3434 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3435 /* Half-life does a Blt from the back buffer to the front buffer,
3436 * Full surface size, no flags... Use present instead
3438 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3441 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3442 while(1)
3444 RECT mySrcRect;
3445 TRACE("Looking if a Present can be done...\n");
3446 /* Source Rectangle must be full surface */
3447 if( SrcRect ) {
3448 if(SrcRect->left != 0 || SrcRect->top != 0 ||
3449 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
3450 TRACE("No, Source rectangle doesn't match\n");
3451 break;
3454 mySrcRect.left = 0;
3455 mySrcRect.top = 0;
3456 mySrcRect.right = Src->currentDesc.Width;
3457 mySrcRect.bottom = Src->currentDesc.Height;
3459 /* No stretching may occur */
3460 if(mySrcRect.right != rect.x2 - rect.x1 ||
3461 mySrcRect.bottom != rect.y2 - rect.y1) {
3462 TRACE("No, stretching is done\n");
3463 break;
3466 /* Destination must be full surface or match the clipping rectangle */
3467 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3469 RECT cliprect;
3470 POINT pos[2];
3471 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3472 pos[0].x = rect.x1;
3473 pos[0].y = rect.y1;
3474 pos[1].x = rect.x2;
3475 pos[1].y = rect.y2;
3476 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3477 pos, 2);
3479 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3480 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3482 TRACE("No, dest rectangle doesn't match(clipper)\n");
3483 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3484 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3485 break;
3488 else
3490 if(rect.x1 != 0 || rect.y1 != 0 ||
3491 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3492 TRACE("No, dest rectangle doesn't match(surface size)\n");
3493 break;
3497 TRACE("Yes\n");
3499 /* These flags are unimportant for the flag check, remove them */
3500 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3501 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3503 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3504 * take very long, while a flip is fast.
3505 * This applies to Half-Life, which does such Blts every time it finished
3506 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3507 * menu. This is also used by all apps when they do windowed rendering
3509 * The problem is that flipping is not really the same as copying. After a
3510 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3511 * untouched. Therefore it's necessary to override the swap effect
3512 * and to set it back after the flip.
3514 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3515 * testcases.
3518 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3519 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3521 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3522 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3524 dstSwapchain->presentParms.SwapEffect = orig_swap;
3526 return WINED3D_OK;
3528 break;
3531 TRACE("Unsupported blit between buffers on the same swapchain\n");
3532 return WINED3DERR_INVALIDCALL;
3533 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3534 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3535 return WINED3DERR_INVALIDCALL;
3536 } else if(dstSwapchain && srcSwapchain) {
3537 FIXME("Implement hardware blit between two different swapchains\n");
3538 return WINED3DERR_INVALIDCALL;
3539 } else if(dstSwapchain) {
3540 if(SrcSurface == myDevice->render_targets[0]) {
3541 TRACE("Blit from active render target to a swapchain\n");
3542 /* Handled with regular texture -> swapchain blit */
3544 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3545 FIXME("Implement blit from a swapchain to the active render target\n");
3546 return WINED3DERR_INVALIDCALL;
3549 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3550 /* Blit from render target to texture */
3551 WINED3DRECT srect;
3552 BOOL upsideDown, stretchx;
3553 BOOL paletteOverride = FALSE;
3555 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3556 TRACE("Color keying not supported by frame buffer to texture blit\n");
3557 return WINED3DERR_INVALIDCALL;
3558 /* Destination color key is checked above */
3561 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3562 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3564 if(SrcRect) {
3565 if(SrcRect->top < SrcRect->bottom) {
3566 srect.y1 = SrcRect->top;
3567 srect.y2 = SrcRect->bottom;
3568 upsideDown = FALSE;
3569 } else {
3570 srect.y1 = SrcRect->bottom;
3571 srect.y2 = SrcRect->top;
3572 upsideDown = TRUE;
3574 srect.x1 = SrcRect->left;
3575 srect.x2 = SrcRect->right;
3576 } else {
3577 srect.x1 = 0;
3578 srect.y1 = 0;
3579 srect.x2 = Src->currentDesc.Width;
3580 srect.y2 = Src->currentDesc.Height;
3581 upsideDown = FALSE;
3583 if(rect.x1 > rect.x2) {
3584 UINT tmp = rect.x2;
3585 rect.x2 = rect.x1;
3586 rect.x1 = tmp;
3587 upsideDown = !upsideDown;
3590 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3591 stretchx = TRUE;
3592 } else {
3593 stretchx = FALSE;
3596 /* When blitting from a render target a texture, the texture isn't required to have a palette.
3597 * In this case grab the palette from the render target. */
3598 if ((This->resource.format_desc->format == WINED3DFMT_P8) && (This->palette == NULL))
3600 paletteOverride = TRUE;
3601 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3602 This->palette = Src->palette;
3605 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3606 * flip the image nor scale it.
3608 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3609 * -> If the app wants a image width an unscaled width, copy it line per line
3610 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3611 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3612 * back buffer. This is slower than reading line per line, thus not used for flipping
3613 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3614 * pixel by pixel
3616 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3617 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3618 * backends.
3620 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)
3621 && surface_can_stretch_rect(Src, This))
3623 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3624 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3625 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3626 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3627 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3628 fb_copy_to_texture_direct(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3629 } else {
3630 TRACE("Using hardware stretching to flip / stretch the texture\n");
3631 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3634 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3635 if(paletteOverride)
3636 This->palette = NULL;
3638 if(!(This->Flags & SFLAG_DONOTFREE)) {
3639 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3640 This->resource.allocatedMemory = NULL;
3641 This->resource.heapMemory = NULL;
3642 } else {
3643 This->Flags &= ~SFLAG_INSYSMEM;
3645 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3646 * path is never entered
3648 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3650 return WINED3D_OK;
3651 } else if(Src) {
3652 /* Blit from offscreen surface to render target */
3653 float glTexCoord[4];
3654 DWORD oldCKeyFlags = Src->CKeyFlags;
3655 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3656 RECT SourceRectangle;
3657 BOOL paletteOverride = FALSE;
3659 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3661 if(SrcRect) {
3662 SourceRectangle.left = SrcRect->left;
3663 SourceRectangle.right = SrcRect->right;
3664 SourceRectangle.top = SrcRect->top;
3665 SourceRectangle.bottom = SrcRect->bottom;
3666 } else {
3667 SourceRectangle.left = 0;
3668 SourceRectangle.right = Src->currentDesc.Width;
3669 SourceRectangle.top = 0;
3670 SourceRectangle.bottom = Src->currentDesc.Height;
3673 /* When blitting from an offscreen surface to a rendertarget, the source
3674 * surface is not required to have a palette. Our rendering / conversion
3675 * code further down the road retrieves the palette from the surface, so
3676 * it must have a palette set. */
3677 if ((Src->resource.format_desc->format == WINED3DFMT_P8) && (Src->palette == NULL))
3679 paletteOverride = TRUE;
3680 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3681 Src->palette = This->palette;
3684 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && GL_SUPPORT(EXT_FRAMEBUFFER_BLIT)
3685 && !(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3686 && surface_can_stretch_rect(Src, This))
3688 TRACE("Using stretch_rect_fbo\n");
3689 /* The source is always a texture, but never the currently active render target, and the texture
3690 * contents are never upside down
3692 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3693 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3695 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3696 if(paletteOverride)
3697 Src->palette = NULL;
3698 return WINED3D_OK;
3701 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3702 /* Fall back to software */
3703 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3704 SourceRectangle.left, SourceRectangle.top,
3705 SourceRectangle.right, SourceRectangle.bottom);
3706 return WINED3DERR_INVALIDCALL;
3709 /* Color keying: Check if we have to do a color keyed blt,
3710 * and if not check if a color key is activated.
3712 * Just modify the color keying parameters in the surface and restore them afterwards
3713 * The surface keeps track of the color key last used to load the opengl surface.
3714 * PreLoad will catch the change to the flags and color key and reload if necessary.
3716 if(Flags & WINEDDBLT_KEYSRC) {
3717 /* Use color key from surface */
3718 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3719 /* Use color key from DDBltFx */
3720 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3721 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3722 } else {
3723 /* Do not use color key */
3724 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3727 /* Now load the surface */
3728 surface_internal_preload((IWineD3DSurface *) Src, SRGB_RGB);
3730 /* Activate the destination context, set it up for blitting */
3731 ActivateContext(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
3733 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3734 * while OpenGL coordinates are window relative.
3735 * Also beware of the origin difference(top left vs bottom left).
3736 * Also beware that the front buffer's surface size is screen width x screen height,
3737 * whereas the real gl drawable size is the size of the window.
3739 if (dstSwapchain && (IWineD3DSurface *)This == dstSwapchain->frontBuffer) {
3740 RECT windowsize;
3741 POINT offset = {0,0};
3742 UINT h;
3743 ClientToScreen(dstSwapchain->win_handle, &offset);
3744 GetClientRect(dstSwapchain->win_handle, &windowsize);
3745 h = windowsize.bottom - windowsize.top;
3746 rect.x1 -= offset.x; rect.x2 -=offset.x;
3747 rect.y1 -= offset.y; rect.y2 -=offset.y;
3748 rect.y1 += This->currentDesc.Height - h; rect.y2 += This->currentDesc.Height - h;
3751 if (!is_identity_fixup(This->resource.format_desc->color_fixup))
3753 FIXME("Destination format %s has a fixup, this is not supported.\n",
3754 debug_d3dformat(This->resource.format_desc->format));
3755 dump_color_fixup_desc(This->resource.format_desc->color_fixup);
3758 if (!myDevice->blitter->color_fixup_supported(Src->resource.format_desc->color_fixup))
3760 FIXME("Source format %s has an unsupported fixup:\n",
3761 debug_d3dformat(Src->resource.format_desc->format));
3762 dump_color_fixup_desc(Src->resource.format_desc->color_fixup);
3765 myDevice->blitter->set_shader((IWineD3DDevice *) myDevice, Src->resource.format_desc,
3766 Src->glDescription.target, Src->pow2Width, Src->pow2Height);
3768 ENTER_GL();
3770 /* Bind the texture */
3771 glBindTexture(Src->glDescription.target, Src->glDescription.textureName);
3772 checkGLcall("glBindTexture");
3774 /* Filtering for StretchRect */
3775 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MAG_FILTER,
3776 magLookup[Filter - WINED3DTEXF_NONE]);
3777 checkGLcall("glTexParameteri");
3778 glTexParameteri(Src->glDescription.target, GL_TEXTURE_MIN_FILTER,
3779 minMipLookup[Filter].mip[WINED3DTEXF_NONE]);
3780 checkGLcall("glTexParameteri");
3781 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3782 glTexParameteri(Src->glDescription.target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3783 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3784 checkGLcall("glTexEnvi");
3786 /* This is for color keying */
3787 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3788 glEnable(GL_ALPHA_TEST);
3789 checkGLcall("glEnable GL_ALPHA_TEST");
3791 /* When the primary render target uses P8, the alpha component contains the palette index.
3792 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3793 * should be masked away have alpha set to 0. */
3794 if(primary_render_target_is_p8(myDevice))
3795 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0);
3796 else
3797 glAlphaFunc(GL_NOTEQUAL, 0.0);
3798 checkGLcall("glAlphaFunc\n");
3799 } else {
3800 glDisable(GL_ALPHA_TEST);
3801 checkGLcall("glDisable GL_ALPHA_TEST");
3804 /* Draw a textured quad
3806 glBegin(GL_QUADS);
3808 glColor3d(1.0f, 1.0f, 1.0f);
3809 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
3810 glVertex3f(rect.x1,
3811 rect.y1,
3812 0.0);
3814 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
3815 glVertex3f(rect.x1, rect.y2, 0.0);
3817 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
3818 glVertex3f(rect.x2,
3819 rect.y2,
3820 0.0);
3822 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
3823 glVertex3f(rect.x2,
3824 rect.y1,
3825 0.0);
3826 glEnd();
3827 checkGLcall("glEnd");
3829 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3830 glDisable(GL_ALPHA_TEST);
3831 checkGLcall("glDisable(GL_ALPHA_TEST)");
3834 glBindTexture(Src->glDescription.target, 0);
3835 checkGLcall("glBindTexture(Src->glDescription.target, 0)");
3837 /* Restore the color key parameters */
3838 Src->CKeyFlags = oldCKeyFlags;
3839 Src->SrcBltCKey = oldBltCKey;
3841 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3842 if(paletteOverride)
3843 Src->palette = NULL;
3845 LEAVE_GL();
3847 /* Leave the opengl state valid for blitting */
3848 myDevice->blitter->unset_shader((IWineD3DDevice *) myDevice);
3850 /* Flush in case the drawable is used by multiple GL contexts */
3851 if(dstSwapchain && (This == (IWineD3DSurfaceImpl *) dstSwapchain->frontBuffer || dstSwapchain->num_contexts >= 2))
3852 glFlush();
3854 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3855 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3856 * is outdated now
3858 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3860 return WINED3D_OK;
3861 } else {
3862 /* Source-Less Blit to render target */
3863 if (Flags & WINEDDBLT_COLORFILL) {
3864 /* This is easy to handle for the D3D Device... */
3865 DWORD color;
3867 TRACE("Colorfill\n");
3869 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3870 must be true if we are here */
3871 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3872 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3873 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3874 TRACE("Surface is higher back buffer, falling back to software\n");
3875 return WINED3DERR_INVALIDCALL;
3878 /* The color as given in the Blt function is in the format of the frame-buffer...
3879 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3881 if (This->resource.format_desc->format == WINED3DFMT_P8)
3883 DWORD alpha;
3885 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
3886 else alpha = 0xFF000000;
3888 if (This->palette) {
3889 color = (alpha |
3890 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
3891 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
3892 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
3893 } else {
3894 color = alpha;
3897 else if (This->resource.format_desc->format == WINED3DFMT_R5G6B5)
3899 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
3900 color = 0xFFFFFFFF;
3901 } else {
3902 color = ((0xFF000000) |
3903 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
3904 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
3905 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
3908 else if ((This->resource.format_desc->format == WINED3DFMT_R8G8B8)
3909 || (This->resource.format_desc->format == WINED3DFMT_X8R8G8B8))
3911 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
3913 else if (This->resource.format_desc->format == WINED3DFMT_A8R8G8B8)
3915 color = DDBltFx->u5.dwFillColor;
3917 else {
3918 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
3919 return WINED3DERR_INVALIDCALL;
3922 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
3923 IWineD3DDeviceImpl_ClearSurface(myDevice, This,
3924 1, /* Number of rectangles */
3925 &rect, WINED3DCLEAR_TARGET, color,
3926 0.0 /* Z */,
3927 0 /* Stencil */);
3928 return WINED3D_OK;
3932 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3933 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3934 return WINED3DERR_INVALIDCALL;
3937 static HRESULT IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3938 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx)
3940 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3941 float depth;
3943 if (Flags & WINEDDBLT_DEPTHFILL) {
3944 switch(This->resource.format_desc->format)
3946 case WINED3DFMT_D16_UNORM:
3947 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3948 break;
3949 case WINED3DFMT_D15S1:
3950 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3951 break;
3952 case WINED3DFMT_D24S8:
3953 case WINED3DFMT_D24X8:
3954 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3955 break;
3956 case WINED3DFMT_D32:
3957 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3958 break;
3959 default:
3960 depth = 0.0;
3961 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format_desc->format));
3964 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3965 DestRect == NULL ? 0 : 1,
3966 (const WINED3DRECT *)DestRect,
3967 WINED3DCLEAR_ZBUFFER,
3968 0x00000000,
3969 depth,
3970 0x00000000);
3973 FIXME("(%p): Unsupp depthstencil blit\n", This);
3974 return WINED3DERR_INVALIDCALL;
3977 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
3978 const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3979 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3980 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3981 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
3982 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3983 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3985 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3987 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3988 return WINEDDERR_SURFACEBUSY;
3991 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3992 * except depth blits, which seem to work
3994 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3995 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3996 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3997 return WINED3DERR_INVALIDCALL;
3998 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3999 TRACE("Z Blit override handled the blit\n");
4000 return WINED3D_OK;
4004 /* Special cases for RenderTargets */
4005 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
4006 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
4007 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
4010 /* For the rest call the X11 surface implementation.
4011 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
4012 * other Blts are rather rare
4014 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
4017 static HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
4018 IWineD3DSurface *Source, const RECT *rsrc, DWORD trans)
4020 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4021 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
4022 IWineD3DDeviceImpl *myDevice = This->resource.wineD3DDevice;
4023 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
4025 if ( (This->Flags & SFLAG_LOCKED) || ((srcImpl != NULL) && (srcImpl->Flags & SFLAG_LOCKED)))
4027 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
4028 return WINEDDERR_SURFACEBUSY;
4031 if(myDevice->inScene &&
4032 (iface == myDevice->stencilBufferTarget ||
4033 (Source && Source == myDevice->stencilBufferTarget))) {
4034 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
4035 return WINED3DERR_INVALIDCALL;
4038 /* Special cases for RenderTargets */
4039 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
4040 ( srcImpl && (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
4042 RECT SrcRect, DstRect;
4043 DWORD Flags=0;
4045 if(rsrc) {
4046 SrcRect.left = rsrc->left;
4047 SrcRect.top= rsrc->top;
4048 SrcRect.bottom = rsrc->bottom;
4049 SrcRect.right = rsrc->right;
4050 } else {
4051 SrcRect.left = 0;
4052 SrcRect.top = 0;
4053 SrcRect.right = srcImpl->currentDesc.Width;
4054 SrcRect.bottom = srcImpl->currentDesc.Height;
4057 DstRect.left = dstx;
4058 DstRect.top=dsty;
4059 DstRect.right = dstx + SrcRect.right - SrcRect.left;
4060 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
4062 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
4063 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
4064 Flags |= WINEDDBLT_KEYSRC;
4065 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
4066 Flags |= WINEDDBLT_KEYDEST;
4067 if(trans & WINEDDBLTFAST_WAIT)
4068 Flags |= WINEDDBLT_WAIT;
4069 if(trans & WINEDDBLTFAST_DONOTWAIT)
4070 Flags |= WINEDDBLT_DONOTWAIT;
4072 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
4076 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
4079 static HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface)
4081 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4082 RGBQUAD col[256];
4083 IWineD3DPaletteImpl *pal = This->palette;
4084 unsigned int n;
4085 TRACE("(%p)\n", This);
4087 if (!pal) return WINED3D_OK;
4089 if (This->resource.format_desc->format == WINED3DFMT_P8
4090 || This->resource.format_desc->format == WINED3DFMT_A8P8)
4092 int bpp;
4093 GLenum format, internal, type;
4094 CONVERT_TYPES convert;
4096 /* Check if we are using a RTL mode which uses texturing for uploads */
4097 BOOL use_texture = (wined3d_settings.rendertargetlock_mode == RTL_READTEX || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX);
4099 /* Check if we have hardware palette conversion if we have convert is set to NO_CONVERSION */
4100 d3dfmt_get_conv(This, TRUE, use_texture, &format, &internal, &type, &convert, &bpp, FALSE);
4102 if((This->resource.usage & WINED3DUSAGE_RENDERTARGET) && (convert == NO_CONVERSION))
4104 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4106 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
4107 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
4109 /* We want to force a palette refresh, so mark the drawable as not being up to date */
4110 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
4112 /* Re-upload the palette */
4113 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4114 d3dfmt_p8_upload_palette(iface, convert);
4115 } else {
4116 if(!(This->Flags & SFLAG_INSYSMEM)) {
4117 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
4118 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
4120 TRACE("Dirtifying surface\n");
4121 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
4125 if(This->Flags & SFLAG_DIBSECTION) {
4126 TRACE("(%p): Updating the hdc's palette\n", This);
4127 for (n=0; n<256; n++) {
4128 col[n].rgbRed = pal->palents[n].peRed;
4129 col[n].rgbGreen = pal->palents[n].peGreen;
4130 col[n].rgbBlue = pal->palents[n].peBlue;
4131 col[n].rgbReserved = 0;
4133 SetDIBColorTable(This->hDC, 0, 256, col);
4136 /* Propagate the changes to the drawable when we have a palette. */
4137 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
4138 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
4140 return WINED3D_OK;
4143 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
4144 /** Check against the maximum texture sizes supported by the video card **/
4145 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4146 unsigned int pow2Width, pow2Height;
4148 This->glDescription.textureName = 0;
4149 This->glDescription.target = GL_TEXTURE_2D;
4151 /* Non-power2 support */
4152 if (GL_SUPPORT(ARB_TEXTURE_NON_POWER_OF_TWO) || GL_SUPPORT(WINE_NORMALIZED_TEXRECT)) {
4153 pow2Width = This->currentDesc.Width;
4154 pow2Height = This->currentDesc.Height;
4155 } else {
4156 /* Find the nearest pow2 match */
4157 pow2Width = pow2Height = 1;
4158 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
4159 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
4161 This->pow2Width = pow2Width;
4162 This->pow2Height = pow2Height;
4164 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
4165 /** TODO: add support for non power two compressed textures **/
4166 if (This->resource.format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
4168 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
4169 This, This->currentDesc.Width, This->currentDesc.Height);
4170 return WINED3DERR_NOTAVAILABLE;
4174 if(pow2Width != This->currentDesc.Width ||
4175 pow2Height != This->currentDesc.Height) {
4176 This->Flags |= SFLAG_NONPOW2;
4179 TRACE("%p\n", This);
4180 if ((This->pow2Width > GL_LIMITS(texture_size) || This->pow2Height > GL_LIMITS(texture_size)) && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL))) {
4181 /* one of three options
4182 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)
4183 2: Set the texture to the maximum size (bad idea)
4184 3: WARN and return WINED3DERR_NOTAVAILABLE;
4185 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.
4187 WARN("(%p) Creating an oversized surface: %ux%u (texture is %ux%u)\n",
4188 This, This->pow2Width, This->pow2Height, This->currentDesc.Width, This->currentDesc.Height);
4189 This->Flags |= SFLAG_OVERSIZE;
4191 /* This will be initialized on the first blt */
4192 This->glRect.left = 0;
4193 This->glRect.top = 0;
4194 This->glRect.right = 0;
4195 This->glRect.bottom = 0;
4196 } else {
4197 /* Check this after the oversize check - do not make an oversized surface a texture_rectangle one.
4198 Second also don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
4199 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
4200 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
4202 if(This->Flags & SFLAG_NONPOW2 && GL_SUPPORT(ARB_TEXTURE_RECTANGLE)
4203 && !((This->resource.format_desc->format == WINED3DFMT_P8) && GL_SUPPORT(EXT_PALETTED_TEXTURE)
4204 && (wined3d_settings.rendertargetlock_mode == RTL_READTEX
4205 || wined3d_settings.rendertargetlock_mode == RTL_TEXTEX)))
4207 This->glDescription.target = GL_TEXTURE_RECTANGLE_ARB;
4208 This->pow2Width = This->currentDesc.Width;
4209 This->pow2Height = This->currentDesc.Height;
4210 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
4213 /* No oversize, gl rect is the full texture size */
4214 This->Flags &= ~SFLAG_OVERSIZE;
4215 This->glRect.left = 0;
4216 This->glRect.top = 0;
4217 This->glRect.right = This->pow2Width;
4218 This->glRect.bottom = This->pow2Height;
4221 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4222 switch(wined3d_settings.offscreen_rendering_mode) {
4223 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4224 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
4225 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4229 This->Flags |= SFLAG_INSYSMEM;
4231 return WINED3D_OK;
4234 struct depth_blt_info
4236 GLenum binding;
4237 GLenum bind_target;
4238 enum tex_types tex_type;
4239 GLfloat coords[4][3];
4242 static void surface_get_depth_blt_info(GLenum target, GLsizei w, GLsizei h, struct depth_blt_info *info)
4244 GLfloat (*coords)[3] = info->coords;
4246 switch (target)
4248 default:
4249 FIXME("Unsupported texture target %#x\n", target);
4250 /* Fall back to GL_TEXTURE_2D */
4251 case GL_TEXTURE_2D:
4252 info->binding = GL_TEXTURE_BINDING_2D;
4253 info->bind_target = GL_TEXTURE_2D;
4254 info->tex_type = tex_2d;
4255 coords[0][0] = 0.0f; coords[0][1] = 1.0f; coords[0][2] = 0.0f;
4256 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 0.0f;
4257 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4258 coords[3][0] = 1.0f; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4259 break;
4261 case GL_TEXTURE_RECTANGLE_ARB:
4262 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
4263 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
4264 info->tex_type = tex_rect;
4265 coords[0][0] = 0.0f; coords[0][1] = h; coords[0][2] = 0.0f;
4266 coords[1][0] = w; coords[1][1] = h; coords[1][2] = 0.0f;
4267 coords[2][0] = 0.0f; coords[2][1] = 0.0f; coords[2][2] = 0.0f;
4268 coords[3][0] = w; coords[3][1] = 0.0f; coords[3][2] = 0.0f;
4269 break;
4271 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4272 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4273 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4274 info->tex_type = tex_cube;
4275 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4276 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4277 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4278 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4280 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4281 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4282 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4283 info->tex_type = tex_cube;
4284 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4285 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4286 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4287 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4289 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4290 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4291 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4292 info->tex_type = tex_cube;
4293 coords[0][0] = -1.0f; coords[0][1] = 1.0f; coords[0][2] = 1.0f;
4294 coords[1][0] = 1.0f; coords[1][1] = 1.0f; coords[1][2] = 1.0f;
4295 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4296 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4298 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4299 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4300 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4301 info->tex_type = tex_cube;
4302 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4303 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4304 coords[2][0] = -1.0f; coords[2][1] = -1.0f; coords[2][2] = 1.0f;
4305 coords[3][0] = 1.0f; coords[3][1] = -1.0f; coords[3][2] = 1.0f;
4307 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4308 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4309 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4310 info->tex_type = tex_cube;
4311 coords[0][0] = -1.0f; coords[0][1] = -1.0f; coords[0][2] = 1.0f;
4312 coords[1][0] = 1.0f; coords[1][1] = -1.0f; coords[1][2] = 1.0f;
4313 coords[2][0] = -1.0f; coords[2][1] = 1.0f; coords[2][2] = 1.0f;
4314 coords[3][0] = 1.0f; coords[3][1] = 1.0f; coords[3][2] = 1.0f;
4316 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4317 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
4318 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4319 info->tex_type = tex_cube;
4320 coords[0][0] = 1.0f; coords[0][1] = -1.0f; coords[0][2] = -1.0f;
4321 coords[1][0] = -1.0f; coords[1][1] = -1.0f; coords[1][2] = -1.0f;
4322 coords[2][0] = 1.0f; coords[2][1] = 1.0f; coords[2][2] = -1.0f;
4323 coords[3][0] = -1.0f; coords[3][1] = 1.0f; coords[3][2] = -1.0f;
4327 /* GL locking is done by the caller */
4328 static void surface_depth_blt(IWineD3DSurfaceImpl *This, GLuint texture, GLsizei w, GLsizei h, GLenum target)
4330 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4331 struct depth_blt_info info;
4332 GLint old_binding = 0;
4334 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4336 glDisable(GL_CULL_FACE);
4337 glDisable(GL_BLEND);
4338 glDisable(GL_ALPHA_TEST);
4339 glDisable(GL_SCISSOR_TEST);
4340 glDisable(GL_STENCIL_TEST);
4341 glEnable(GL_DEPTH_TEST);
4342 glDepthFunc(GL_ALWAYS);
4343 glDepthMask(GL_TRUE);
4344 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4345 glViewport(0, 0, w, h);
4347 surface_get_depth_blt_info(target, w, h, &info);
4348 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4349 glGetIntegerv(info.binding, &old_binding);
4350 glBindTexture(info.bind_target, texture);
4352 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4354 glBegin(GL_TRIANGLE_STRIP);
4355 glTexCoord3fv(info.coords[0]);
4356 glVertex2f(-1.0f, -1.0f);
4357 glTexCoord3fv(info.coords[1]);
4358 glVertex2f(1.0f, -1.0f);
4359 glTexCoord3fv(info.coords[2]);
4360 glVertex2f(-1.0f, 1.0f);
4361 glTexCoord3fv(info.coords[3]);
4362 glVertex2f(1.0f, 1.0f);
4363 glEnd();
4365 glBindTexture(info.bind_target, old_binding);
4367 glPopAttrib();
4369 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4372 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
4373 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4375 TRACE("(%p) New location %#x\n", This, location);
4377 if (location & ~SFLAG_DS_LOCATIONS) {
4378 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
4381 This->Flags &= ~SFLAG_DS_LOCATIONS;
4382 This->Flags |= location;
4385 /* Context activation is done by the caller. */
4386 void surface_load_ds_location(IWineD3DSurface *iface, DWORD location) {
4387 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4388 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4390 TRACE("(%p) New location %#x\n", This, location);
4392 /* TODO: Make this work for modes other than FBO */
4393 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4395 if (This->Flags & location) {
4396 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
4397 return;
4400 if (This->current_renderbuffer) {
4401 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
4402 return;
4405 if (location == SFLAG_DS_OFFSCREEN) {
4406 if (This->Flags & SFLAG_DS_ONSCREEN) {
4407 GLint old_binding = 0;
4408 GLenum bind_target;
4410 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
4412 ENTER_GL();
4414 if (!device->depth_blt_texture) {
4415 glGenTextures(1, &device->depth_blt_texture);
4418 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4419 * directly on the FBO texture. That's because we need to flip. */
4420 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4421 if (This->glDescription.target == GL_TEXTURE_RECTANGLE_ARB) {
4422 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4423 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4424 } else {
4425 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4426 bind_target = GL_TEXTURE_2D;
4428 glBindTexture(bind_target, device->depth_blt_texture);
4429 glCopyTexImage2D(bind_target,
4430 This->glDescription.level,
4431 This->resource.format_desc->glInternal,
4434 This->currentDesc.Width,
4435 This->currentDesc.Height,
4437 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4438 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4439 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4440 glBindTexture(bind_target, old_binding);
4442 /* Setup the destination */
4443 if (!device->depth_blt_rb) {
4444 GL_EXTCALL(glGenRenderbuffersEXT(1, &device->depth_blt_rb));
4445 checkGLcall("glGenRenderbuffersEXT");
4447 if (device->depth_blt_rb_w != This->currentDesc.Width
4448 || device->depth_blt_rb_h != This->currentDesc.Height) {
4449 GL_EXTCALL(glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4450 checkGLcall("glBindRenderbufferEXT");
4451 GL_EXTCALL(glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8, This->currentDesc.Width, This->currentDesc.Height));
4452 checkGLcall("glRenderbufferStorageEXT");
4453 device->depth_blt_rb_w = This->currentDesc.Width;
4454 device->depth_blt_rb_h = This->currentDesc.Height;
4457 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->dst_fbo);
4458 GL_EXTCALL(glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, device->depth_blt_rb));
4459 checkGLcall("glFramebufferRenderbufferEXT");
4460 context_attach_depth_stencil_fbo(device, GL_FRAMEBUFFER_EXT, iface, FALSE);
4462 /* Do the actual blit */
4463 surface_depth_blt(This, device->depth_blt_texture, This->currentDesc.Width, This->currentDesc.Height, bind_target);
4464 checkGLcall("depth_blt");
4466 if (device->activeContext->current_fbo) {
4467 context_bind_fbo((IWineD3DDevice *)device, GL_FRAMEBUFFER_EXT, &device->activeContext->current_fbo->id);
4468 } else {
4469 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4470 checkGLcall("glBindFramebuffer()");
4473 LEAVE_GL();
4474 } else {
4475 FIXME("No up to date depth stencil location\n");
4477 } else if (location == SFLAG_DS_ONSCREEN) {
4478 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4479 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4481 ENTER_GL();
4483 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0));
4484 checkGLcall("glBindFramebuffer()");
4485 surface_depth_blt(This, This->glDescription.textureName, This->currentDesc.Width, This->currentDesc.Height, This->glDescription.target);
4486 checkGLcall("depth_blt");
4488 if (device->activeContext->current_fbo) {
4489 GL_EXTCALL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, device->activeContext->current_fbo->id));
4490 checkGLcall("glBindFramebuffer()");
4493 LEAVE_GL();
4494 } else {
4495 FIXME("No up to date depth stencil location\n");
4497 } else {
4498 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4501 This->Flags |= location;
4504 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4505 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4506 IWineD3DBaseTexture *texture;
4507 IWineD3DSurfaceImpl *overlay;
4509 TRACE("(%p)->(%s, %s)\n", iface, debug_surflocation(flag),
4510 persistent ? "TRUE" : "FALSE");
4512 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4513 if (This->Flags & SFLAG_SWAPCHAIN)
4515 TRACE("Surface %p is an onscreen surface\n", iface);
4516 } else {
4517 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4518 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4522 if(persistent) {
4523 if(((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) ||
4524 ((This->Flags & SFLAG_INSRGBTEX) && !(flag & 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 &= ~SFLAG_LOCATIONS;
4532 This->Flags |= flag;
4534 /* Redraw emulated overlays, if any */
4535 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4536 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4537 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4540 } else {
4541 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))) {
4542 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4543 TRACE("Passing to container\n");
4544 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4545 IWineD3DBaseTexture_Release(texture);
4548 This->Flags &= ~flag;
4551 if(!(This->Flags & SFLAG_LOCATIONS)) {
4552 ERR("%p: Surface does not have any up to date location\n", This);
4556 struct coords {
4557 GLfloat x, y, z;
4560 struct float_rect
4562 float l;
4563 float t;
4564 float r;
4565 float b;
4568 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
4570 f->l = ((r->left * 2.0f) / w) - 1.0f;
4571 f->t = ((r->top * 2.0f) / h) - 1.0f;
4572 f->r = ((r->right * 2.0f) / w) - 1.0f;
4573 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
4576 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in) {
4577 struct coords coords[4];
4578 RECT rect;
4579 IWineD3DSwapChain *swapchain;
4580 IWineD3DBaseTexture *texture;
4581 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4582 GLenum bind_target;
4583 struct float_rect f;
4585 if(rect_in) {
4586 rect = *rect_in;
4587 } else {
4588 rect.left = 0;
4589 rect.top = 0;
4590 rect.right = This->currentDesc.Width;
4591 rect.bottom = This->currentDesc.Height;
4594 switch(This->glDescription.target)
4596 case GL_TEXTURE_2D:
4597 bind_target = GL_TEXTURE_2D;
4599 coords[0].x = (float)rect.left / This->pow2Width;
4600 coords[0].y = (float)rect.top / This->pow2Height;
4601 coords[0].z = 0;
4603 coords[1].x = (float)rect.left / This->pow2Width;
4604 coords[1].y = (float)rect.bottom / This->pow2Height;
4605 coords[1].z = 0;
4607 coords[2].x = (float)rect.right / This->pow2Width;
4608 coords[2].y = (float)rect.bottom / This->pow2Height;
4609 coords[2].z = 0;
4611 coords[3].x = (float)rect.right / This->pow2Width;
4612 coords[3].y = (float)rect.top / This->pow2Height;
4613 coords[3].z = 0;
4614 break;
4616 case GL_TEXTURE_RECTANGLE_ARB:
4617 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4618 coords[0].x = rect.left; coords[0].y = rect.top; coords[0].z = 0;
4619 coords[1].x = rect.left; coords[1].y = rect.bottom; coords[1].z = 0;
4620 coords[2].x = rect.right; coords[2].y = rect.bottom; coords[2].z = 0;
4621 coords[3].x = rect.right; coords[3].y = rect.top; coords[3].z = 0;
4622 break;
4624 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
4625 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4626 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4627 coords[0].x = 1; coords[0].y = -f.t; coords[0].z = -f.l;
4628 coords[1].x = 1; coords[1].y = -f.b; coords[1].z = -f.l;
4629 coords[2].x = 1; coords[2].y = -f.b; coords[2].z = -f.r;
4630 coords[3].x = 1; coords[3].y = -f.t; coords[3].z = -f.r;
4631 break;
4633 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
4634 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4635 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4636 coords[0].x = -1; coords[0].y = -f.t; coords[0].z = f.l;
4637 coords[1].x = -1; coords[1].y = -f.b; coords[1].z = f.l;
4638 coords[2].x = -1; coords[2].y = -f.b; coords[2].z = f.r;
4639 coords[3].x = -1; coords[3].y = -f.t; coords[3].z = f.r;
4640 break;
4642 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
4643 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4644 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4645 coords[0].x = f.l; coords[0].y = 1; coords[0].z = f.t;
4646 coords[1].x = f.l; coords[1].y = 1; coords[1].z = f.b;
4647 coords[2].x = f.r; coords[2].y = 1; coords[2].z = f.b;
4648 coords[3].x = f.r; coords[3].y = 1; coords[3].z = f.t;
4649 break;
4651 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
4652 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4653 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4654 coords[0].x = f.l; coords[0].y = -1; coords[0].z = -f.t;
4655 coords[1].x = f.l; coords[1].y = -1; coords[1].z = -f.b;
4656 coords[2].x = f.r; coords[2].y = -1; coords[2].z = -f.b;
4657 coords[3].x = f.r; coords[3].y = -1; coords[3].z = -f.t;
4658 break;
4660 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
4661 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4662 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4663 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1;
4664 coords[1].x = f.l; coords[1].y = -f.b; coords[1].z = 1;
4665 coords[2].x = f.r; coords[2].y = -f.b; coords[2].z = 1;
4666 coords[3].x = f.r; coords[3].y = -f.t; coords[3].z = 1;
4667 break;
4669 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
4670 bind_target = GL_TEXTURE_CUBE_MAP_ARB;
4671 cube_coords_float(&rect, This->pow2Width, This->pow2Height, &f);
4672 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1;
4673 coords[1].x = -f.l; coords[1].y = -f.b; coords[1].z = -1;
4674 coords[2].x = -f.r; coords[2].y = -f.b; coords[2].z = -1;
4675 coords[3].x = -f.r; coords[3].y = -f.t; coords[3].z = -1;
4676 break;
4678 default:
4679 ERR("Unexpected texture target %#x\n", This->glDescription.target);
4680 return;
4683 ActivateContext(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4684 ENTER_GL();
4686 glEnable(bind_target);
4687 checkGLcall("glEnable(bind_target)");
4688 glBindTexture(bind_target, This->glDescription.textureName);
4689 checkGLcall("bind_target, This->glDescription.textureName)");
4690 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4691 checkGLcall("glTexParameteri");
4692 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4693 checkGLcall("glTexParameteri");
4695 if (device->render_offscreen)
4697 LONG tmp = rect.top;
4698 rect.top = rect.bottom;
4699 rect.bottom = tmp;
4702 glBegin(GL_QUADS);
4703 glTexCoord3fv(&coords[0].x);
4704 glVertex2i(rect.left, rect.top);
4706 glTexCoord3fv(&coords[1].x);
4707 glVertex2i(rect.left, rect.bottom);
4709 glTexCoord3fv(&coords[2].x);
4710 glVertex2i(rect.right, rect.bottom);
4712 glTexCoord3fv(&coords[3].x);
4713 glVertex2i(rect.right, rect.top);
4714 glEnd();
4715 checkGLcall("glEnd");
4717 glDisable(bind_target);
4718 checkGLcall("glDisable(bind_target)");
4720 LEAVE_GL();
4722 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DSwapChain, (void **) &swapchain)))
4724 /* Make sure to flush the buffers. This is needed in apps like Red Alert II and Tiberian SUN that use multiple WGL contexts. */
4725 if(((IWineD3DSwapChainImpl*)swapchain)->frontBuffer == (IWineD3DSurface*)This ||
4726 ((IWineD3DSwapChainImpl*)swapchain)->num_contexts >= 2)
4727 glFlush();
4729 IWineD3DSwapChain_Release(swapchain);
4730 } else {
4731 /* We changed the filtering settings on the texture. Inform the container about this to get the filters
4732 * reset properly next draw
4734 if(SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface*)This, &IID_IWineD3DBaseTexture, (void **) &texture)))
4736 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
4737 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
4738 ((IWineD3DBaseTextureImpl *) texture)->baseTexture.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
4739 IWineD3DBaseTexture_Release(texture);
4744 /*****************************************************************************
4745 * IWineD3DSurface::LoadLocation
4747 * Copies the current surface data from wherever it is to the requested
4748 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4749 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4750 * multiple locations, the gl texture is preferred over the drawable, which is
4751 * preferred over system memory. The PBO counts as system memory. If rect is
4752 * not NULL, only the specified rectangle is copied (only supported for
4753 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4754 * location is marked up to date after the copy.
4756 * Parameters:
4757 * flag: Surface location flag to be updated
4758 * rect: rectangle to be copied
4760 * Returns:
4761 * WINED3D_OK on success
4762 * WINED3DERR_DEVICELOST on an internal error
4764 *****************************************************************************/
4765 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4766 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4767 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
4768 GLenum format, internal, type;
4769 CONVERT_TYPES convert;
4770 int bpp;
4771 int width, pitch, outpitch;
4772 BYTE *mem;
4773 BOOL drawable_read_ok = TRUE;
4775 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4776 if (This->Flags & SFLAG_SWAPCHAIN)
4778 TRACE("Surface %p is an onscreen surface\n", iface);
4779 } else {
4780 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4781 * Prefer SFLAG_INTEXTURE. */
4782 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4783 drawable_read_ok = FALSE;
4787 TRACE("(%p)->(%s, %p)\n", iface, debug_surflocation(flag), rect);
4788 if(rect) {
4789 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4792 if(This->Flags & flag) {
4793 TRACE("Location already up to date\n");
4794 return WINED3D_OK;
4797 if(!(This->Flags & SFLAG_LOCATIONS)) {
4798 ERR("%p: Surface does not have any up to date location\n", This);
4799 This->Flags |= SFLAG_LOST;
4800 return WINED3DERR_DEVICELOST;
4803 if(flag == SFLAG_INSYSMEM) {
4804 surface_prepare_system_memory(This);
4806 /* Download the surface to system memory */
4807 if(This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) {
4808 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4809 surface_bind_and_dirtify(This, !(This->Flags & SFLAG_INTEXTURE));
4811 surface_download_data(This);
4812 } else {
4813 /* Note: It might be faster to download into a texture first. */
4814 read_from_framebuffer(This, rect,
4815 This->resource.allocatedMemory,
4816 IWineD3DSurface_GetPitch(iface));
4818 } else if(flag == SFLAG_INDRAWABLE) {
4819 if(This->Flags & SFLAG_INTEXTURE) {
4820 surface_blt_to_drawable(This, rect);
4821 } else {
4822 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4823 /* This needs a shader to convert the srgb data sampled from the GL texture into RGB
4824 * values, otherwise we get incorrect values in the target. For now go the slow way
4825 * via a system memory copy
4827 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4830 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, FALSE);
4832 /* The width is in 'length' not in bytes */
4833 width = This->currentDesc.Width;
4834 pitch = IWineD3DSurface_GetPitch(iface);
4836 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4837 * but it isn't set (yet) in all cases it is getting called. */
4838 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4839 TRACE("Removing the pbo attached to surface %p\n", This);
4840 if (!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4841 surface_remove_pbo(This);
4844 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4845 int height = This->currentDesc.Height;
4847 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4848 outpitch = width * bpp;
4849 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4851 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4852 if(!mem) {
4853 ERR("Out of memory %d, %d!\n", outpitch, height);
4854 return WINED3DERR_OUTOFVIDEOMEMORY;
4856 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4858 This->Flags |= SFLAG_CONVERTED;
4859 } else {
4860 This->Flags &= ~SFLAG_CONVERTED;
4861 mem = This->resource.allocatedMemory;
4864 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4866 /* Don't delete PBO memory */
4867 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4868 HeapFree(GetProcessHeap(), 0, mem);
4870 } else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */ {
4871 if (drawable_read_ok && (This->Flags & SFLAG_INDRAWABLE)) {
4872 read_from_framebuffer_texture(This, flag == SFLAG_INSRGBTEX);
4873 } else { /* Upload from system memory */
4874 BOOL srgb = flag == SFLAG_INSRGBTEX;
4875 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
4876 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
4878 if(srgb) {
4879 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE) {
4880 /* Performance warning ... */
4881 FIXME("%p: Downloading rgb texture to reload it as srgb\n", This);
4882 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4884 } else {
4885 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4886 /* Performance warning ... */
4887 FIXME("%p: Downloading srgb texture to reload it as rgb\n", This);
4888 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4892 if(!device->isInDraw) ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
4893 surface_bind_and_dirtify(This, srgb);
4895 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4896 This->Flags |= SFLAG_GLCKEY;
4897 This->glCKey = This->SrcBltCKey;
4899 else This->Flags &= ~SFLAG_GLCKEY;
4901 /* The width is in 'length' not in bytes */
4902 width = This->currentDesc.Width;
4903 pitch = IWineD3DSurface_GetPitch(iface);
4905 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4906 * but it isn't set (yet) in all cases it is getting called. */
4907 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4908 TRACE("Removing the pbo attached to surface %p\n", This);
4909 surface_remove_pbo(This);
4912 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4913 int height = This->currentDesc.Height;
4915 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4916 outpitch = width * bpp;
4917 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4919 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4920 if(!mem) {
4921 ERR("Out of memory %d, %d!\n", outpitch, height);
4922 return WINED3DERR_OUTOFVIDEOMEMORY;
4924 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4926 This->Flags |= SFLAG_CONVERTED;
4928 else if ((This->resource.format_desc->format == WINED3DFMT_P8)
4929 && (GL_SUPPORT(EXT_PALETTED_TEXTURE) || GL_SUPPORT(ARB_FRAGMENT_PROGRAM)))
4931 d3dfmt_p8_upload_palette(iface, convert);
4932 This->Flags &= ~SFLAG_CONVERTED;
4933 mem = This->resource.allocatedMemory;
4934 } else {
4935 This->Flags &= ~SFLAG_CONVERTED;
4936 mem = This->resource.allocatedMemory;
4939 /* Make sure the correct pitch is used */
4940 ENTER_GL();
4941 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4942 LEAVE_GL();
4944 if ((This->Flags & SFLAG_NONPOW2) && !(This->Flags & SFLAG_OVERSIZE)) {
4945 TRACE("non power of two support\n");
4946 if(!(This->Flags & alloc_flag)) {
4947 surface_allocate_surface(This, internal, This->pow2Width, This->pow2Height, format, type);
4948 This->Flags |= alloc_flag;
4950 if (mem || (This->Flags & SFLAG_PBO)) {
4951 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
4953 } else {
4954 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4955 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4957 if(!(This->Flags & alloc_flag)) {
4958 surface_allocate_surface(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type);
4959 This->Flags |= alloc_flag;
4961 if (mem || (This->Flags & SFLAG_PBO)) {
4962 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
4966 /* Restore the default pitch */
4967 ENTER_GL();
4968 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4969 LEAVE_GL();
4971 /* Don't delete PBO memory */
4972 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4973 HeapFree(GetProcessHeap(), 0, mem);
4977 if(rect == NULL) {
4978 This->Flags |= flag;
4981 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO && !(This->Flags & SFLAG_SWAPCHAIN)
4982 && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4983 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4984 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4987 return WINED3D_OK;
4990 static HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container)
4992 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4993 IWineD3DSwapChain *swapchain = NULL;
4995 /* Update the drawable size method */
4996 if(container) {
4997 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4999 if(swapchain) {
5000 This->get_drawable_size = get_drawable_size_swapchain;
5001 IWineD3DSwapChain_Release(swapchain);
5002 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
5003 switch(wined3d_settings.offscreen_rendering_mode) {
5004 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
5005 case ORM_PBUFFER: This->get_drawable_size = get_drawable_size_pbuffer; break;
5006 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
5010 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
5013 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
5014 return SURFACE_OPENGL;
5017 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
5018 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
5019 HRESULT hr;
5021 /* If there's no destination surface there is nothing to do */
5022 if(!This->overlay_dest) return WINED3D_OK;
5024 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
5025 * update the overlay. Prevent an endless recursion
5027 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
5028 return WINED3D_OK;
5030 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
5031 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
5032 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
5033 NULL, WINED3DTEXF_LINEAR);
5034 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
5036 return hr;
5039 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
5041 /* IUnknown */
5042 IWineD3DBaseSurfaceImpl_QueryInterface,
5043 IWineD3DBaseSurfaceImpl_AddRef,
5044 IWineD3DSurfaceImpl_Release,
5045 /* IWineD3DResource */
5046 IWineD3DBaseSurfaceImpl_GetParent,
5047 IWineD3DBaseSurfaceImpl_GetDevice,
5048 IWineD3DBaseSurfaceImpl_SetPrivateData,
5049 IWineD3DBaseSurfaceImpl_GetPrivateData,
5050 IWineD3DBaseSurfaceImpl_FreePrivateData,
5051 IWineD3DBaseSurfaceImpl_SetPriority,
5052 IWineD3DBaseSurfaceImpl_GetPriority,
5053 IWineD3DSurfaceImpl_PreLoad,
5054 IWineD3DSurfaceImpl_UnLoad,
5055 IWineD3DBaseSurfaceImpl_GetType,
5056 /* IWineD3DSurface */
5057 IWineD3DBaseSurfaceImpl_GetContainer,
5058 IWineD3DBaseSurfaceImpl_GetDesc,
5059 IWineD3DSurfaceImpl_LockRect,
5060 IWineD3DSurfaceImpl_UnlockRect,
5061 IWineD3DSurfaceImpl_GetDC,
5062 IWineD3DSurfaceImpl_ReleaseDC,
5063 IWineD3DSurfaceImpl_Flip,
5064 IWineD3DSurfaceImpl_Blt,
5065 IWineD3DBaseSurfaceImpl_GetBltStatus,
5066 IWineD3DBaseSurfaceImpl_GetFlipStatus,
5067 IWineD3DBaseSurfaceImpl_IsLost,
5068 IWineD3DBaseSurfaceImpl_Restore,
5069 IWineD3DSurfaceImpl_BltFast,
5070 IWineD3DBaseSurfaceImpl_GetPalette,
5071 IWineD3DBaseSurfaceImpl_SetPalette,
5072 IWineD3DSurfaceImpl_RealizePalette,
5073 IWineD3DBaseSurfaceImpl_SetColorKey,
5074 IWineD3DBaseSurfaceImpl_GetPitch,
5075 IWineD3DSurfaceImpl_SetMem,
5076 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
5077 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
5078 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
5079 IWineD3DBaseSurfaceImpl_UpdateOverlay,
5080 IWineD3DBaseSurfaceImpl_SetClipper,
5081 IWineD3DBaseSurfaceImpl_GetClipper,
5082 /* Internal use: */
5083 IWineD3DSurfaceImpl_LoadTexture,
5084 IWineD3DSurfaceImpl_BindTexture,
5085 IWineD3DSurfaceImpl_SaveSnapshot,
5086 IWineD3DSurfaceImpl_SetContainer,
5087 IWineD3DSurfaceImpl_GetGlDesc,
5088 IWineD3DBaseSurfaceImpl_GetData,
5089 IWineD3DSurfaceImpl_SetFormat,
5090 IWineD3DSurfaceImpl_PrivateSetup,
5091 IWineD3DSurfaceImpl_ModifyLocation,
5092 IWineD3DSurfaceImpl_LoadLocation,
5093 IWineD3DSurfaceImpl_GetImplType,
5094 IWineD3DSurfaceImpl_DrawOverlay
5096 #undef GLINFO_LOCATION
5098 #define GLINFO_LOCATION device->adapter->gl_info
5099 static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
5100 /* Context activation is done by the caller. */
5101 static void ffp_blit_free(IWineD3DDevice *iface) { }
5103 /* Context activation is done by the caller. */
5104 static HRESULT ffp_blit_set(IWineD3DDevice *iface, const struct GlPixelFormatDesc *format_desc,
5105 GLenum textype, UINT width, UINT height)
5107 ENTER_GL();
5108 glEnable(textype);
5109 checkGLcall("glEnable(textype)");
5110 LEAVE_GL();
5111 return WINED3D_OK;
5114 /* Context activation is done by the caller. */
5115 static void ffp_blit_unset(IWineD3DDevice *iface) {
5116 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
5117 ENTER_GL();
5118 glDisable(GL_TEXTURE_2D);
5119 checkGLcall("glDisable(GL_TEXTURE_2D)");
5120 if(GL_SUPPORT(ARB_TEXTURE_CUBE_MAP)) {
5121 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
5122 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
5124 if(GL_SUPPORT(ARB_TEXTURE_RECTANGLE)) {
5125 glDisable(GL_TEXTURE_RECTANGLE_ARB);
5126 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
5128 LEAVE_GL();
5131 static BOOL ffp_blit_color_fixup_supported(struct color_fixup_desc fixup)
5133 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
5135 TRACE("Checking support for fixup:\n");
5136 dump_color_fixup_desc(fixup);
5139 /* We only support identity conversions. */
5140 if (is_identity_fixup(fixup))
5142 TRACE("[OK]\n");
5143 return TRUE;
5146 TRACE("[FAILED]\n");
5147 return FALSE;
5150 const struct blit_shader ffp_blit = {
5151 ffp_blit_alloc,
5152 ffp_blit_free,
5153 ffp_blit_set,
5154 ffp_blit_unset,
5155 ffp_blit_color_fixup_supported