2 * IWineD3DSurface Implementation
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006-2008 Stefan Dösinger for CodeWeavers
11 * Copyright 2007-2008 Henri Verbeet
12 * Copyright 2006-2008 Roderick Colenbrander
13 * Copyright 2009 Henri Verbeet for CodeWeavers
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wine/port.h"
32 #include "wined3d_private.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
35 WINE_DECLARE_DEBUG_CHANNEL(d3d);
37 #define GLINFO_LOCATION (*gl_info)
39 static void surface_cleanup(IWineD3DSurfaceImpl *This)
41 IWineD3DDeviceImpl *device = This->resource.device;
42 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
43 struct wined3d_context *context = NULL;
44 renderbuffer_entry_t *entry, *entry2;
46 TRACE("(%p) : Cleaning up.\n", This);
48 /* Need a context to destroy the texture. Use the currently active render
49 * target, but only if the primary render target exists. Otherwise
50 * lastActiveRenderTarget is garbage. When destroying the primary render
51 * target, Uninit3D() will activate a context before doing anything. */
52 if (device->render_targets && device->render_targets[0])
54 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
59 if (This->texture_name)
61 /* Release the OpenGL texture. */
62 TRACE("Deleting texture %u.\n", This->texture_name);
63 glDeleteTextures(1, &This->texture_name);
66 if (This->Flags & SFLAG_PBO)
69 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
72 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry)
74 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
75 HeapFree(GetProcessHeap(), 0, entry);
80 if (This->Flags & SFLAG_DIBSECTION)
83 SelectObject(This->hDC, This->dib.holdbitmap);
85 /* Release the DIB section. */
86 DeleteObject(This->dib.DIBsection);
87 This->dib.bitmap_data = NULL;
88 This->resource.allocatedMemory = NULL;
91 if (This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem((IWineD3DSurface *)This, NULL);
92 if (This->overlay_dest) list_remove(&This->overlay_entry);
94 HeapFree(GetProcessHeap(), 0, This->palette9);
96 resource_cleanup((IWineD3DResource *)This);
98 if (context) context_release(context);
101 UINT surface_calculate_size(const struct wined3d_format_desc *format_desc, UINT alignment, UINT width, UINT height)
105 if (format_desc->format == WINED3DFMT_UNKNOWN)
109 else if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
111 UINT row_block_count = (width + format_desc->block_width - 1) / format_desc->block_width;
112 UINT row_count = (height + format_desc->block_height - 1) / format_desc->block_height;
113 size = row_count * row_block_count * format_desc->block_byte_count;
117 /* The pitch is a multiple of 4 bytes. */
118 size = height * (((width * format_desc->byte_count) + alignment - 1) & ~(alignment - 1));
121 if (format_desc->heightscale != 0.0f) size *= format_desc->heightscale;
130 enum tex_types tex_type;
131 GLfloat coords[4][3];
142 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
144 f->l = ((r->left * 2.0f) / w) - 1.0f;
145 f->t = ((r->top * 2.0f) / h) - 1.0f;
146 f->r = ((r->right * 2.0f) / w) - 1.0f;
147 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
150 static void surface_get_blt_info(GLenum target, const RECT *rect_in, GLsizei w, GLsizei h, struct blt_info *info)
152 GLfloat (*coords)[3] = info->coords;
169 FIXME("Unsupported texture target %#x\n", target);
170 /* Fall back to GL_TEXTURE_2D */
172 info->binding = GL_TEXTURE_BINDING_2D;
173 info->bind_target = GL_TEXTURE_2D;
174 info->tex_type = tex_2d;
175 coords[0][0] = (float)rect.left / w;
176 coords[0][1] = (float)rect.top / h;
179 coords[1][0] = (float)rect.right / w;
180 coords[1][1] = (float)rect.top / h;
183 coords[2][0] = (float)rect.left / w;
184 coords[2][1] = (float)rect.bottom / h;
187 coords[3][0] = (float)rect.right / w;
188 coords[3][1] = (float)rect.bottom / h;
192 case GL_TEXTURE_RECTANGLE_ARB:
193 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
194 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
195 info->tex_type = tex_rect;
196 coords[0][0] = rect.left; coords[0][1] = rect.top; coords[0][2] = 0.0f;
197 coords[1][0] = rect.right; coords[1][1] = rect.top; coords[1][2] = 0.0f;
198 coords[2][0] = rect.left; coords[2][1] = rect.bottom; coords[2][2] = 0.0f;
199 coords[3][0] = rect.right; coords[3][1] = rect.bottom; coords[3][2] = 0.0f;
202 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
203 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
204 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
205 info->tex_type = tex_cube;
206 cube_coords_float(&rect, w, h, &f);
208 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
209 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
210 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
211 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
214 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
215 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
216 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
217 info->tex_type = tex_cube;
218 cube_coords_float(&rect, w, h, &f);
220 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
221 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
222 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
223 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
226 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
227 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
228 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
229 info->tex_type = tex_cube;
230 cube_coords_float(&rect, w, h, &f);
232 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
233 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
234 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
235 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
238 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
239 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
240 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
241 info->tex_type = tex_cube;
242 cube_coords_float(&rect, w, h, &f);
244 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
245 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
246 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
247 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
250 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
251 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
252 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
253 info->tex_type = tex_cube;
254 cube_coords_float(&rect, w, h, &f);
256 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
257 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
258 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
259 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
262 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
263 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
264 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
265 info->tex_type = tex_cube;
266 cube_coords_float(&rect, w, h, &f);
268 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
269 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
270 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
271 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
276 static inline void surface_get_rect(IWineD3DSurfaceImpl *This, const RECT *rect_in, RECT *rect_out)
279 *rect_out = *rect_in;
284 rect_out->right = This->currentDesc.Width;
285 rect_out->bottom = This->currentDesc.Height;
289 /* GL locking and context activation is done by the caller */
290 void draw_textured_quad(IWineD3DSurfaceImpl *src_surface, const RECT *src_rect, const RECT *dst_rect, WINED3DTEXTUREFILTERTYPE Filter)
292 IWineD3DBaseTextureImpl *texture;
293 struct blt_info info;
295 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
297 glEnable(info.bind_target);
298 checkGLcall("glEnable(bind_target)");
300 /* Bind the texture */
301 glBindTexture(info.bind_target, src_surface->texture_name);
302 checkGLcall("glBindTexture");
304 /* Filtering for StretchRect */
305 glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
306 wined3d_gl_mag_filter(magLookup, Filter));
307 checkGLcall("glTexParameteri");
308 glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
309 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
310 checkGLcall("glTexParameteri");
311 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
312 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
313 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
314 checkGLcall("glTexEnvi");
317 glBegin(GL_TRIANGLE_STRIP);
318 glTexCoord3fv(info.coords[0]);
319 glVertex2i(dst_rect->left, dst_rect->top);
321 glTexCoord3fv(info.coords[1]);
322 glVertex2i(dst_rect->right, dst_rect->top);
324 glTexCoord3fv(info.coords[2]);
325 glVertex2i(dst_rect->left, dst_rect->bottom);
327 glTexCoord3fv(info.coords[3]);
328 glVertex2i(dst_rect->right, dst_rect->bottom);
331 /* Unbind the texture */
332 glBindTexture(info.bind_target, 0);
333 checkGLcall("glBindTexture(info->bind_target, 0)");
335 /* We changed the filtering settings on the texture. Inform the
336 * container about this to get the filters reset properly next draw. */
337 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)src_surface, &IID_IWineD3DBaseTexture, (void **)&texture)))
339 texture->baseTexture.texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
340 texture->baseTexture.texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
341 texture->baseTexture.texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
342 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *)texture);
346 HRESULT surface_init(IWineD3DSurfaceImpl *surface, WINED3DSURFTYPE surface_type, UINT alignment,
347 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
348 UINT multisample_quality, IWineD3DDeviceImpl *device, DWORD usage, WINED3DFORMAT format,
349 WINED3DPOOL pool, IUnknown *parent, const struct wined3d_parent_ops *parent_ops)
351 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
352 const struct wined3d_format_desc *format_desc = getFormatDescEntry(format, gl_info);
353 void (*cleanup)(IWineD3DSurfaceImpl *This);
354 unsigned int resource_size;
357 if (multisample_quality > 0)
359 FIXME("multisample_quality set to %u, substituting 0\n", multisample_quality);
360 multisample_quality = 0;
363 /* FIXME: Check that the format is supported by the device. */
365 resource_size = surface_calculate_size(format_desc, alignment, width, height);
367 /* Look at the implementation and set the correct Vtable. */
368 switch (surface_type)
371 surface->lpVtbl = &IWineD3DSurface_Vtbl;
372 cleanup = surface_cleanup;
376 surface->lpVtbl = &IWineGDISurface_Vtbl;
377 cleanup = surface_gdi_cleanup;
381 ERR("Requested unknown surface implementation %#x.\n", surface_type);
382 return WINED3DERR_INVALIDCALL;
385 hr = resource_init((IWineD3DResource *)surface, WINED3DRTYPE_SURFACE,
386 device, resource_size, usage, format_desc, pool, parent, parent_ops);
389 WARN("Failed to initialize resource, returning %#x.\n", hr);
393 /* "Standalone" surface. */
394 IWineD3DSurface_SetContainer((IWineD3DSurface *)surface, NULL);
396 surface->currentDesc.Width = width;
397 surface->currentDesc.Height = height;
398 surface->currentDesc.MultiSampleType = multisample_type;
399 surface->currentDesc.MultiSampleQuality = multisample_quality;
400 surface->texture_level = level;
401 list_init(&surface->overlays);
404 surface->Flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
405 if (discard) surface->Flags |= SFLAG_DISCARD;
406 if (lockable || format == WINED3DFMT_D16_LOCKABLE) surface->Flags |= SFLAG_LOCKABLE;
408 /* Quick lockable sanity check.
409 * TODO: remove this after surfaces, usage and lockability have been debugged properly
410 * this function is too deep to need to care about things like this.
411 * Levels need to be checked too, since they all affect what can be done. */
414 case WINED3DPOOL_SCRATCH:
417 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
418 "which are mutually exclusive, setting lockable to TRUE.\n");
423 case WINED3DPOOL_SYSTEMMEM:
425 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
428 case WINED3DPOOL_MANAGED:
429 if (usage & WINED3DUSAGE_DYNAMIC)
430 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
433 case WINED3DPOOL_DEFAULT:
434 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
435 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
439 FIXME("Unknown pool %#x.\n", pool);
443 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
445 FIXME("Trying to create a render target that isn't in the default pool.\n");
448 /* Mark the texture as dirty so that it gets loaded first time around. */
449 surface_add_dirty_rect((IWineD3DSurface *)surface, NULL);
450 list_init(&surface->renderbuffers);
452 TRACE("surface %p, memory %p, size %u\n", surface, surface->resource.allocatedMemory, surface->resource.size);
454 /* Call the private setup routine */
455 hr = IWineD3DSurface_PrivateSetup((IWineD3DSurface *)surface);
458 ERR("Private setup failed, returning %#x\n", hr);
466 static void surface_force_reload(IWineD3DSurface *iface)
468 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
470 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
473 void surface_set_texture_name(IWineD3DSurface *iface, GLuint new_name, BOOL srgb)
475 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
481 name = &This->texture_name_srgb;
482 flag = SFLAG_INSRGBTEX;
486 name = &This->texture_name;
487 flag = SFLAG_INTEXTURE;
490 TRACE("(%p) : setting texture name %u\n", This, new_name);
492 if (!*name && new_name)
494 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
495 * surface has no texture name yet. See if we can get rid of this. */
496 if (This->Flags & flag)
497 ERR("Surface has SFLAG_INTEXTURE set, but no texture name\n");
498 IWineD3DSurface_ModifyLocation(iface, flag, FALSE);
502 surface_force_reload(iface);
505 void surface_set_texture_target(IWineD3DSurface *iface, GLenum target)
507 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
509 TRACE("(%p) : setting target %#x\n", This, target);
511 if (This->texture_target != target)
513 if (target == GL_TEXTURE_RECTANGLE_ARB)
515 This->Flags &= ~SFLAG_NORMCOORD;
517 else if (This->texture_target == GL_TEXTURE_RECTANGLE_ARB)
519 This->Flags |= SFLAG_NORMCOORD;
522 This->texture_target = target;
523 surface_force_reload(iface);
526 /* Context activation is done by the caller. */
527 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This, BOOL srgb) {
528 DWORD active_sampler;
530 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
531 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
532 * gl states. The current texture unit should always be a valid one.
534 * To be more specific, this is tricky because we can implicitly be called
535 * from sampler() in state.c. This means we can't touch anything other than
536 * whatever happens to be the currently active texture, or we would risk
537 * marking already applied sampler states dirty again.
539 * TODO: Track the current active texture per GL context instead of using glGet
541 GLint active_texture;
543 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
545 active_sampler = This->resource.device->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
547 if (active_sampler != WINED3D_UNMAPPED_STAGE)
549 IWineD3DDeviceImpl_MarkStateDirty(This->resource.device, STATE_SAMPLER(active_sampler));
551 IWineD3DSurface_BindTexture((IWineD3DSurface *)This, srgb);
554 /* This function checks if the primary render target uses the 8bit paletted format. */
555 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
557 if (device->render_targets && device->render_targets[0]) {
558 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
559 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
560 && (render_target->resource.format_desc->format == WINED3DFMT_P8_UINT))
566 /* This call just downloads data, the caller is responsible for binding the
567 * correct texture. */
568 /* Context activation is done by the caller. */
569 static void surface_download_data(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info)
571 const struct wined3d_format_desc *format_desc = This->resource.format_desc;
573 /* Only support read back of converted P8 surfaces */
574 if (This->Flags & SFLAG_CONVERTED && format_desc->format != WINED3DFMT_P8_UINT)
576 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(format_desc->format));
582 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
584 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
585 This, This->texture_level, format_desc->glFormat, format_desc->glType,
586 This->resource.allocatedMemory);
588 if (This->Flags & SFLAG_PBO)
590 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
591 checkGLcall("glBindBufferARB");
592 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target, This->texture_level, NULL));
593 checkGLcall("glGetCompressedTexImageARB");
594 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
595 checkGLcall("glBindBufferARB");
599 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target,
600 This->texture_level, This->resource.allocatedMemory));
601 checkGLcall("glGetCompressedTexImageARB");
607 GLenum format = format_desc->glFormat;
608 GLenum type = format_desc->glType;
612 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
613 if (format_desc->format == WINED3DFMT_P8_UINT && primary_render_target_is_p8(This->resource.device))
616 type = GL_UNSIGNED_BYTE;
619 if (This->Flags & SFLAG_NONPOW2) {
620 unsigned char alignment = This->resource.device->surface_alignment;
621 src_pitch = format_desc->byte_count * This->pow2Width;
622 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
623 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
624 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
626 mem = This->resource.allocatedMemory;
629 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
630 This, This->texture_level, format, type, mem);
632 if(This->Flags & SFLAG_PBO) {
633 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
634 checkGLcall("glBindBufferARB");
636 glGetTexImage(This->texture_target, This->texture_level, format, type, NULL);
637 checkGLcall("glGetTexImage");
639 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
640 checkGLcall("glBindBufferARB");
642 glGetTexImage(This->texture_target, This->texture_level, format, type, mem);
643 checkGLcall("glGetTexImage");
647 if (This->Flags & SFLAG_NONPOW2) {
648 const BYTE *src_data;
652 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
653 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
654 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
656 * We're doing this...
658 * instead of boxing the texture :
659 * |<-texture width ->| -->pow2width| /\
660 * |111111111111111111| | |
661 * |222 Texture 222222| boxed empty | texture height
662 * |3333 Data 33333333| | |
663 * |444444444444444444| | \/
664 * ----------------------------------- |
665 * | boxed empty | boxed empty | pow2height
667 * -----------------------------------
670 * we're repacking the data to the expected texture width
672 * |<-texture width ->| -->pow2width| /\
673 * |111111111111111111222222222222222| |
674 * |222333333333333333333444444444444| texture height
678 * | empty | pow2height
680 * -----------------------------------
684 * |<-texture width ->| /\
685 * |111111111111111111|
686 * |222222222222222222|texture height
687 * |333333333333333333|
688 * |444444444444444444| \/
689 * --------------------
691 * this also means that any references to allocatedMemory should work with the data as if were a
692 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
694 * internally the texture is still stored in a boxed format so any references to textureName will
695 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
697 * Performance should not be an issue, because applications normally do not lock the surfaces when
698 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
699 * and doesn't have to be re-read.
702 dst_data = This->resource.allocatedMemory;
703 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
704 for (y = 1 ; y < This->currentDesc.Height; y++) {
705 /* skip the first row */
706 src_data += src_pitch;
707 dst_data += dst_pitch;
708 memcpy(dst_data, src_data, dst_pitch);
711 HeapFree(GetProcessHeap(), 0, mem);
715 /* Surface has now been downloaded */
716 This->Flags |= SFLAG_INSYSMEM;
719 /* This call just uploads data, the caller is responsible for binding the
720 * correct texture. */
721 /* Context activation is done by the caller. */
722 static void surface_upload_data(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info,
723 const struct wined3d_format_desc *format_desc, BOOL srgb, const GLvoid *data)
725 GLsizei width = This->currentDesc.Width;
726 GLsizei height = This->currentDesc.Height;
731 internal = format_desc->glGammaInternal;
733 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET
734 && surface_is_offscreen((IWineD3DSurface *)This))
736 internal = format_desc->rtInternal;
740 internal = format_desc->glInternal;
743 TRACE("This %p, internal %#x, width %d, height %d, format %#x, type %#x, data %p.\n",
744 This, internal, width, height, format_desc->glFormat, format_desc->glType, data);
745 TRACE("target %#x, level %u, resource size %u.\n",
746 This->texture_target, This->texture_level, This->resource.size);
748 if (format_desc->heightscale != 1.0f && format_desc->heightscale != 0.0f) height *= format_desc->heightscale;
752 if (This->Flags & SFLAG_PBO)
754 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
755 checkGLcall("glBindBufferARB");
757 TRACE("(%p) pbo: %#x, data: %p.\n", This, This->pbo, data);
761 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
763 TRACE("Calling glCompressedTexSubImage2DARB.\n");
765 GL_EXTCALL(glCompressedTexSubImage2DARB(This->texture_target, This->texture_level,
766 0, 0, width, height, internal, This->resource.size, data));
767 checkGLcall("glCompressedTexSubImage2DARB");
771 TRACE("Calling glTexSubImage2D.\n");
773 glTexSubImage2D(This->texture_target, This->texture_level,
774 0, 0, width, height, format_desc->glFormat, format_desc->glType, data);
775 checkGLcall("glTexSubImage2D");
778 if (This->Flags & SFLAG_PBO)
780 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
781 checkGLcall("glBindBufferARB");
786 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
788 IWineD3DDeviceImpl *device = This->resource.device;
791 for (i = 0; i < device->numContexts; ++i)
793 context_surface_update(device->contexts[i], This);
798 /* This call just allocates the texture, the caller is responsible for binding
799 * the correct texture. */
800 /* Context activation is done by the caller. */
801 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info,
802 const struct wined3d_format_desc *format_desc, BOOL srgb)
804 BOOL enable_client_storage = FALSE;
805 GLsizei width = This->pow2Width;
806 GLsizei height = This->pow2Height;
807 const BYTE *mem = NULL;
812 internal = format_desc->glGammaInternal;
814 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET
815 && surface_is_offscreen((IWineD3DSurface *)This))
817 internal = format_desc->rtInternal;
821 internal = format_desc->glInternal;
824 if (format_desc->heightscale != 1.0f && format_desc->heightscale != 0.0f) height *= format_desc->heightscale;
826 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",
827 This, This->texture_target, This->texture_level, debug_d3dformat(format_desc->format),
828 internal, width, height, format_desc->glFormat, format_desc->glType);
832 if (gl_info->supported[APPLE_CLIENT_STORAGE])
834 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
835 /* In some cases we want to disable client storage.
836 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
837 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
838 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
839 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
841 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
842 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
843 This->Flags &= ~SFLAG_CLIENT;
844 enable_client_storage = TRUE;
846 This->Flags |= SFLAG_CLIENT;
848 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
849 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
851 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
855 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED && mem)
857 GL_EXTCALL(glCompressedTexImage2DARB(This->texture_target, This->texture_level,
858 internal, width, height, 0, This->resource.size, mem));
862 glTexImage2D(This->texture_target, This->texture_level,
863 internal, width, height, 0, format_desc->glFormat, format_desc->glType, mem);
864 checkGLcall("glTexImage2D");
867 if(enable_client_storage) {
868 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
869 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
874 /* In D3D the depth stencil dimensions have to be greater than or equal to the
875 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
876 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
877 /* GL locking is done by the caller */
878 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
879 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
880 const struct wined3d_gl_info *gl_info = &This->resource.device->adapter->gl_info;
881 renderbuffer_entry_t *entry;
882 GLuint renderbuffer = 0;
883 unsigned int src_width, src_height;
885 src_width = This->pow2Width;
886 src_height = This->pow2Height;
888 /* A depth stencil smaller than the render target is not valid */
889 if (width > src_width || height > src_height) return;
891 /* Remove any renderbuffer set if the sizes match */
892 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
893 || (width == src_width && height == src_height))
895 This->current_renderbuffer = NULL;
899 /* Look if we've already got a renderbuffer of the correct dimensions */
900 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
901 if (entry->width == width && entry->height == height) {
902 renderbuffer = entry->id;
903 This->current_renderbuffer = entry;
909 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
910 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
911 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
912 This->resource.format_desc->glInternal, width, height);
914 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
915 entry->width = width;
916 entry->height = height;
917 entry->id = renderbuffer;
918 list_add_head(&This->renderbuffers, &entry->entry);
920 This->current_renderbuffer = entry;
923 checkGLcall("set_compatible_renderbuffer");
926 GLenum surface_get_gl_buffer(IWineD3DSurface *iface)
928 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
929 IWineD3DSwapChainImpl *swapchain = (IWineD3DSwapChainImpl *)This->container;
931 TRACE("iface %p.\n", iface);
933 if (!(This->Flags & SFLAG_SWAPCHAIN))
935 ERR("Surface %p is not on a swapchain.\n", iface);
939 if (swapchain->backBuffer && swapchain->backBuffer[0] == iface)
941 if (swapchain->render_to_fbo)
943 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
944 return GL_COLOR_ATTACHMENT0;
946 TRACE("Returning GL_BACK\n");
949 else if (swapchain->frontBuffer == iface)
951 TRACE("Returning GL_FRONT\n");
955 FIXME("Higher back buffer, returning GL_BACK\n");
959 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
960 void surface_add_dirty_rect(IWineD3DSurface *iface, const RECT *dirty_rect)
962 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
963 IWineD3DBaseTexture *baseTexture = NULL;
965 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
966 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
968 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
971 This->dirtyRect.left = min(This->dirtyRect.left, dirty_rect->left);
972 This->dirtyRect.top = min(This->dirtyRect.top, dirty_rect->top);
973 This->dirtyRect.right = max(This->dirtyRect.right, dirty_rect->right);
974 This->dirtyRect.bottom = max(This->dirtyRect.bottom, dirty_rect->bottom);
978 This->dirtyRect.left = 0;
979 This->dirtyRect.top = 0;
980 This->dirtyRect.right = This->currentDesc.Width;
981 This->dirtyRect.bottom = This->currentDesc.Height;
984 TRACE("(%p) : Dirty: yes, Rect:(%d, %d, %d, %d)\n", This, This->dirtyRect.left,
985 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
987 /* if the container is a basetexture then mark it dirty. */
988 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture)))
990 TRACE("Passing to container\n");
991 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
992 IWineD3DBaseTexture_Release(baseTexture);
996 static BOOL surface_convert_color_to_argb(IWineD3DSurfaceImpl *This, DWORD color, DWORD *argb_color)
998 IWineD3DDeviceImpl *device = This->resource.device;
1000 switch(This->resource.format_desc->format)
1002 case WINED3DFMT_P8_UINT:
1006 if (primary_render_target_is_p8(device))
1007 alpha = color << 24;
1011 if (This->palette) {
1012 *argb_color = (alpha |
1013 (This->palette->palents[color].peRed << 16) |
1014 (This->palette->palents[color].peGreen << 8) |
1015 (This->palette->palents[color].peBlue));
1017 *argb_color = alpha;
1022 case WINED3DFMT_B5G6R5_UNORM:
1024 if (color == 0xFFFF) {
1025 *argb_color = 0xFFFFFFFF;
1027 *argb_color = ((0xFF000000) |
1028 ((color & 0xF800) << 8) |
1029 ((color & 0x07E0) << 5) |
1030 ((color & 0x001F) << 3));
1035 case WINED3DFMT_B8G8R8_UNORM:
1036 case WINED3DFMT_B8G8R8X8_UNORM:
1037 *argb_color = 0xFF000000 | color;
1040 case WINED3DFMT_B8G8R8A8_UNORM:
1041 *argb_color = color;
1045 ERR("Unhandled conversion from %s to ARGB!\n", debug_d3dformat(This->resource.format_desc->format));
1051 static ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface)
1053 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1054 ULONG ref = InterlockedDecrement(&This->resource.ref);
1055 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
1059 surface_cleanup(This);
1060 This->resource.parent_ops->wined3d_object_destroyed(This->resource.parent);
1062 TRACE("(%p) Released.\n", This);
1063 HeapFree(GetProcessHeap(), 0, This);
1069 /* ****************************************************
1070 IWineD3DSurface IWineD3DResource parts follow
1071 **************************************************** */
1073 void surface_internal_preload(IWineD3DSurface *iface, enum WINED3DSRGB srgb)
1075 /* TODO: check for locks */
1076 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1077 IWineD3DDeviceImpl *device = This->resource.device;
1078 IWineD3DBaseTexture *baseTexture = NULL;
1080 TRACE("(%p)Checking to see if the container is a base texture\n", This);
1081 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
1082 IWineD3DBaseTextureImpl *tex_impl = (IWineD3DBaseTextureImpl *) baseTexture;
1083 TRACE("Passing to container\n");
1084 tex_impl->baseTexture.internal_preload(baseTexture, srgb);
1085 IWineD3DBaseTexture_Release(baseTexture);
1087 struct wined3d_context *context = NULL;
1089 TRACE("(%p) : About to load surface\n", This);
1091 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
1093 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
1094 || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
1096 if(palette9_changed(This)) {
1097 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
1098 /* TODO: This is not necessarily needed with hw palettized texture support */
1099 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
1100 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
1101 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
1105 IWineD3DSurface_LoadTexture(iface, srgb == SRGB_SRGB ? TRUE : FALSE);
1107 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
1108 /* Tell opengl to try and keep this texture in video ram (well mostly) */
1112 glPrioritizeTextures(1, &This->texture_name, &tmp);
1116 if (context) context_release(context);
1120 static void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
1121 surface_internal_preload(iface, SRGB_ANY);
1124 /* Context activation is done by the caller. */
1125 static void surface_remove_pbo(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info)
1127 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1128 This->resource.allocatedMemory =
1129 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1132 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1133 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
1134 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
1135 checkGLcall("glGetBufferSubDataARB");
1136 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
1137 checkGLcall("glDeleteBuffersARB");
1141 This->Flags &= ~SFLAG_PBO;
1144 BOOL surface_init_sysmem(IWineD3DSurface *iface)
1146 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
1148 if(!This->resource.allocatedMemory)
1150 This->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->resource.size + RESOURCE_ALIGNMENT);
1151 if(!This->resource.heapMemory)
1153 ERR("Out of memory\n");
1156 This->resource.allocatedMemory =
1157 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1161 memset(This->resource.allocatedMemory, 0, This->resource.size);
1164 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
1168 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
1169 IWineD3DBaseTexture *texture = NULL;
1170 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
1171 IWineD3DDeviceImpl *device = This->resource.device;
1172 const struct wined3d_gl_info *gl_info;
1173 renderbuffer_entry_t *entry, *entry2;
1174 struct wined3d_context *context;
1176 TRACE("(%p)\n", iface);
1178 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
1179 /* Default pool resources are supposed to be destroyed before Reset is called.
1180 * Implicit resources stay however. So this means we have an implicit render target
1181 * or depth stencil. The content may be destroyed, but we still have to tear down
1182 * opengl resources, so we cannot leave early.
1184 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1185 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1186 * or the depth stencil into an FBO the texture or render buffer will be removed
1187 * and all flags get lost
1189 surface_init_sysmem(iface);
1191 /* Load the surface into system memory */
1192 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
1193 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
1195 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
1196 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSRGBTEX, FALSE);
1197 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1199 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
1200 gl_info = context->gl_info;
1202 /* Destroy PBOs, but load them into real sysmem before */
1203 if (This->Flags & SFLAG_PBO)
1204 surface_remove_pbo(This, gl_info);
1206 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1207 * all application-created targets the application has to release the surface
1208 * before calling _Reset
1210 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
1212 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1214 list_remove(&entry->entry);
1215 HeapFree(GetProcessHeap(), 0, entry);
1217 list_init(&This->renderbuffers);
1218 This->current_renderbuffer = NULL;
1220 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
1223 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
1226 glDeleteTextures(1, &This->texture_name);
1227 This->texture_name = 0;
1228 glDeleteTextures(1, &This->texture_name_srgb);
1229 This->texture_name_srgb = 0;
1232 IWineD3DBaseTexture_Release(texture);
1235 context_release(context);
1238 /* ******************************************************
1239 IWineD3DSurface IWineD3DSurface parts follow
1240 ****************************************************** */
1242 /* Read the framebuffer back into the surface */
1243 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, const RECT *rect, void *dest, UINT pitch)
1245 IWineD3DDeviceImpl *myDevice = This->resource.device;
1246 const struct wined3d_gl_info *gl_info;
1247 struct wined3d_context *context;
1251 BYTE *row, *top, *bottom;
1255 BOOL srcIsUpsideDown;
1260 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1261 static BOOL warned = FALSE;
1263 ERR("The application tries to lock the render target, but render target locking is disabled\n");
1269 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
1270 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
1271 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
1272 * context->last_was_blit set on the unlock.
1274 context = context_acquire(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1275 gl_info = context->gl_info;
1279 /* Select the correct read buffer, and give some debug output.
1280 * There is no need to keep track of the current read buffer or reset it, every part of the code
1281 * that reads sets the read buffer as desired.
1283 if (surface_is_offscreen((IWineD3DSurface *) This))
1285 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1286 * Read from the back buffer
1288 TRACE("Locking offscreen render target\n");
1289 glReadBuffer(myDevice->offscreenBuffer);
1290 srcIsUpsideDown = TRUE;
1294 /* Onscreen surfaces are always part of a swapchain */
1295 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This);
1296 TRACE("Locking %#x buffer\n", buffer);
1297 glReadBuffer(buffer);
1298 checkGLcall("glReadBuffer");
1299 srcIsUpsideDown = FALSE;
1302 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
1304 local_rect.left = 0;
1306 local_rect.right = This->currentDesc.Width;
1307 local_rect.bottom = This->currentDesc.Height;
1311 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
1313 switch(This->resource.format_desc->format)
1315 case WINED3DFMT_P8_UINT:
1317 if(primary_render_target_is_p8(myDevice)) {
1318 /* In case of P8 render targets the index is stored in the alpha component */
1320 type = GL_UNSIGNED_BYTE;
1322 bpp = This->resource.format_desc->byte_count;
1324 /* GL can't return palettized data, so read ARGB pixels into a
1325 * separate block of memory and convert them into palettized format
1326 * in software. Slow, but if the app means to use palettized render
1327 * targets and locks it...
1329 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
1330 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
1331 * for the color channels when palettizing the colors.
1334 type = GL_UNSIGNED_BYTE;
1336 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
1338 ERR("Out of memory\n");
1342 bpp = This->resource.format_desc->byte_count * 3;
1349 fmt = This->resource.format_desc->glFormat;
1350 type = This->resource.format_desc->glType;
1351 bpp = This->resource.format_desc->byte_count;
1354 if(This->Flags & SFLAG_PBO) {
1355 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
1356 checkGLcall("glBindBufferARB");
1358 ERR("mem not null for pbo -- unexpected\n");
1363 /* Save old pixel store pack state */
1364 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
1365 checkGLcall("glGetIntegerv");
1366 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
1367 checkGLcall("glGetIntegerv");
1368 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
1369 checkGLcall("glGetIntegerv");
1371 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1372 glPixelStorei(GL_PACK_ROW_LENGTH, This->currentDesc.Width);
1373 checkGLcall("glPixelStorei");
1374 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
1375 checkGLcall("glPixelStorei");
1376 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
1377 checkGLcall("glPixelStorei");
1379 glReadPixels(local_rect.left, (!srcIsUpsideDown) ? (This->currentDesc.Height - local_rect.bottom) : local_rect.top ,
1380 local_rect.right - local_rect.left,
1381 local_rect.bottom - local_rect.top,
1383 checkGLcall("glReadPixels");
1385 /* Reset previous pixel store pack state */
1386 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
1387 checkGLcall("glPixelStorei");
1388 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
1389 checkGLcall("glPixelStorei");
1390 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
1391 checkGLcall("glPixelStorei");
1393 if(This->Flags & SFLAG_PBO) {
1394 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1395 checkGLcall("glBindBufferARB");
1397 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
1398 * to get a pointer to it and perform the flipping in software. This is a lot
1399 * faster than calling glReadPixels for each line. In case we want more speed
1400 * we should rerender it flipped in a FBO and read the data back from the FBO. */
1401 if(!srcIsUpsideDown) {
1402 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1403 checkGLcall("glBindBufferARB");
1405 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1406 checkGLcall("glMapBufferARB");
1410 /* TODO: Merge this with the palettization loop below for P8 targets */
1411 if(!srcIsUpsideDown) {
1413 /* glReadPixels returns the image upside down, and there is no way to prevent this.
1414 Flip the lines in software */
1415 len = (local_rect.right - local_rect.left) * bpp;
1416 off = local_rect.left * bpp;
1418 row = HeapAlloc(GetProcessHeap(), 0, len);
1420 ERR("Out of memory\n");
1421 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT) HeapFree(GetProcessHeap(), 0, mem);
1426 top = mem + pitch * local_rect.top;
1427 bottom = mem + pitch * (local_rect.bottom - 1);
1428 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
1429 memcpy(row, top + off, len);
1430 memcpy(top + off, bottom + off, len);
1431 memcpy(bottom + off, row, len);
1435 HeapFree(GetProcessHeap(), 0, row);
1437 /* Unmap the temp PBO buffer */
1438 if(This->Flags & SFLAG_PBO) {
1439 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1440 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1445 context_release(context);
1447 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
1448 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
1449 * the same color but we have no choice.
1450 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
1452 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(myDevice))
1454 const PALETTEENTRY *pal = NULL;
1455 DWORD width = pitch / 3;
1459 pal = This->palette->palents;
1461 ERR("Palette is missing, cannot perform inverse palette lookup\n");
1462 HeapFree(GetProcessHeap(), 0, mem);
1466 for(y = local_rect.top; y < local_rect.bottom; y++) {
1467 for(x = local_rect.left; x < local_rect.right; x++) {
1468 /* start lines pixels */
1469 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
1470 const BYTE *green = blue + 1;
1471 const BYTE *red = green + 1;
1473 for(c = 0; c < 256; c++) {
1474 if(*red == pal[c].peRed &&
1475 *green == pal[c].peGreen &&
1476 *blue == pal[c].peBlue)
1478 *((BYTE *) dest + y * width + x) = c;
1484 HeapFree(GetProcessHeap(), 0, mem);
1488 /* Read the framebuffer contents into a texture */
1489 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This, BOOL srgb)
1491 IWineD3DDeviceImpl *device = This->resource.device;
1492 const struct wined3d_gl_info *gl_info;
1493 struct wined3d_context *context;
1495 BOOL alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
1497 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
1498 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
1499 * states in the stateblock, and no driver was found yet that had bugs in that regard.
1501 context = context_acquire(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
1502 gl_info = context->gl_info;
1504 surface_bind_and_dirtify(This, srgb);
1507 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1510 /* Select the correct read buffer, and give some debug output.
1511 * There is no need to keep track of the current read buffer or reset it, every part of the code
1512 * that reads sets the read buffer as desired.
1514 if (!surface_is_offscreen((IWineD3DSurface *)This))
1516 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This);
1517 TRACE("Locking %#x buffer\n", buffer);
1520 glReadBuffer(buffer);
1521 checkGLcall("glReadBuffer");
1526 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1527 * Read from the back buffer
1529 TRACE("Locking offscreen render target\n");
1531 glReadBuffer(device->offscreenBuffer);
1532 checkGLcall("glReadBuffer");
1536 if (!(This->Flags & alloc_flag))
1538 surface_allocate_surface(This, gl_info, This->resource.format_desc, srgb);
1539 This->Flags |= alloc_flag;
1543 /* If !SrcIsUpsideDown we should flip the surface.
1544 * This can be done using glCopyTexSubImage2D but this
1545 * is VERY slow, so don't do that. We should prevent
1546 * this code from getting called in such cases or perhaps
1547 * we can use FBOs */
1549 glCopyTexSubImage2D(This->texture_target, This->texture_level,
1550 0, 0, 0, 0, This->currentDesc.Width, This->currentDesc.Height);
1551 checkGLcall("glCopyTexSubImage2D");
1553 glReadBuffer(prevRead);
1554 checkGLcall("glReadBuffer");
1558 context_release(context);
1560 TRACE("Updated target %d\n", This->texture_target);
1563 /* Context activation is done by the caller. */
1564 void surface_prepare_texture(IWineD3DSurfaceImpl *surface, const struct wined3d_gl_info *gl_info, BOOL srgb)
1566 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
1567 CONVERT_TYPES convert;
1568 struct wined3d_format_desc desc;
1570 if (surface->Flags & alloc_flag) return;
1572 d3dfmt_get_conv(surface, TRUE, TRUE, &desc, &convert);
1573 if(convert != NO_CONVERSION) surface->Flags |= SFLAG_CONVERTED;
1574 else surface->Flags &= ~SFLAG_CONVERTED;
1576 surface_bind_and_dirtify(surface, srgb);
1577 surface_allocate_surface(surface, gl_info, &desc, srgb);
1578 surface->Flags |= alloc_flag;
1581 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This)
1583 IWineD3DDeviceImpl *device = This->resource.device;
1584 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1586 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
1587 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
1590 if(!(This->Flags & SFLAG_DYNLOCK)) {
1592 /* MAXLOCKCOUNT is defined in wined3d_private.h */
1593 if(This->lockCount > MAXLOCKCOUNT) {
1594 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
1595 This->Flags |= SFLAG_DYNLOCK;
1599 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
1600 * Also don't create a PBO for systemmem surfaces.
1602 if (gl_info->supported[ARB_PIXEL_BUFFER_OBJECT] && (This->Flags & SFLAG_DYNLOCK)
1603 && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2))
1604 && (This->resource.pool != WINED3DPOOL_SYSTEMMEM))
1607 struct wined3d_context *context;
1609 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
1612 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
1613 error = glGetError();
1614 if(This->pbo == 0 || error != GL_NO_ERROR) {
1615 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
1618 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
1620 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1621 checkGLcall("glBindBufferARB");
1623 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1624 checkGLcall("glBufferDataARB");
1626 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1627 checkGLcall("glBindBufferARB");
1629 /* We don't need the system memory anymore and we can't even use it for PBOs */
1630 if(!(This->Flags & SFLAG_CLIENT)) {
1631 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1632 This->resource.heapMemory = NULL;
1634 This->resource.allocatedMemory = NULL;
1635 This->Flags |= SFLAG_PBO;
1637 context_release(context);
1639 else if (!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO))
1641 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1644 if(!This->resource.heapMemory) {
1645 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1647 This->resource.allocatedMemory =
1648 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1649 if(This->Flags & SFLAG_INSYSMEM) {
1650 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1655 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1656 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1657 IWineD3DDeviceImpl *myDevice = This->resource.device;
1658 const RECT *pass_rect = pRect;
1660 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1662 /* This is also done in the base class, but we have to verify this before loading any data from
1663 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1664 * may interfere, and all other bad things may happen
1666 if (This->Flags & SFLAG_LOCKED) {
1667 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1668 return WINED3DERR_INVALIDCALL;
1670 This->Flags |= SFLAG_LOCKED;
1672 if (!(This->Flags & SFLAG_LOCKABLE))
1674 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1677 if (Flags & WINED3DLOCK_DISCARD) {
1678 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1679 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1680 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1681 This->Flags |= SFLAG_INSYSMEM;
1685 if (This->Flags & SFLAG_INSYSMEM) {
1686 TRACE("Local copy is up to date, not downloading data\n");
1687 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1691 /* IWineD3DSurface_LoadLocation() does not check if the rectangle specifies
1692 * the full surface. Most callers don't need that, so do it here. */
1693 if (pRect && pRect->top == 0 && pRect->left == 0
1694 && pRect->right == This->currentDesc.Width
1695 && pRect->bottom == This->currentDesc.Height)
1700 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
1701 && ((This->Flags & SFLAG_SWAPCHAIN) || iface == myDevice->render_targets[0])))
1703 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1707 if (This->Flags & SFLAG_PBO)
1709 const struct wined3d_gl_info *gl_info;
1710 struct wined3d_context *context;
1712 context = context_acquire(myDevice, NULL, CTXUSAGE_RESOURCELOAD);
1713 gl_info = context->gl_info;
1716 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1717 checkGLcall("glBindBufferARB");
1719 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1720 if(This->resource.allocatedMemory) {
1721 ERR("The surface already has PBO memory allocated!\n");
1724 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1725 checkGLcall("glMapBufferARB");
1727 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1728 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1729 checkGLcall("glBindBufferARB");
1732 context_release(context);
1735 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1738 IWineD3DBaseTexture *pBaseTexture;
1741 * as seen in msdn docs
1743 surface_add_dirty_rect(iface, pRect);
1745 /** Dirtify Container if needed */
1746 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1747 TRACE("Making container dirty\n");
1748 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1749 IWineD3DBaseTexture_Release(pBaseTexture);
1751 TRACE("Surface is standalone, no need to dirty the container\n");
1755 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1758 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1760 GLint prev_rasterpos[4];
1761 GLint skipBytes = 0;
1762 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1763 IWineD3DDeviceImpl *myDevice = This->resource.device;
1764 const struct wined3d_gl_info *gl_info;
1765 struct wined3d_context *context;
1767 /* Activate the correct context for the render target */
1768 context = context_acquire(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1769 gl_info = context->gl_info;
1773 if (!surface_is_offscreen((IWineD3DSurface *)This))
1775 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This);
1776 TRACE("Unlocking %#x buffer.\n", buffer);
1777 context_set_draw_buffer(context, buffer);
1781 /* Primary offscreen render target */
1782 TRACE("Offscreen render target.\n");
1783 context_set_draw_buffer(context, myDevice->offscreenBuffer);
1786 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1787 checkGLcall("glGetIntegerv");
1788 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1789 checkGLcall("glGetIntegerv");
1790 glPixelZoom(1.0f, -1.0f);
1791 checkGLcall("glPixelZoom");
1793 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1794 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1795 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1797 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1798 checkGLcall("glRasterPos3i");
1800 /* Some drivers(radeon dri, others?) don't like exceptions during
1801 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1802 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1803 * catch to put the dib section in InSync mode, which leads to a crash
1804 * and a blocked x server on my radeon card.
1806 * The following lines read the dib section so it is put in InSync mode
1807 * before glDrawPixels is called and the crash is prevented. There won't
1808 * be any interfering gdi accesses, because UnlockRect is called from
1809 * ReleaseDC, and the app won't use the dc any more afterwards.
1811 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1813 read = This->resource.allocatedMemory[0];
1816 if(This->Flags & SFLAG_PBO) {
1817 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1818 checkGLcall("glBindBufferARB");
1821 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1822 if(This->Flags & SFLAG_LOCKED) {
1823 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1824 (This->lockedRect.bottom - This->lockedRect.top)-1,
1826 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1827 checkGLcall("glDrawPixels");
1829 glDrawPixels(This->currentDesc.Width,
1830 This->currentDesc.Height,
1832 checkGLcall("glDrawPixels");
1835 if(This->Flags & SFLAG_PBO) {
1836 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1837 checkGLcall("glBindBufferARB");
1840 glPixelZoom(1.0f, 1.0f);
1841 checkGLcall("glPixelZoom");
1843 glRasterPos3iv(&prev_rasterpos[0]);
1844 checkGLcall("glRasterPos3iv");
1846 /* Reset to previous pack row length */
1847 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1848 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH)");
1851 context_release(context);
1854 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1855 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1856 IWineD3DDeviceImpl *myDevice = This->resource.device;
1859 if (!(This->Flags & SFLAG_LOCKED)) {
1860 WARN("trying to Unlock an unlocked surf@%p\n", This);
1861 return WINEDDERR_NOTLOCKED;
1864 if (This->Flags & SFLAG_PBO)
1866 const struct wined3d_gl_info *gl_info;
1867 struct wined3d_context *context;
1869 TRACE("Freeing PBO memory\n");
1871 context = context_acquire(myDevice, NULL, CTXUSAGE_RESOURCELOAD);
1872 gl_info = context->gl_info;
1875 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1876 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1877 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1878 checkGLcall("glUnmapBufferARB");
1880 context_release(context);
1882 This->resource.allocatedMemory = NULL;
1885 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1887 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1888 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1892 if ((This->Flags & SFLAG_SWAPCHAIN) || (myDevice->render_targets && iface == myDevice->render_targets[0]))
1894 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1895 static BOOL warned = FALSE;
1897 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1903 if(This->dirtyRect.left == 0 &&
1904 This->dirtyRect.top == 0 &&
1905 This->dirtyRect.right == This->currentDesc.Width &&
1906 This->dirtyRect.bottom == This->currentDesc.Height) {
1909 /* TODO: Proper partial rectangle tracking */
1910 fullsurface = FALSE;
1911 This->Flags |= SFLAG_INSYSMEM;
1914 switch(wined3d_settings.rendertargetlock_mode) {
1916 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1920 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1925 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1926 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1927 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1928 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1929 * not fully up to date because only a subrectangle was read in LockRect.
1931 This->Flags &= ~SFLAG_INSYSMEM;
1932 This->Flags |= SFLAG_INDRAWABLE;
1935 This->dirtyRect.left = This->currentDesc.Width;
1936 This->dirtyRect.top = This->currentDesc.Height;
1937 This->dirtyRect.right = 0;
1938 This->dirtyRect.bottom = 0;
1939 } else if(iface == myDevice->stencilBufferTarget) {
1940 FIXME("Depth Stencil buffer locking is not implemented\n");
1942 /* The rest should be a normal texture */
1943 IWineD3DBaseTextureImpl *impl;
1944 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1945 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1946 * states need resetting
1948 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1949 if(impl->baseTexture.bindCount) {
1950 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1952 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1957 This->Flags &= ~SFLAG_LOCKED;
1958 memset(&This->lockedRect, 0, sizeof(RECT));
1960 /* Overlays have to be redrawn manually after changes with the GL implementation */
1961 if(This->overlay_dest) {
1962 IWineD3DSurface_DrawOverlay(iface);
1967 static void surface_release_client_storage(IWineD3DSurface *iface)
1969 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
1970 struct wined3d_context *context;
1972 context = context_acquire(This->resource.device, NULL, CTXUSAGE_RESOURCELOAD);
1975 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
1976 if(This->texture_name)
1978 surface_bind_and_dirtify(This, FALSE);
1979 glTexImage2D(This->texture_target, This->texture_level,
1980 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
1982 if(This->texture_name_srgb)
1984 surface_bind_and_dirtify(This, TRUE);
1985 glTexImage2D(This->texture_target, This->texture_level,
1986 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
1988 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
1991 context_release(context);
1993 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSRGBTEX, FALSE);
1994 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
1995 surface_force_reload(iface);
1998 static HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC)
2000 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2001 WINED3DLOCKED_RECT lock;
2005 TRACE("(%p)->(%p)\n",This,pHDC);
2007 if(This->Flags & SFLAG_USERPTR) {
2008 ERR("Not supported on surfaces with an application-provided surfaces\n");
2009 return WINEDDERR_NODC;
2012 /* Give more detailed info for ddraw */
2013 if (This->Flags & SFLAG_DCINUSE)
2014 return WINEDDERR_DCALREADYCREATED;
2016 /* Can't GetDC if the surface is locked */
2017 if (This->Flags & SFLAG_LOCKED)
2018 return WINED3DERR_INVALIDCALL;
2020 memset(&lock, 0, sizeof(lock)); /* To be sure */
2022 /* Create a DIB section if there isn't a hdc yet */
2024 if(This->Flags & SFLAG_CLIENT) {
2025 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2026 surface_release_client_storage(iface);
2028 hr = IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
2029 if(FAILED(hr)) return WINED3DERR_INVALIDCALL;
2031 /* Use the dib section from now on if we are not using a PBO */
2032 if(!(This->Flags & SFLAG_PBO))
2033 This->resource.allocatedMemory = This->dib.bitmap_data;
2036 /* Lock the surface */
2037 hr = IWineD3DSurface_LockRect(iface,
2042 if(This->Flags & SFLAG_PBO) {
2043 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
2044 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
2048 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
2049 /* keep the dib section */
2053 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
2054 || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
2056 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
2057 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
2059 const PALETTEENTRY *pal = NULL;
2062 pal = This->palette->palents;
2064 IWineD3DSurfaceImpl *dds_primary;
2065 IWineD3DSwapChainImpl *swapchain;
2066 swapchain = (IWineD3DSwapChainImpl *)This->resource.device->swapchains[0];
2067 dds_primary = (IWineD3DSurfaceImpl *)swapchain->frontBuffer;
2068 if (dds_primary && dds_primary->palette)
2069 pal = dds_primary->palette->palents;
2073 for (n=0; n<256; n++) {
2074 col[n].rgbRed = pal[n].peRed;
2075 col[n].rgbGreen = pal[n].peGreen;
2076 col[n].rgbBlue = pal[n].peBlue;
2077 col[n].rgbReserved = 0;
2079 SetDIBColorTable(This->hDC, 0, 256, col);
2084 TRACE("returning %p\n",*pHDC);
2085 This->Flags |= SFLAG_DCINUSE;
2090 static HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC)
2092 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2094 TRACE("(%p)->(%p)\n",This,hDC);
2096 if (!(This->Flags & SFLAG_DCINUSE))
2097 return WINEDDERR_NODC;
2099 if (This->hDC !=hDC) {
2100 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
2101 return WINEDDERR_NODC;
2104 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
2105 /* Copy the contents of the DIB over to the PBO */
2106 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
2109 /* we locked first, so unlock now */
2110 IWineD3DSurface_UnlockRect(iface);
2112 This->Flags &= ~SFLAG_DCINUSE;
2117 /* ******************************************************
2118 IWineD3DSurface Internal (No mapping to directx api) parts follow
2119 ****************************************************** */
2121 HRESULT d3dfmt_get_conv(IWineD3DSurfaceImpl *This, BOOL need_alpha_ck, BOOL use_texturing, struct wined3d_format_desc *desc, CONVERT_TYPES *convert)
2123 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
2124 IWineD3DDeviceImpl *device = This->resource.device;
2125 BOOL blit_supported = FALSE;
2126 RECT rect = {0, 0, This->pow2Width, This->pow2Height};
2128 /* Copy the default values from the surface. Below we might perform fixups */
2129 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
2130 *desc = *This->resource.format_desc;
2131 *convert = NO_CONVERSION;
2133 /* Ok, now look if we have to do any conversion */
2134 switch(This->resource.format_desc->format)
2136 case WINED3DFMT_P8_UINT:
2141 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, BLIT_OP_BLIT,
2142 &rect, This->resource.usage, This->resource.pool,
2143 This->resource.format_desc, &rect, This->resource.usage,
2144 This->resource.pool, This->resource.format_desc);
2146 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
2147 * texturing. Further also use conversion in case of color keying.
2148 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
2149 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
2150 * conflicts with this.
2152 if (!((blit_supported && device->render_targets && This == (IWineD3DSurfaceImpl*)device->render_targets[0]))
2153 || colorkey_active || !use_texturing)
2155 desc->glFormat = GL_RGBA;
2156 desc->glInternal = GL_RGBA;
2157 desc->glType = GL_UNSIGNED_BYTE;
2158 desc->conv_byte_count = 4;
2159 if(colorkey_active) {
2160 *convert = CONVERT_PALETTED_CK;
2162 *convert = CONVERT_PALETTED;
2167 case WINED3DFMT_B2G3R3_UNORM:
2168 /* **********************
2169 GL_UNSIGNED_BYTE_3_3_2
2170 ********************** */
2171 if (colorkey_active) {
2172 /* This texture format will never be used.. So do not care about color keying
2173 up until the point in time it will be needed :-) */
2174 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
2178 case WINED3DFMT_B5G6R5_UNORM:
2179 if (colorkey_active) {
2180 *convert = CONVERT_CK_565;
2181 desc->glFormat = GL_RGBA;
2182 desc->glInternal = GL_RGB5_A1;
2183 desc->glType = GL_UNSIGNED_SHORT_5_5_5_1;
2184 desc->conv_byte_count = 2;
2188 case WINED3DFMT_B5G5R5X1_UNORM:
2189 if (colorkey_active) {
2190 *convert = CONVERT_CK_5551;
2191 desc->glFormat = GL_BGRA;
2192 desc->glInternal = GL_RGB5_A1;
2193 desc->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
2194 desc->conv_byte_count = 2;
2198 case WINED3DFMT_B8G8R8_UNORM:
2199 if (colorkey_active) {
2200 *convert = CONVERT_CK_RGB24;
2201 desc->glFormat = GL_RGBA;
2202 desc->glInternal = GL_RGBA8;
2203 desc->glType = GL_UNSIGNED_INT_8_8_8_8;
2204 desc->conv_byte_count = 4;
2208 case WINED3DFMT_B8G8R8X8_UNORM:
2209 if (colorkey_active) {
2210 *convert = CONVERT_RGB32_888;
2211 desc->glFormat = GL_RGBA;
2212 desc->glInternal = GL_RGBA8;
2213 desc->glType = GL_UNSIGNED_INT_8_8_8_8;
2214 desc->conv_byte_count = 4;
2225 void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey)
2227 IWineD3DDeviceImpl *device = This->resource.device;
2228 IWineD3DPaletteImpl *pal = This->palette;
2229 BOOL index_in_alpha = FALSE;
2232 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2233 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2234 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2235 * duplicate entries. Store the color key in the unused alpha component to speed the
2236 * download up and to make conversion unneeded. */
2237 index_in_alpha = primary_render_target_is_p8(device);
2241 UINT dxVersion = ((IWineD3DImpl *)device->wined3d)->dxVersion;
2243 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2246 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2249 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
2250 * there's no palette at this time. */
2251 for (i = 0; i < 256; i++) table[i][3] = i;
2256 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2257 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2258 * capability flag is present (wine does advertise this capability) */
2259 for (i = 0; i < 256; ++i)
2261 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2262 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2263 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2264 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2270 TRACE("Using surface palette %p\n", pal);
2271 /* Get the surface's palette */
2272 for (i = 0; i < 256; ++i)
2274 table[i][0] = pal->palents[i].peRed;
2275 table[i][1] = pal->palents[i].peGreen;
2276 table[i][2] = pal->palents[i].peBlue;
2278 /* When index_in_alpha is set the palette index is stored in the
2279 * alpha component. In case of a readback we can then read
2280 * GL_ALPHA. Color keying is handled in BltOverride using a
2281 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
2282 * color key itself is passed to glAlphaFunc in other cases the
2283 * alpha component of pixels that should be masked away is set to 0. */
2288 else if (colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue)
2289 && (i <= This->SrcBltCKey.dwColorSpaceHighValue))
2293 else if(pal->Flags & WINEDDPCAPS_ALPHA)
2295 table[i][3] = pal->palents[i].peFlags;
2305 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
2306 UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This)
2310 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
2315 memcpy(dst, src, pitch * height);
2318 case CONVERT_PALETTED:
2319 case CONVERT_PALETTED_CK:
2321 IWineD3DPaletteImpl* pal = This->palette;
2326 /* TODO: If we are a sublevel, try to get the palette from level 0 */
2329 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2331 for (y = 0; y < height; y++)
2333 source = src + pitch * y;
2334 dest = dst + outpitch * y;
2335 /* This is an 1 bpp format, using the width here is fine */
2336 for (x = 0; x < width; x++) {
2337 BYTE color = *source++;
2338 *dest++ = table[color][0];
2339 *dest++ = table[color][1];
2340 *dest++ = table[color][2];
2341 *dest++ = table[color][3];
2347 case CONVERT_CK_565:
2349 /* Converting the 565 format in 5551 packed to emulate color-keying.
2351 Note : in all these conversion, it would be best to average the averaging
2352 pixels to get the color of the pixel that will be color-keyed to
2353 prevent 'color bleeding'. This will be done later on if ever it is
2356 Note2: Nvidia documents say that their driver does not support alpha + color keying
2357 on the same surface and disables color keying in such a case
2363 TRACE("Color keyed 565\n");
2365 for (y = 0; y < height; y++) {
2366 Source = (const WORD *)(src + y * pitch);
2367 Dest = (WORD *) (dst + y * outpitch);
2368 for (x = 0; x < width; x++ ) {
2369 WORD color = *Source++;
2370 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
2371 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2372 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2381 case CONVERT_CK_5551:
2383 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
2387 TRACE("Color keyed 5551\n");
2388 for (y = 0; y < height; y++) {
2389 Source = (const WORD *)(src + y * pitch);
2390 Dest = (WORD *) (dst + y * outpitch);
2391 for (x = 0; x < width; x++ ) {
2392 WORD color = *Source++;
2394 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2395 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2399 *Dest &= ~(1 << 15);
2407 case CONVERT_CK_RGB24:
2409 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
2411 for (y = 0; y < height; y++)
2413 source = src + pitch * y;
2414 dest = dst + outpitch * y;
2415 for (x = 0; x < width; x++) {
2416 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
2417 DWORD dstcolor = color << 8;
2418 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2419 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2422 *(DWORD*)dest = dstcolor;
2430 case CONVERT_RGB32_888:
2432 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
2434 for (y = 0; y < height; y++)
2436 source = src + pitch * y;
2437 dest = dst + outpitch * y;
2438 for (x = 0; x < width; x++) {
2439 DWORD color = 0xffffff & *(const DWORD*)source;
2440 DWORD dstcolor = color << 8;
2441 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2442 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2445 *(DWORD*)dest = dstcolor;
2454 ERR("Unsupported conversion type %#x.\n", convert);
2459 BOOL palette9_changed(IWineD3DSurfaceImpl *This)
2461 IWineD3DDeviceImpl *device = This->resource.device;
2463 if (This->palette || (This->resource.format_desc->format != WINED3DFMT_P8_UINT
2464 && This->resource.format_desc->format != WINED3DFMT_P8_UINT_A8_UNORM))
2466 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2467 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2474 if (!memcmp(This->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256))
2479 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2481 memcpy(This->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2485 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2486 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2487 DWORD flag = srgb_mode ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2489 if (!(This->Flags & flag)) {
2490 TRACE("Reloading because surface is dirty\n");
2491 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2492 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2493 /* Reload: vice versa OR */
2494 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2495 /* Also reload: Color key is active AND the color key has changed */
2496 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2497 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2498 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2499 TRACE("Reloading because of color keying\n");
2500 /* To perform the color key conversion we need a sysmem copy of
2501 * the surface. Make sure we have it
2504 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2505 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2506 /* TODO: This is not necessarily needed with hw palettized texture support */
2507 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2509 TRACE("surface is already in texture\n");
2513 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2514 * These resources are not bound by device size or format restrictions. Because of this,
2515 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2516 * However, these resources can always be created, locked, and copied.
2518 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2520 FIXME("(%p) Operation not supported for scratch textures\n",This);
2521 return WINED3DERR_INVALIDCALL;
2524 IWineD3DSurface_LoadLocation(iface, flag, NULL /* no partial locking for textures yet */);
2528 static unsigned int gen = 0;
2531 if ((gen % 10) == 0) {
2532 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm",
2533 This, This->texture_target, This->texture_level, gen);
2534 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2537 * debugging crash code
2546 if (!(This->Flags & SFLAG_DONOTFREE)) {
2547 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2548 This->resource.allocatedMemory = NULL;
2549 This->resource.heapMemory = NULL;
2550 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2556 /* Context activation is done by the caller. */
2557 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface, BOOL srgb) {
2558 /* TODO: check for locks */
2559 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2560 IWineD3DBaseTexture *baseTexture = NULL;
2562 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2563 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2564 TRACE("Passing to container\n");
2565 IWineD3DBaseTexture_BindTexture(baseTexture, srgb);
2566 IWineD3DBaseTexture_Release(baseTexture);
2572 TRACE("(%p) : Binding surface\n", This);
2574 name = srgb ? &This->texture_name_srgb : &This->texture_name;
2578 if (!This->texture_level)
2581 glGenTextures(1, name);
2582 checkGLcall("glGenTextures");
2583 TRACE("Surface %p given name %d\n", This, *name);
2585 glBindTexture(This->texture_target, *name);
2586 checkGLcall("glBindTexture");
2587 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2588 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2589 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2590 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2591 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2592 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2593 glTexParameteri(This->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2594 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2595 glTexParameteri(This->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2596 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2598 /* This is where we should be reducing the amount of GLMemoryUsed */
2600 /* Mipmap surfaces should have a base texture container */
2601 ERR("Mipmap surface has a glTexture bound to it!\n");
2604 glBindTexture(This->texture_target, *name);
2605 checkGLcall("glBindTexture");
2613 static HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename)
2616 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2617 char *allocatedMemory;
2618 const char *textureRow;
2619 IWineD3DSwapChain *swapChain = NULL;
2620 int width, height, i, y;
2621 GLuint tmpTexture = 0;
2624 Textures may not be stored in ->allocatedgMemory and a GlTexture
2625 so we should lock the surface before saving a snapshot, or at least check that
2627 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2628 by calling GetTexImage and in compressed form by calling
2629 GetCompressedTexImageARB. Queried compressed images can be saved and
2630 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2631 texture images do not need to be processed by the GL and should
2632 significantly improve texture loading performance relative to uncompressed
2635 /* Setup the width and height to be the internal texture width and height. */
2636 width = This->pow2Width;
2637 height = This->pow2Height;
2638 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2639 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2641 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2642 /* if were not a real texture then read the back buffer into a real texture */
2643 /* we don't want to interfere with the back buffer so read the data into a temporary
2644 * texture and then save the data out of the temporary texture
2648 TRACE("(%p) Reading render target into texture\n", This);
2650 glGenTextures(1, &tmpTexture);
2651 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2653 glTexImage2D(GL_TEXTURE_2D,
2660 GL_UNSIGNED_INT_8_8_8_8_REV,
2663 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2664 checkGLcall("glGetIntegerv");
2665 glReadBuffer(swapChain ? GL_BACK : This->resource.device->offscreenBuffer);
2666 checkGLcall("glReadBuffer");
2667 glCopyTexImage2D(GL_TEXTURE_2D,
2676 checkGLcall("glCopyTexImage2D");
2677 glReadBuffer(prevRead);
2680 } else { /* bind the real texture, and make sure it up to date */
2681 surface_internal_preload(iface, SRGB_RGB);
2682 surface_bind_and_dirtify(This, FALSE);
2684 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2686 FIXME("Saving texture level %d width %d height %d\n", This->texture_level, width, height);
2687 glGetTexImage(GL_TEXTURE_2D, This->texture_level, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, allocatedMemory);
2688 checkGLcall("glGetTexImage");
2690 glBindTexture(GL_TEXTURE_2D, 0);
2691 glDeleteTextures(1, &tmpTexture);
2695 f = fopen(filename, "w+");
2697 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2698 return WINED3DERR_INVALIDCALL;
2700 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2701 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format_desc->format));
2716 fwrite(&width,2,1,f);
2718 fwrite(&height,2,1,f);
2723 /* 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 */
2725 textureRow = allocatedMemory + (width * (height - 1) *4);
2727 textureRow = allocatedMemory;
2728 for (y = 0 ; y < height; y++) {
2729 for (i = 0; i < width; i++) {
2730 color = *((const DWORD*)textureRow);
2731 fputc((color >> 16) & 0xFF, f); /* B */
2732 fputc((color >> 8) & 0xFF, f); /* G */
2733 fputc((color >> 0) & 0xFF, f); /* R */
2734 fputc((color >> 24) & 0xFF, f); /* A */
2737 /* take two rows of the pointer to the texture memory */
2739 (textureRow-= width << 3);
2742 TRACE("Closing file\n");
2746 IWineD3DSwapChain_Release(swapChain);
2748 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2752 static HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2753 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2756 TRACE("(%p) : Calling base function first\n", This);
2757 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2759 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2760 TRACE("(%p) : glFormat %d, glFormatInternal %d, glType %d\n", This, This->resource.format_desc->glFormat,
2761 This->resource.format_desc->glInternal, This->resource.format_desc->glType);
2766 static HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2767 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2769 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2770 WARN("Surface is locked or the HDC is in use\n");
2771 return WINED3DERR_INVALIDCALL;
2774 if(Mem && Mem != This->resource.allocatedMemory) {
2775 void *release = NULL;
2777 /* Do I have to copy the old surface content? */
2778 if(This->Flags & SFLAG_DIBSECTION) {
2779 /* Release the DC. No need to hold the critical section for the update
2780 * Thread because this thread runs only on front buffers, but this method
2781 * fails for render targets in the check above.
2783 SelectObject(This->hDC, This->dib.holdbitmap);
2784 DeleteDC(This->hDC);
2785 /* Release the DIB section */
2786 DeleteObject(This->dib.DIBsection);
2787 This->dib.bitmap_data = NULL;
2788 This->resource.allocatedMemory = NULL;
2790 This->Flags &= ~SFLAG_DIBSECTION;
2791 } else if(!(This->Flags & SFLAG_USERPTR)) {
2792 release = This->resource.heapMemory;
2793 This->resource.heapMemory = NULL;
2795 This->resource.allocatedMemory = Mem;
2796 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2798 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2799 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2801 /* For client textures opengl has to be notified */
2802 if(This->Flags & SFLAG_CLIENT) {
2803 surface_release_client_storage(iface);
2806 /* Now free the old memory if any */
2807 HeapFree(GetProcessHeap(), 0, release);
2808 } else if(This->Flags & SFLAG_USERPTR) {
2809 /* LockRect and GetDC will re-create the dib section and allocated memory */
2810 This->resource.allocatedMemory = NULL;
2811 /* HeapMemory should be NULL already */
2812 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2813 This->Flags &= ~SFLAG_USERPTR;
2815 if(This->Flags & SFLAG_CLIENT) {
2816 surface_release_client_storage(iface);
2822 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
2824 /* Flip the surface contents */
2829 front->hDC = back->hDC;
2833 /* Flip the DIBsection */
2836 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
2837 tmp = front->dib.DIBsection;
2838 front->dib.DIBsection = back->dib.DIBsection;
2839 back->dib.DIBsection = tmp;
2841 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
2842 else front->Flags &= ~SFLAG_DIBSECTION;
2843 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
2844 else back->Flags &= ~SFLAG_DIBSECTION;
2847 /* Flip the surface data */
2851 tmp = front->dib.bitmap_data;
2852 front->dib.bitmap_data = back->dib.bitmap_data;
2853 back->dib.bitmap_data = tmp;
2855 tmp = front->resource.allocatedMemory;
2856 front->resource.allocatedMemory = back->resource.allocatedMemory;
2857 back->resource.allocatedMemory = tmp;
2859 tmp = front->resource.heapMemory;
2860 front->resource.heapMemory = back->resource.heapMemory;
2861 back->resource.heapMemory = tmp;
2866 GLuint tmp_pbo = front->pbo;
2867 front->pbo = back->pbo;
2868 back->pbo = tmp_pbo;
2871 /* client_memory should not be different, but just in case */
2874 tmp = front->dib.client_memory;
2875 front->dib.client_memory = back->dib.client_memory;
2876 back->dib.client_memory = tmp;
2879 /* Flip the opengl texture */
2883 tmp = back->texture_name;
2884 back->texture_name = front->texture_name;
2885 front->texture_name = tmp;
2887 tmp = back->texture_name_srgb;
2888 back->texture_name_srgb = front->texture_name_srgb;
2889 front->texture_name_srgb = tmp;
2893 DWORD tmp_flags = back->Flags;
2894 back->Flags = front->Flags;
2895 front->Flags = tmp_flags;
2899 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2900 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2901 IWineD3DSwapChainImpl *swapchain = NULL;
2903 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2905 /* Flipping is only supported on RenderTargets and overlays*/
2906 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
2907 WARN("Tried to flip a non-render target, non-overlay surface\n");
2908 return WINEDDERR_NOTFLIPPABLE;
2911 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
2912 flip_surface(This, (IWineD3DSurfaceImpl *) override);
2914 /* Update the overlay if it is visible */
2915 if(This->overlay_dest) {
2916 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
2923 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2924 * FIXME("(%p) Target override is not supported by now\n", This);
2925 * Additionally, it isn't really possible to support triple-buffering
2926 * properly on opengl at all
2930 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2932 ERR("Flipped surface is not on a swapchain\n");
2933 return WINEDDERR_NOTFLIPPABLE;
2936 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2937 * and only d3d8 and d3d9 apps specify the presentation interval
2939 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2940 /* Most common case first to avoid wasting time on all the other cases */
2941 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2942 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2943 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2944 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2945 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2946 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2947 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2949 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2952 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2953 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *)swapchain,
2954 NULL, NULL, swapchain->win_handle, NULL, 0);
2955 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2959 /* Does a direct frame buffer -> texture copy. Stretching is done
2960 * with single pixel copy calls
2962 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
2963 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
2965 IWineD3DDeviceImpl *myDevice = This->resource.device;
2968 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
2969 struct wined3d_context *context;
2970 BOOL upsidedown = FALSE;
2971 RECT dst_rect = *dst_rect_in;
2973 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2974 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2976 if(dst_rect.top > dst_rect.bottom) {
2977 UINT tmp = dst_rect.bottom;
2978 dst_rect.bottom = dst_rect.top;
2983 context = context_acquire(myDevice, SrcSurface, CTXUSAGE_BLIT);
2984 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
2987 /* Bind the target texture */
2988 glBindTexture(This->texture_target, This->texture_name);
2989 checkGLcall("glBindTexture");
2990 if(surface_is_offscreen(SrcSurface)) {
2991 TRACE("Reading from an offscreen target\n");
2992 upsidedown = !upsidedown;
2993 glReadBuffer(myDevice->offscreenBuffer);
2997 glReadBuffer(surface_get_gl_buffer(SrcSurface));
2999 checkGLcall("glReadBuffer");
3001 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
3002 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
3004 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3006 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3008 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
3009 ERR("Texture filtering not supported in direct blit\n");
3012 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
3013 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3015 ERR("Texture filtering not supported in direct blit\n");
3019 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3020 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3022 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
3024 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3025 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
3026 src_rect->left, Src->currentDesc.Height - src_rect->bottom,
3027 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3029 UINT yoffset = Src->currentDesc.Height - src_rect->top + dst_rect.top - 1;
3030 /* I have to process this row by row to swap the image,
3031 * otherwise it would be upside down, so stretching in y direction
3032 * doesn't cost extra time
3034 * However, stretching in x direction can be avoided if not necessary
3036 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
3037 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3039 /* Well, that stuff works, but it's very slow.
3040 * find a better way instead
3044 for(col = dst_rect.left; col < dst_rect.right; col++) {
3045 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3046 dst_rect.left + col /* x offset */, row /* y offset */,
3047 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3050 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3051 dst_rect.left /* x offset */, row /* y offset */,
3052 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
3056 checkGLcall("glCopyTexSubImage2D");
3059 context_release(context);
3061 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3062 * path is never entered
3064 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3067 /* Uses the hardware to stretch and flip the image */
3068 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3069 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
3071 IWineD3DDeviceImpl *myDevice = This->resource.device;
3072 GLuint src, backup = 0;
3073 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3074 IWineD3DSwapChainImpl *src_swapchain = NULL;
3075 float left, right, top, bottom; /* Texture coordinates */
3076 UINT fbwidth = Src->currentDesc.Width;
3077 UINT fbheight = Src->currentDesc.Height;
3078 struct wined3d_context *context;
3079 GLenum drawBuffer = GL_BACK;
3080 GLenum texture_target;
3081 BOOL noBackBufferBackup;
3083 BOOL upsidedown = FALSE;
3084 RECT dst_rect = *dst_rect_in;
3086 TRACE("Using hwstretch blit\n");
3087 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3088 context = context_acquire(myDevice, SrcSurface, CTXUSAGE_BLIT);
3089 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3091 src_offscreen = surface_is_offscreen(SrcSurface);
3092 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3093 if (!noBackBufferBackup && !Src->texture_name)
3095 /* Get it a description */
3096 surface_internal_preload(SrcSurface, SRGB_RGB);
3100 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3101 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3103 if (context->aux_buffers >= 2)
3105 /* Got more than one aux buffer? Use the 2nd aux buffer */
3106 drawBuffer = GL_AUX1;
3108 else if ((!src_offscreen || myDevice->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3110 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3111 drawBuffer = GL_AUX0;
3114 if(noBackBufferBackup) {
3115 glGenTextures(1, &backup);
3116 checkGLcall("glGenTextures");
3117 glBindTexture(GL_TEXTURE_2D, backup);
3118 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3119 texture_target = GL_TEXTURE_2D;
3121 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3122 * we are reading from the back buffer, the backup can be used as source texture
3124 texture_target = Src->texture_target;
3125 glBindTexture(texture_target, Src->texture_name);
3126 checkGLcall("glBindTexture(texture_target, Src->texture_name)");
3127 glEnable(texture_target);
3128 checkGLcall("glEnable(texture_target)");
3130 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3131 Src->Flags &= ~SFLAG_INTEXTURE;
3134 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3135 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3137 if(dst_rect.top > dst_rect.bottom) {
3138 UINT tmp = dst_rect.bottom;
3139 dst_rect.bottom = dst_rect.top;
3146 TRACE("Reading from an offscreen target\n");
3147 upsidedown = !upsidedown;
3148 glReadBuffer(myDevice->offscreenBuffer);
3152 glReadBuffer(surface_get_gl_buffer(SrcSurface));
3155 /* TODO: Only back up the part that will be overwritten */
3156 glCopyTexSubImage2D(texture_target, 0,
3157 0, 0 /* read offsets */,
3162 checkGLcall("glCopyTexSubImage2D");
3164 /* No issue with overriding these - the sampler is dirty due to blit usage */
3165 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3166 wined3d_gl_mag_filter(magLookup, Filter));
3167 checkGLcall("glTexParameteri");
3168 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3169 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
3170 checkGLcall("glTexParameteri");
3172 IWineD3DSurface_GetContainer((IWineD3DSurface *)SrcSurface, &IID_IWineD3DSwapChain, (void **)&src_swapchain);
3173 if (src_swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *)src_swapchain);
3174 if (!src_swapchain || (IWineD3DSurface *) Src == src_swapchain->backBuffer[0]) {
3175 src = backup ? backup : Src->texture_name;
3177 glReadBuffer(GL_FRONT);
3178 checkGLcall("glReadBuffer(GL_FRONT)");
3180 glGenTextures(1, &src);
3181 checkGLcall("glGenTextures(1, &src)");
3182 glBindTexture(GL_TEXTURE_2D, src);
3183 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
3185 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3186 * out for power of 2 sizes
3188 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
3189 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3190 checkGLcall("glTexImage2D");
3191 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
3192 0, 0 /* read offsets */,
3197 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3198 checkGLcall("glTexParameteri");
3199 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3200 checkGLcall("glTexParameteri");
3202 glReadBuffer(GL_BACK);
3203 checkGLcall("glReadBuffer(GL_BACK)");
3205 if(texture_target != GL_TEXTURE_2D) {
3206 glDisable(texture_target);
3207 glEnable(GL_TEXTURE_2D);
3208 texture_target = GL_TEXTURE_2D;
3211 checkGLcall("glEnd and previous");
3213 left = src_rect->left;
3214 right = src_rect->right;
3217 top = Src->currentDesc.Height - src_rect->top;
3218 bottom = Src->currentDesc.Height - src_rect->bottom;
3220 top = Src->currentDesc.Height - src_rect->bottom;
3221 bottom = Src->currentDesc.Height - src_rect->top;
3224 if(Src->Flags & SFLAG_NORMCOORD) {
3225 left /= Src->pow2Width;
3226 right /= Src->pow2Width;
3227 top /= Src->pow2Height;
3228 bottom /= Src->pow2Height;
3231 /* draw the source texture stretched and upside down. The correct surface is bound already */
3232 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3233 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3235 context_set_draw_buffer(context, drawBuffer);
3236 glReadBuffer(drawBuffer);
3240 glTexCoord2f(left, bottom);
3241 glVertex2i(0, fbheight);
3244 glTexCoord2f(left, top);
3245 glVertex2i(0, fbheight - dst_rect.bottom - dst_rect.top);
3248 glTexCoord2f(right, top);
3249 glVertex2i(dst_rect.right - dst_rect.left, fbheight - dst_rect.bottom - dst_rect.top);
3252 glTexCoord2f(right, bottom);
3253 glVertex2i(dst_rect.right - dst_rect.left, fbheight);
3255 checkGLcall("glEnd and previous");
3257 if (texture_target != This->texture_target)
3259 glDisable(texture_target);
3260 glEnable(This->texture_target);
3261 texture_target = This->texture_target;
3264 /* Now read the stretched and upside down image into the destination texture */
3265 glBindTexture(texture_target, This->texture_name);
3266 checkGLcall("glBindTexture");
3267 glCopyTexSubImage2D(texture_target,
3269 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
3270 0, 0, /* We blitted the image to the origin */
3271 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3272 checkGLcall("glCopyTexSubImage2D");
3274 if(drawBuffer == GL_BACK) {
3275 /* Write the back buffer backup back */
3277 if(texture_target != GL_TEXTURE_2D) {
3278 glDisable(texture_target);
3279 glEnable(GL_TEXTURE_2D);
3280 texture_target = GL_TEXTURE_2D;
3282 glBindTexture(GL_TEXTURE_2D, backup);
3283 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3285 if (texture_target != Src->texture_target)
3287 glDisable(texture_target);
3288 glEnable(Src->texture_target);
3289 texture_target = Src->texture_target;
3291 glBindTexture(Src->texture_target, Src->texture_name);
3292 checkGLcall("glBindTexture(Src->texture_target, Src->texture_name)");
3297 glTexCoord2f(0.0f, (float)fbheight / (float)Src->pow2Height);
3301 glTexCoord2f(0.0f, 0.0f);
3302 glVertex2i(0, fbheight);
3305 glTexCoord2f((float)fbwidth / (float)Src->pow2Width, 0.0f);
3306 glVertex2i(fbwidth, Src->currentDesc.Height);
3309 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
3310 glVertex2i(fbwidth, 0);
3313 glDisable(texture_target);
3314 checkGLcall("glDisable(texture_target)");
3317 if (src != Src->texture_name && src != backup)
3319 glDeleteTextures(1, &src);
3320 checkGLcall("glDeleteTextures(1, &src)");
3323 glDeleteTextures(1, &backup);
3324 checkGLcall("glDeleteTextures(1, &backup)");
3329 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
3331 context_release(context);
3333 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3334 * path is never entered
3336 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3339 /* Until the blit_shader is ready, define some prototypes here. */
3340 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum blit_operation blit_op,
3341 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool,
3342 const struct wined3d_format_desc *src_format_desc,
3343 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool,
3344 const struct wined3d_format_desc *dst_format_desc);
3346 /* Not called from the VTable */
3347 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3348 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx,
3349 WINED3DTEXTUREFILTERTYPE Filter)
3351 IWineD3DDeviceImpl *myDevice = This->resource.device;
3352 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3353 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3354 RECT dst_rect, src_rect;
3356 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3358 /* Get the swapchain. One of the surfaces has to be a primary surface */
3359 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3360 WARN("Destination is in sysmem, rejecting gl blt\n");
3361 return WINED3DERR_INVALIDCALL;
3363 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3364 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3366 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3367 WARN("Src is in sysmem, rejecting gl blt\n");
3368 return WINED3DERR_INVALIDCALL;
3370 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3371 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3374 /* Early sort out of cases where no render target is used */
3375 if(!dstSwapchain && !srcSwapchain &&
3376 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3377 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3378 return WINED3DERR_INVALIDCALL;
3381 /* No destination color keying supported */
3382 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3383 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3384 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3385 return WINED3DERR_INVALIDCALL;
3388 surface_get_rect(This, DestRect, &dst_rect);
3389 if(Src) surface_get_rect(Src, SrcRect, &src_rect);
3391 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3392 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3393 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3394 /* Half-life does a Blt from the back buffer to the front buffer,
3395 * Full surface size, no flags... Use present instead
3397 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3400 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3403 TRACE("Looking if a Present can be done...\n");
3404 /* Source Rectangle must be full surface */
3405 if(src_rect.left != 0 || src_rect.top != 0 ||
3406 src_rect.right != Src->currentDesc.Width || src_rect.bottom != Src->currentDesc.Height) {
3407 TRACE("No, Source rectangle doesn't match\n");
3411 /* No stretching may occur */
3412 if(src_rect.right != dst_rect.right - dst_rect.left ||
3413 src_rect.bottom != dst_rect.bottom - dst_rect.top) {
3414 TRACE("No, stretching is done\n");
3418 /* Destination must be full surface or match the clipping rectangle */
3419 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3423 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3424 pos[0].x = dst_rect.left;
3425 pos[0].y = dst_rect.top;
3426 pos[1].x = dst_rect.right;
3427 pos[1].y = dst_rect.bottom;
3428 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3431 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3432 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3434 TRACE("No, dest rectangle doesn't match(clipper)\n");
3435 TRACE("Clip rect at %s\n", wine_dbgstr_rect(&cliprect));
3436 TRACE("Blt dest: %s\n", wine_dbgstr_rect(&dst_rect));
3442 if(dst_rect.left != 0 || dst_rect.top != 0 ||
3443 dst_rect.right != This->currentDesc.Width || dst_rect.bottom != This->currentDesc.Height) {
3444 TRACE("No, dest rectangle doesn't match(surface size)\n");
3451 /* These flags are unimportant for the flag check, remove them */
3452 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3453 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3455 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3456 * take very long, while a flip is fast.
3457 * This applies to Half-Life, which does such Blts every time it finished
3458 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3459 * menu. This is also used by all apps when they do windowed rendering
3461 * The problem is that flipping is not really the same as copying. After a
3462 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3463 * untouched. Therefore it's necessary to override the swap effect
3464 * and to set it back after the flip.
3466 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3470 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3471 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3473 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3474 IWineD3DSwapChain_Present((IWineD3DSwapChain *)dstSwapchain,
3475 NULL, NULL, dstSwapchain->win_handle, NULL, 0);
3477 dstSwapchain->presentParms.SwapEffect = orig_swap;
3484 TRACE("Unsupported blit between buffers on the same swapchain\n");
3485 return WINED3DERR_INVALIDCALL;
3486 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3487 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3488 return WINED3DERR_INVALIDCALL;
3489 } else if(dstSwapchain && srcSwapchain) {
3490 FIXME("Implement hardware blit between two different swapchains\n");
3491 return WINED3DERR_INVALIDCALL;
3492 } else if(dstSwapchain) {
3493 if(SrcSurface == myDevice->render_targets[0]) {
3494 TRACE("Blit from active render target to a swapchain\n");
3495 /* Handled with regular texture -> swapchain blit */
3497 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3498 FIXME("Implement blit from a swapchain to the active render target\n");
3499 return WINED3DERR_INVALIDCALL;
3502 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3503 /* Blit from render target to texture */
3506 /* P8 read back is not implemented */
3507 if (Src->resource.format_desc->format == WINED3DFMT_P8_UINT ||
3508 This->resource.format_desc->format == WINED3DFMT_P8_UINT)
3510 TRACE("P8 read back not supported by frame buffer to texture blit\n");
3511 return WINED3DERR_INVALIDCALL;
3514 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3515 TRACE("Color keying not supported by frame buffer to texture blit\n");
3516 return WINED3DERR_INVALIDCALL;
3517 /* Destination color key is checked above */
3520 if(dst_rect.right - dst_rect.left != src_rect.right - src_rect.left) {
3526 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3527 * flip the image nor scale it.
3529 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3530 * -> If the app wants a image width an unscaled width, copy it line per line
3531 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3532 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3533 * back buffer. This is slower than reading line per line, thus not used for flipping
3534 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3537 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3538 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3541 if (fbo_blit_supported(&myDevice->adapter->gl_info, BLIT_OP_BLIT,
3542 &src_rect, Src->resource.usage, Src->resource.pool, Src->resource.format_desc,
3543 &dst_rect, This->resource.usage, This->resource.pool, This->resource.format_desc))
3545 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &src_rect,
3546 (IWineD3DSurface *)This, &dst_rect, Filter);
3547 } else if((!stretchx) || dst_rect.right - dst_rect.left > Src->currentDesc.Width ||
3548 dst_rect.bottom - dst_rect.top > Src->currentDesc.Height) {
3549 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3550 fb_copy_to_texture_direct(This, SrcSurface, &src_rect, &dst_rect, Filter);
3552 TRACE("Using hardware stretching to flip / stretch the texture\n");
3553 fb_copy_to_texture_hwstretch(This, SrcSurface, &src_rect, &dst_rect, Filter);
3556 if(!(This->Flags & SFLAG_DONOTFREE)) {
3557 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3558 This->resource.allocatedMemory = NULL;
3559 This->resource.heapMemory = NULL;
3561 This->Flags &= ~SFLAG_INSYSMEM;
3566 /* Blit from offscreen surface to render target */
3567 DWORD oldCKeyFlags = Src->CKeyFlags;
3568 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3569 struct wined3d_context *context;
3571 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3573 if (!(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3574 && fbo_blit_supported(&myDevice->adapter->gl_info, BLIT_OP_BLIT,
3575 &src_rect, Src->resource.usage, Src->resource.pool, Src->resource.format_desc,
3576 &dst_rect, This->resource.usage, This->resource.pool, This->resource.format_desc))
3578 TRACE("Using stretch_rect_fbo\n");
3579 /* The source is always a texture, but never the currently active render target, and the texture
3580 * contents are never upside down
3582 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &src_rect,
3583 (IWineD3DSurface *)This, &dst_rect, Filter);
3587 if (!(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3588 && arbfp_blit.blit_supported(&myDevice->adapter->gl_info, BLIT_OP_BLIT,
3589 &src_rect, Src->resource.usage, Src->resource.pool, Src->resource.format_desc,
3590 &dst_rect, This->resource.usage, This->resource.pool, This->resource.format_desc))
3592 return arbfp_blit_surface(myDevice, Src, &src_rect, This, &dst_rect, BLIT_OP_BLIT, Filter);
3595 /* Color keying: Check if we have to do a color keyed blt,
3596 * and if not check if a color key is activated.
3598 * Just modify the color keying parameters in the surface and restore them afterwards
3599 * The surface keeps track of the color key last used to load the opengl surface.
3600 * PreLoad will catch the change to the flags and color key and reload if necessary.
3602 if(Flags & WINEDDBLT_KEYSRC) {
3603 /* Use color key from surface */
3604 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3605 /* Use color key from DDBltFx */
3606 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3607 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3609 /* Do not use color key */
3610 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3613 /* Now load the surface */
3614 surface_internal_preload((IWineD3DSurface *) Src, SRGB_RGB);
3616 /* Activate the destination context, set it up for blitting */
3617 context = context_acquire(myDevice, (IWineD3DSurface *)This, CTXUSAGE_BLIT);
3619 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3620 * while OpenGL coordinates are window relative.
3621 * Also beware of the origin difference(top left vs bottom left).
3622 * Also beware that the front buffer's surface size is screen width x screen height,
3623 * whereas the real gl drawable size is the size of the window.
3625 if (dstSwapchain && (IWineD3DSurface *)This == dstSwapchain->frontBuffer) {
3627 POINT offset = {0,0};
3629 ClientToScreen(context->win_handle, &offset);
3630 GetClientRect(context->win_handle, &windowsize);
3631 h = windowsize.bottom - windowsize.top;
3632 dst_rect.left -= offset.x; dst_rect.right -=offset.x;
3633 dst_rect.top -= offset.y; dst_rect.bottom -=offset.y;
3634 dst_rect.top += This->currentDesc.Height - h; dst_rect.bottom += This->currentDesc.Height - h;
3637 if (!myDevice->blitter->blit_supported(&myDevice->adapter->gl_info, BLIT_OP_BLIT,
3638 &src_rect, Src->resource.usage, Src->resource.pool, Src->resource.format_desc,
3639 &dst_rect, This->resource.usage, This->resource.pool, This->resource.format_desc))
3641 FIXME("Unsupported blit operation falling back to software\n");
3642 return WINED3DERR_INVALIDCALL;
3645 myDevice->blitter->set_shader((IWineD3DDevice *) myDevice, Src);
3649 /* This is for color keying */
3650 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3651 glEnable(GL_ALPHA_TEST);
3652 checkGLcall("glEnable(GL_ALPHA_TEST)");
3654 /* When the primary render target uses P8, the alpha component contains the palette index.
3655 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3656 * should be masked away have alpha set to 0. */
3657 if(primary_render_target_is_p8(myDevice))
3658 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
3660 glAlphaFunc(GL_NOTEQUAL, 0.0f);
3661 checkGLcall("glAlphaFunc");
3663 glDisable(GL_ALPHA_TEST);
3664 checkGLcall("glDisable(GL_ALPHA_TEST)");
3667 /* Draw a textured quad
3669 draw_textured_quad(Src, &src_rect, &dst_rect, Filter);
3671 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3672 glDisable(GL_ALPHA_TEST);
3673 checkGLcall("glDisable(GL_ALPHA_TEST)");
3676 /* Restore the color key parameters */
3677 Src->CKeyFlags = oldCKeyFlags;
3678 Src->SrcBltCKey = oldBltCKey;
3682 /* Leave the opengl state valid for blitting */
3683 myDevice->blitter->unset_shader((IWineD3DDevice *) myDevice);
3685 if (wined3d_settings.strict_draw_ordering || (dstSwapchain
3686 && ((IWineD3DSurface *)This == dstSwapchain->frontBuffer
3687 || dstSwapchain->num_contexts > 1)))
3688 wglFlush(); /* Flush to ensure ordering across contexts. */
3690 context_release(context);
3692 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3693 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3696 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3700 /* Source-Less Blit to render target */
3701 if (Flags & WINEDDBLT_COLORFILL) {
3704 TRACE("Colorfill\n");
3706 /* The color as given in the Blt function is in the format of the frame-buffer...
3707 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3709 if (!surface_convert_color_to_argb(This, DDBltFx->u5.dwFillColor, &color))
3711 /* The color conversion function already prints an error, so need to do it here */
3712 return WINED3DERR_INVALIDCALL;
3715 if (ffp_blit.blit_supported(&myDevice->adapter->gl_info, BLIT_OP_COLOR_FILL,
3717 &dst_rect, This->resource.usage, This->resource.pool, This->resource.format_desc))
3719 return ffp_blit.color_fill(myDevice, This, &dst_rect, color);
3721 else if (cpu_blit.blit_supported(&myDevice->adapter->gl_info, BLIT_OP_COLOR_FILL,
3723 &dst_rect, This->resource.usage, This->resource.pool, This->resource.format_desc))
3725 return cpu_blit.color_fill(myDevice, This, &dst_rect, color);
3727 return WINED3DERR_INVALIDCALL;
3731 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3732 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3733 return WINED3DERR_INVALIDCALL;
3736 static HRESULT IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3737 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx)
3739 IWineD3DDeviceImpl *myDevice = This->resource.device;
3742 if (Flags & WINEDDBLT_DEPTHFILL) {
3743 switch(This->resource.format_desc->format)
3745 case WINED3DFMT_D16_UNORM:
3746 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3748 case WINED3DFMT_S1_UINT_D15_UNORM:
3749 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3751 case WINED3DFMT_D24_UNORM_S8_UINT:
3752 case WINED3DFMT_X8D24_UNORM:
3753 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3755 case WINED3DFMT_D32_UNORM:
3756 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3760 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format_desc->format));
3763 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3764 DestRect == NULL ? 0 : 1,
3765 (const WINED3DRECT *)DestRect,
3766 WINED3DCLEAR_ZBUFFER,
3772 FIXME("(%p): Unsupp depthstencil blit\n", This);
3773 return WINED3DERR_INVALIDCALL;
3776 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
3777 const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3778 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3779 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3780 IWineD3DDeviceImpl *myDevice = This->resource.device;
3782 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3783 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3785 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3787 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3788 return WINEDDERR_SURFACEBUSY;
3791 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3792 * except depth blits, which seem to work
3794 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3795 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3796 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3797 return WINED3DERR_INVALIDCALL;
3798 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3799 TRACE("Z Blit override handled the blit\n");
3804 /* Special cases for RenderTargets */
3805 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3806 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3807 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3810 /* For the rest call the X11 surface implementation.
3811 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3812 * other Blts are rather rare
3814 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3817 static HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
3818 IWineD3DSurface *Source, const RECT *rsrc, DWORD trans)
3820 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3821 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3822 IWineD3DDeviceImpl *myDevice = This->resource.device;
3824 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3826 if ( (This->Flags & SFLAG_LOCKED) || (srcImpl->Flags & SFLAG_LOCKED))
3828 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3829 return WINEDDERR_SURFACEBUSY;
3832 if(myDevice->inScene &&
3833 (iface == myDevice->stencilBufferTarget ||
3834 (Source == myDevice->stencilBufferTarget))) {
3835 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3836 return WINED3DERR_INVALIDCALL;
3839 /* Special cases for RenderTargets */
3840 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3841 (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) ) {
3843 RECT SrcRect, DstRect;
3846 surface_get_rect(srcImpl, rsrc, &SrcRect);
3848 DstRect.left = dstx;
3850 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3851 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3853 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3854 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3855 Flags |= WINEDDBLT_KEYSRC;
3856 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3857 Flags |= WINEDDBLT_KEYDEST;
3858 if(trans & WINEDDBLTFAST_WAIT)
3859 Flags |= WINEDDBLT_WAIT;
3860 if(trans & WINEDDBLTFAST_DONOTWAIT)
3861 Flags |= WINEDDBLT_DONOTWAIT;
3863 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
3867 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3870 static HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface)
3872 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3874 IWineD3DPaletteImpl *pal = This->palette;
3876 TRACE("(%p)\n", This);
3878 if (!pal) return WINED3D_OK;
3880 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
3881 || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
3883 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3885 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
3886 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
3888 /* We want to force a palette refresh, so mark the drawable as not being up to date */
3889 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
3891 if(!(This->Flags & SFLAG_INSYSMEM)) {
3892 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3893 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
3895 TRACE("Dirtifying surface\n");
3896 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3900 if(This->Flags & SFLAG_DIBSECTION) {
3901 TRACE("(%p): Updating the hdc's palette\n", This);
3902 for (n=0; n<256; n++) {
3903 col[n].rgbRed = pal->palents[n].peRed;
3904 col[n].rgbGreen = pal->palents[n].peGreen;
3905 col[n].rgbBlue = pal->palents[n].peBlue;
3906 col[n].rgbReserved = 0;
3908 SetDIBColorTable(This->hDC, 0, 256, col);
3911 /* Propagate the changes to the drawable when we have a palette. */
3912 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3913 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
3918 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3919 /** Check against the maximum texture sizes supported by the video card **/
3920 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3921 const struct wined3d_gl_info *gl_info = &This->resource.device->adapter->gl_info;
3922 unsigned int pow2Width, pow2Height;
3924 This->texture_name = 0;
3925 This->texture_target = GL_TEXTURE_2D;
3927 /* Non-power2 support */
3928 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINE_NORMALIZED_TEXRECT])
3930 pow2Width = This->currentDesc.Width;
3931 pow2Height = This->currentDesc.Height;
3935 /* Find the nearest pow2 match */
3936 pow2Width = pow2Height = 1;
3937 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
3938 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
3940 This->pow2Width = pow2Width;
3941 This->pow2Height = pow2Height;
3943 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
3944 /** TODO: add support for non power two compressed textures **/
3945 if (This->resource.format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
3947 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
3948 This, This->currentDesc.Width, This->currentDesc.Height);
3949 return WINED3DERR_NOTAVAILABLE;
3953 if(pow2Width != This->currentDesc.Width ||
3954 pow2Height != This->currentDesc.Height) {
3955 This->Flags |= SFLAG_NONPOW2;
3958 TRACE("%p\n", This);
3959 if ((This->pow2Width > gl_info->limits.texture_size || This->pow2Height > gl_info->limits.texture_size)
3960 && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
3962 /* one of three options
3963 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)
3964 2: Set the texture to the maximum size (bad idea)
3965 3: WARN and return WINED3DERR_NOTAVAILABLE;
3966 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.
3968 if(This->resource.pool == WINED3DPOOL_DEFAULT || This->resource.pool == WINED3DPOOL_MANAGED)
3970 WARN("(%p) Unable to allocate a surface which exceeds the maximum OpenGL texture size\n", This);
3971 return WINED3DERR_NOTAVAILABLE;
3974 /* We should never use this surface in combination with OpenGL! */
3975 TRACE("(%p) Creating an oversized surface: %ux%u\n", This, This->pow2Width, This->pow2Height);
3979 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
3980 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
3981 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
3983 if (This->Flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
3984 && !(This->resource.format_desc->format == WINED3DFMT_P8_UINT
3985 && gl_info->supported[EXT_PALETTED_TEXTURE]
3986 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
3988 This->texture_target = GL_TEXTURE_RECTANGLE_ARB;
3989 This->pow2Width = This->currentDesc.Width;
3990 This->pow2Height = This->currentDesc.Height;
3991 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
3995 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
3996 switch(wined3d_settings.offscreen_rendering_mode) {
3997 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
3998 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4002 This->Flags |= SFLAG_INSYSMEM;
4007 /* GL locking is done by the caller */
4008 static void surface_depth_blt(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info,
4009 GLuint texture, GLsizei w, GLsizei h, GLenum target)
4011 IWineD3DDeviceImpl *device = This->resource.device;
4012 struct blt_info info;
4013 GLint old_binding = 0;
4015 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4017 glDisable(GL_CULL_FACE);
4018 glDisable(GL_BLEND);
4019 glDisable(GL_ALPHA_TEST);
4020 glDisable(GL_SCISSOR_TEST);
4021 glDisable(GL_STENCIL_TEST);
4022 glEnable(GL_DEPTH_TEST);
4023 glDepthFunc(GL_ALWAYS);
4024 glDepthMask(GL_TRUE);
4025 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4026 glViewport(0, 0, w, h);
4028 surface_get_blt_info(target, NULL, w, h, &info);
4029 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4030 glGetIntegerv(info.binding, &old_binding);
4031 glBindTexture(info.bind_target, texture);
4033 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4035 glBegin(GL_TRIANGLE_STRIP);
4036 glTexCoord3fv(info.coords[0]);
4037 glVertex2f(-1.0f, -1.0f);
4038 glTexCoord3fv(info.coords[1]);
4039 glVertex2f(1.0f, -1.0f);
4040 glTexCoord3fv(info.coords[2]);
4041 glVertex2f(-1.0f, 1.0f);
4042 glTexCoord3fv(info.coords[3]);
4043 glVertex2f(1.0f, 1.0f);
4046 glBindTexture(info.bind_target, old_binding);
4050 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4053 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
4054 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4056 TRACE("(%p) New location %#x\n", This, location);
4058 if (location & ~SFLAG_DS_LOCATIONS) {
4059 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
4062 This->Flags &= ~SFLAG_DS_LOCATIONS;
4063 This->Flags |= location;
4066 /* Context activation is done by the caller. */
4067 void surface_load_ds_location(IWineD3DSurface *iface, struct wined3d_context *context, DWORD location)
4069 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4070 IWineD3DDeviceImpl *device = This->resource.device;
4071 const struct wined3d_gl_info *gl_info = context->gl_info;
4073 TRACE("(%p) New location %#x\n", This, location);
4075 /* TODO: Make this work for modes other than FBO */
4076 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4078 if (This->Flags & location) {
4079 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
4083 if (This->current_renderbuffer) {
4084 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
4088 if (location == SFLAG_DS_OFFSCREEN) {
4089 if (This->Flags & SFLAG_DS_ONSCREEN) {
4090 GLint old_binding = 0;
4093 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
4097 if (!device->depth_blt_texture) {
4098 glGenTextures(1, &device->depth_blt_texture);
4101 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4102 * directly on the FBO texture. That's because we need to flip. */
4103 context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4104 if (This->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4106 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4107 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4109 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4110 bind_target = GL_TEXTURE_2D;
4112 glBindTexture(bind_target, device->depth_blt_texture);
4113 glCopyTexImage2D(bind_target, This->texture_level, This->resource.format_desc->glInternal,
4114 0, 0, This->currentDesc.Width, This->currentDesc.Height, 0);
4115 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4116 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4117 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4118 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4119 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4120 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4121 glBindTexture(bind_target, old_binding);
4123 /* Setup the destination */
4124 if (!device->depth_blt_rb) {
4125 gl_info->fbo_ops.glGenRenderbuffers(1, &device->depth_blt_rb);
4126 checkGLcall("glGenRenderbuffersEXT");
4128 if (device->depth_blt_rb_w != This->currentDesc.Width
4129 || device->depth_blt_rb_h != This->currentDesc.Height) {
4130 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, device->depth_blt_rb);
4131 checkGLcall("glBindRenderbufferEXT");
4132 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8,
4133 This->currentDesc.Width, This->currentDesc.Height);
4134 checkGLcall("glRenderbufferStorageEXT");
4135 device->depth_blt_rb_w = This->currentDesc.Width;
4136 device->depth_blt_rb_h = This->currentDesc.Height;
4139 context_bind_fbo(context, GL_FRAMEBUFFER, &context->dst_fbo);
4140 gl_info->fbo_ops.glFramebufferRenderbuffer(GL_FRAMEBUFFER,
4141 GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, device->depth_blt_rb);
4142 checkGLcall("glFramebufferRenderbufferEXT");
4143 context_attach_depth_stencil_fbo(context, GL_FRAMEBUFFER, This, FALSE);
4145 /* Do the actual blit */
4146 surface_depth_blt(This, gl_info, device->depth_blt_texture,
4147 This->currentDesc.Width, This->currentDesc.Height, bind_target);
4148 checkGLcall("depth_blt");
4150 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER, &context->current_fbo->id);
4151 else context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4155 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
4159 FIXME("No up to date depth stencil location\n");
4161 } else if (location == SFLAG_DS_ONSCREEN) {
4162 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4163 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4167 context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4168 surface_depth_blt(This, gl_info, This->texture_name,
4169 This->currentDesc.Width, This->currentDesc.Height, This->texture_target);
4170 checkGLcall("depth_blt");
4172 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER, &context->current_fbo->id);
4176 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
4180 FIXME("No up to date depth stencil location\n");
4183 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4186 This->Flags |= location;
4189 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4190 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4191 IWineD3DBaseTexture *texture;
4192 IWineD3DSurfaceImpl *overlay;
4194 TRACE("(%p)->(%s, %s)\n", iface, debug_surflocation(flag),
4195 persistent ? "TRUE" : "FALSE");
4197 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4198 if (surface_is_offscreen(iface))
4200 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4201 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4205 TRACE("Surface %p is an onscreen surface\n", iface);
4210 if(((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) ||
4211 ((This->Flags & SFLAG_INSRGBTEX) && !(flag & SFLAG_INSRGBTEX))) {
4212 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4213 TRACE("Passing to container\n");
4214 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4215 IWineD3DBaseTexture_Release(texture);
4218 This->Flags &= ~SFLAG_LOCATIONS;
4219 This->Flags |= flag;
4221 /* Redraw emulated overlays, if any */
4222 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4223 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4224 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4228 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))) {
4229 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4230 TRACE("Passing to container\n");
4231 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4232 IWineD3DBaseTexture_Release(texture);
4235 This->Flags &= ~flag;
4238 if(!(This->Flags & SFLAG_LOCATIONS)) {
4239 ERR("%p: Surface does not have any up to date location\n", This);
4243 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in)
4245 IWineD3DDeviceImpl *device = This->resource.device;
4246 IWineD3DSwapChainImpl *swapchain;
4247 struct wined3d_context *context;
4248 RECT src_rect, dst_rect;
4250 surface_get_rect(This, rect_in, &src_rect);
4252 context = context_acquire(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4253 if (context->render_offscreen)
4255 dst_rect.left = src_rect.left;
4256 dst_rect.right = src_rect.right;
4257 dst_rect.top = src_rect.bottom;
4258 dst_rect.bottom = src_rect.top;
4262 dst_rect = src_rect;
4265 device->blitter->set_shader((IWineD3DDevice *) device, This);
4268 draw_textured_quad(This, &src_rect, &dst_rect, WINED3DTEXF_POINT);
4271 device->blitter->set_shader((IWineD3DDevice *) device, This);
4273 swapchain = (This->Flags & SFLAG_SWAPCHAIN) ? (IWineD3DSwapChainImpl *)This->container : NULL;
4274 if (wined3d_settings.strict_draw_ordering || (swapchain
4275 && ((IWineD3DSurface *)This == swapchain->frontBuffer
4276 || swapchain->num_contexts > 1)))
4277 wglFlush(); /* Flush to ensure ordering across contexts. */
4279 context_release(context);
4282 /*****************************************************************************
4283 * IWineD3DSurface::LoadLocation
4285 * Copies the current surface data from wherever it is to the requested
4286 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4287 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4288 * multiple locations, the gl texture is preferred over the drawable, which is
4289 * preferred over system memory. The PBO counts as system memory. If rect is
4290 * not NULL, only the specified rectangle is copied (only supported for
4291 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4292 * location is marked up to date after the copy.
4295 * flag: Surface location flag to be updated
4296 * rect: rectangle to be copied
4299 * WINED3D_OK on success
4300 * WINED3DERR_DEVICELOST on an internal error
4302 *****************************************************************************/
4303 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4304 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4305 IWineD3DDeviceImpl *device = This->resource.device;
4306 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4307 struct wined3d_format_desc desc;
4308 CONVERT_TYPES convert;
4309 int width, pitch, outpitch;
4311 BOOL drawable_read_ok = TRUE;
4312 BOOL in_fbo = FALSE;
4314 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4315 if (surface_is_offscreen(iface))
4317 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4318 * Prefer SFLAG_INTEXTURE. */
4319 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4320 drawable_read_ok = FALSE;
4325 TRACE("Surface %p is an onscreen surface\n", iface);
4329 TRACE("(%p)->(%s, %p)\n", iface, debug_surflocation(flag), rect);
4331 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4334 if(This->Flags & flag) {
4335 TRACE("Location already up to date\n");
4339 if(!(This->Flags & SFLAG_LOCATIONS)) {
4340 ERR("%p: Surface does not have any up to date location\n", This);
4341 This->Flags |= SFLAG_LOST;
4342 return WINED3DERR_DEVICELOST;
4345 if(flag == SFLAG_INSYSMEM) {
4346 surface_prepare_system_memory(This);
4348 /* Download the surface to system memory */
4349 if (This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
4351 struct wined3d_context *context = NULL;
4353 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4355 surface_bind_and_dirtify(This, !(This->Flags & SFLAG_INTEXTURE));
4356 surface_download_data(This, gl_info);
4358 if (context) context_release(context);
4362 /* Note: It might be faster to download into a texture first. */
4363 read_from_framebuffer(This, rect,
4364 This->resource.allocatedMemory,
4365 IWineD3DSurface_GetPitch(iface));
4367 } else if(flag == SFLAG_INDRAWABLE) {
4368 if(This->Flags & SFLAG_INTEXTURE) {
4369 surface_blt_to_drawable(This, rect);
4372 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4373 /* This needs a shader to convert the srgb data sampled from the GL texture into RGB
4374 * values, otherwise we get incorrect values in the target. For now go the slow way
4375 * via a system memory copy
4377 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4380 d3dfmt_get_conv(This, FALSE /* We need color keying */, FALSE /* We won't use textures */, &desc, &convert);
4382 /* The width is in 'length' not in bytes */
4383 width = This->currentDesc.Width;
4384 pitch = IWineD3DSurface_GetPitch(iface);
4386 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4387 * but it isn't set (yet) in all cases it is getting called. */
4388 if ((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO))
4390 struct wined3d_context *context = NULL;
4392 TRACE("Removing the pbo attached to surface %p\n", This);
4394 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4395 surface_remove_pbo(This, gl_info);
4396 if (context) context_release(context);
4399 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4400 int height = This->currentDesc.Height;
4401 byte_count = desc.conv_byte_count;
4403 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4404 outpitch = width * byte_count;
4405 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4407 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4409 ERR("Out of memory %d, %d!\n", outpitch, height);
4410 return WINED3DERR_OUTOFVIDEOMEMORY;
4412 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4414 This->Flags |= SFLAG_CONVERTED;
4416 This->Flags &= ~SFLAG_CONVERTED;
4417 mem = This->resource.allocatedMemory;
4418 byte_count = desc.byte_count;
4421 flush_to_framebuffer_drawpixels(This, desc.glFormat, desc.glType, byte_count, mem);
4423 /* Don't delete PBO memory */
4424 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4425 HeapFree(GetProcessHeap(), 0, mem);
4427 } else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */ {
4428 if (drawable_read_ok && (This->Flags & SFLAG_INDRAWABLE)) {
4429 read_from_framebuffer_texture(This, flag == SFLAG_INSRGBTEX);
4433 /* Upload from system memory */
4434 BOOL srgb = flag == SFLAG_INSRGBTEX;
4435 struct wined3d_context *context = NULL;
4437 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */,
4441 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE) {
4442 /* Performance warning ... */
4443 FIXME("%p: Downloading rgb texture to reload it as srgb\n", This);
4444 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4447 if((This->Flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX) {
4448 /* Performance warning ... */
4449 FIXME("%p: Downloading srgb texture to reload it as rgb\n", This);
4450 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4453 if(!(This->Flags & SFLAG_INSYSMEM)) {
4454 /* Should not happen */
4455 ERR("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set\n");
4456 /* Lets hope we get it from somewhere... */
4457 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4460 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4462 surface_prepare_texture(This, gl_info, srgb);
4463 surface_bind_and_dirtify(This, srgb);
4465 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4466 This->Flags |= SFLAG_GLCKEY;
4467 This->glCKey = This->SrcBltCKey;
4469 else This->Flags &= ~SFLAG_GLCKEY;
4471 /* The width is in 'length' not in bytes */
4472 width = This->currentDesc.Width;
4473 pitch = IWineD3DSurface_GetPitch(iface);
4475 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4476 * but it isn't set (yet) in all cases it is getting called. */
4477 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4478 TRACE("Removing the pbo attached to surface %p\n", This);
4479 surface_remove_pbo(This, gl_info);
4483 /* This code is entered for texture formats which need a fixup. */
4484 int height = This->currentDesc.Height;
4486 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4487 outpitch = width * desc.conv_byte_count;
4488 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4490 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4492 ERR("Out of memory %d, %d!\n", outpitch, height);
4493 if (context) context_release(context);
4494 return WINED3DERR_OUTOFVIDEOMEMORY;
4496 desc.convert(This->resource.allocatedMemory, mem, pitch, width, height);
4497 } else if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4498 /* This code is only entered for color keying fixups */
4499 int height = This->currentDesc.Height;
4501 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4502 outpitch = width * desc.conv_byte_count;
4503 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4505 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4507 ERR("Out of memory %d, %d!\n", outpitch, height);
4508 if (context) context_release(context);
4509 return WINED3DERR_OUTOFVIDEOMEMORY;
4511 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4513 mem = This->resource.allocatedMemory;
4516 /* Make sure the correct pitch is used */
4518 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4521 if (mem || (This->Flags & SFLAG_PBO))
4522 surface_upload_data(This, gl_info, &desc, srgb, mem);
4524 /* Restore the default pitch */
4526 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4529 if (context) context_release(context);
4531 /* Don't delete PBO memory */
4532 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4533 HeapFree(GetProcessHeap(), 0, mem);
4538 This->Flags |= flag;
4541 if (in_fbo && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4542 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4543 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4549 static HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container)
4551 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4552 IWineD3DSwapChain *swapchain = NULL;
4554 /* Update the drawable size method */
4556 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4559 This->get_drawable_size = get_drawable_size_swapchain;
4560 IWineD3DSwapChain_Release(swapchain);
4561 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4562 switch(wined3d_settings.offscreen_rendering_mode) {
4563 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4564 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4568 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4571 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4572 return SURFACE_OPENGL;
4575 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
4576 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4579 /* If there's no destination surface there is nothing to do */
4580 if(!This->overlay_dest) return WINED3D_OK;
4582 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
4583 * update the overlay. Prevent an endless recursion
4585 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
4588 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
4589 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
4590 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
4591 NULL, WINED3DTEXF_LINEAR);
4592 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
4597 BOOL surface_is_offscreen(IWineD3DSurface *iface)
4599 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4600 IWineD3DSwapChainImpl *swapchain = (IWineD3DSwapChainImpl *) This->container;
4602 /* Not on a swapchain - must be offscreen */
4603 if (!(This->Flags & SFLAG_SWAPCHAIN)) return TRUE;
4605 /* The front buffer is always onscreen */
4606 if(iface == swapchain->frontBuffer) return FALSE;
4608 /* If the swapchain is rendered to an FBO, the backbuffer is
4609 * offscreen, otherwise onscreen */
4610 return swapchain->render_to_fbo;
4613 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4616 IWineD3DBaseSurfaceImpl_QueryInterface,
4617 IWineD3DBaseSurfaceImpl_AddRef,
4618 IWineD3DSurfaceImpl_Release,
4619 /* IWineD3DResource */
4620 IWineD3DBaseSurfaceImpl_GetParent,
4621 IWineD3DBaseSurfaceImpl_SetPrivateData,
4622 IWineD3DBaseSurfaceImpl_GetPrivateData,
4623 IWineD3DBaseSurfaceImpl_FreePrivateData,
4624 IWineD3DBaseSurfaceImpl_SetPriority,
4625 IWineD3DBaseSurfaceImpl_GetPriority,
4626 IWineD3DSurfaceImpl_PreLoad,
4627 IWineD3DSurfaceImpl_UnLoad,
4628 IWineD3DBaseSurfaceImpl_GetType,
4629 /* IWineD3DSurface */
4630 IWineD3DBaseSurfaceImpl_GetContainer,
4631 IWineD3DBaseSurfaceImpl_GetDesc,
4632 IWineD3DSurfaceImpl_LockRect,
4633 IWineD3DSurfaceImpl_UnlockRect,
4634 IWineD3DSurfaceImpl_GetDC,
4635 IWineD3DSurfaceImpl_ReleaseDC,
4636 IWineD3DSurfaceImpl_Flip,
4637 IWineD3DSurfaceImpl_Blt,
4638 IWineD3DBaseSurfaceImpl_GetBltStatus,
4639 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4640 IWineD3DBaseSurfaceImpl_IsLost,
4641 IWineD3DBaseSurfaceImpl_Restore,
4642 IWineD3DSurfaceImpl_BltFast,
4643 IWineD3DBaseSurfaceImpl_GetPalette,
4644 IWineD3DBaseSurfaceImpl_SetPalette,
4645 IWineD3DSurfaceImpl_RealizePalette,
4646 IWineD3DBaseSurfaceImpl_SetColorKey,
4647 IWineD3DBaseSurfaceImpl_GetPitch,
4648 IWineD3DSurfaceImpl_SetMem,
4649 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4650 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4651 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4652 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4653 IWineD3DBaseSurfaceImpl_SetClipper,
4654 IWineD3DBaseSurfaceImpl_GetClipper,
4656 IWineD3DSurfaceImpl_LoadTexture,
4657 IWineD3DSurfaceImpl_BindTexture,
4658 IWineD3DSurfaceImpl_SaveSnapshot,
4659 IWineD3DSurfaceImpl_SetContainer,
4660 IWineD3DBaseSurfaceImpl_GetData,
4661 IWineD3DSurfaceImpl_SetFormat,
4662 IWineD3DSurfaceImpl_PrivateSetup,
4663 IWineD3DSurfaceImpl_ModifyLocation,
4664 IWineD3DSurfaceImpl_LoadLocation,
4665 IWineD3DSurfaceImpl_GetImplType,
4666 IWineD3DSurfaceImpl_DrawOverlay
4669 static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
4670 /* Context activation is done by the caller. */
4671 static void ffp_blit_free(IWineD3DDevice *iface) { }
4673 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
4674 /* Context activation is done by the caller. */
4675 static void ffp_blit_p8_upload_palette(IWineD3DSurfaceImpl *surface, const struct wined3d_gl_info *gl_info)
4678 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
4680 d3dfmt_p8_init_palette(surface, table, colorkey_active);
4682 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
4684 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
4688 /* Context activation is done by the caller. */
4689 static HRESULT ffp_blit_set(IWineD3DDevice *iface, IWineD3DSurfaceImpl *surface)
4691 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
4692 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4693 enum complex_fixup fixup = get_complex_fixup(surface->resource.format_desc->color_fixup);
4695 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
4696 * else the surface is converted in software at upload time in LoadLocation.
4698 if(fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
4699 ffp_blit_p8_upload_palette(surface, gl_info);
4702 glEnable(surface->texture_target);
4703 checkGLcall("glEnable(surface->texture_target)");
4708 /* Context activation is done by the caller. */
4709 static void ffp_blit_unset(IWineD3DDevice *iface)
4711 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
4712 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4715 glDisable(GL_TEXTURE_2D);
4716 checkGLcall("glDisable(GL_TEXTURE_2D)");
4717 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
4719 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4720 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4722 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
4724 glDisable(GL_TEXTURE_RECTANGLE_ARB);
4725 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4730 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum blit_operation blit_op,
4731 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool,
4732 const struct wined3d_format_desc *src_format_desc,
4733 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool,
4734 const struct wined3d_format_desc *dst_format_desc)
4736 enum complex_fixup src_fixup;
4738 if (blit_op == BLIT_OP_COLOR_FILL)
4740 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4742 TRACE("Color fill not supported\n");
4749 src_fixup = get_complex_fixup(src_format_desc->color_fixup);
4750 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4752 TRACE("Checking support for fixup:\n");
4753 dump_color_fixup_desc(src_format_desc->color_fixup);
4756 if (blit_op != BLIT_OP_BLIT)
4758 TRACE("Unsupported blit_op=%d\n", blit_op);
4762 if (!is_identity_fixup(dst_format_desc->color_fixup))
4764 TRACE("Destination fixups are not supported\n");
4768 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
4770 TRACE("P8 fixup supported\n");
4774 /* We only support identity conversions. */
4775 if (is_identity_fixup(src_format_desc->color_fixup))
4781 TRACE("[FAILED]\n");
4785 static HRESULT ffp_blit_color_fill(IWineD3DDeviceImpl *device, IWineD3DSurfaceImpl *dst_surface, const RECT *dst_rect, DWORD fill_color)
4787 return IWineD3DDeviceImpl_ClearSurface(device, dst_surface, 1 /* Number of rectangles */,
4788 (const WINED3DRECT*)dst_rect, WINED3DCLEAR_TARGET, fill_color, 0.0f /* Z */, 0 /* Stencil */);
4791 const struct blit_shader ffp_blit = {
4800 static HRESULT cpu_blit_alloc(IWineD3DDevice *iface)
4805 /* Context activation is done by the caller. */
4806 static void cpu_blit_free(IWineD3DDevice *iface)
4810 /* Context activation is done by the caller. */
4811 static HRESULT cpu_blit_set(IWineD3DDevice *iface, IWineD3DSurfaceImpl *surface)
4816 /* Context activation is done by the caller. */
4817 static void cpu_blit_unset(IWineD3DDevice *iface)
4821 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum blit_operation blit_op,
4822 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool,
4823 const struct wined3d_format_desc *src_format_desc,
4824 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool,
4825 const struct wined3d_format_desc *dst_format_desc)
4827 if (blit_op == BLIT_OP_COLOR_FILL)
4835 static HRESULT cpu_blit_color_fill(IWineD3DDeviceImpl *device, IWineD3DSurfaceImpl *dst_surface, const RECT *dst_rect, DWORD fill_color)
4838 memset(&BltFx, 0, sizeof(BltFx));
4839 BltFx.dwSize = sizeof(BltFx);
4840 BltFx.u5.dwFillColor = color_convert_argb_to_fmt(fill_color, dst_surface->resource.format_desc->format);
4841 return IWineD3DBaseSurfaceImpl_Blt((IWineD3DSurface*)dst_surface, dst_rect, NULL, NULL, WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
4844 const struct blit_shader cpu_blit = {
4853 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum blit_operation blit_op,
4854 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool,
4855 const struct wined3d_format_desc *src_format_desc,
4856 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool,
4857 const struct wined3d_format_desc *dst_format_desc)
4859 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
4862 /* We only support blitting. Things like color keying / color fill should
4863 * be handled by other blitters.
4865 if (blit_op != BLIT_OP_BLIT)
4868 /* Source and/or destination need to be on the GL side */
4869 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
4872 if(!((src_format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET))
4873 && ((dst_format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
4876 if (!is_identity_fixup(src_format_desc->color_fixup) ||
4877 !is_identity_fixup(dst_format_desc->color_fixup))
4880 if (!(src_format_desc->format == dst_format_desc->format
4881 || (is_identity_fixup(src_format_desc->color_fixup)
4882 && is_identity_fixup(dst_format_desc->color_fixup))))