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(IWineD3DSurfaceImpl *surface)
468 surface->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
471 void surface_set_texture_name(IWineD3DSurface *iface, GLuint new_name, BOOL srgb)
473 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
479 name = &This->texture_name_srgb;
480 flag = SFLAG_INSRGBTEX;
484 name = &This->texture_name;
485 flag = SFLAG_INTEXTURE;
488 TRACE("(%p) : setting texture name %u\n", This, new_name);
490 if (!*name && new_name)
492 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
493 * surface has no texture name yet. See if we can get rid of this. */
494 if (This->Flags & flag)
495 ERR("Surface has SFLAG_INTEXTURE set, but no texture name\n");
496 IWineD3DSurface_ModifyLocation(iface, flag, FALSE);
500 surface_force_reload(This);
503 void surface_set_texture_target(IWineD3DSurface *iface, GLenum target)
505 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
507 TRACE("(%p) : setting target %#x\n", This, target);
509 if (This->texture_target != target)
511 if (target == GL_TEXTURE_RECTANGLE_ARB)
513 This->Flags &= ~SFLAG_NORMCOORD;
515 else if (This->texture_target == GL_TEXTURE_RECTANGLE_ARB)
517 This->Flags |= SFLAG_NORMCOORD;
520 This->texture_target = target;
521 surface_force_reload(This);
524 /* Context activation is done by the caller. */
525 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This, BOOL srgb) {
526 DWORD active_sampler;
528 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
529 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
530 * gl states. The current texture unit should always be a valid one.
532 * To be more specific, this is tricky because we can implicitly be called
533 * from sampler() in state.c. This means we can't touch anything other than
534 * whatever happens to be the currently active texture, or we would risk
535 * marking already applied sampler states dirty again.
537 * TODO: Track the current active texture per GL context instead of using glGet
539 GLint active_texture;
541 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
543 active_sampler = This->resource.device->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
545 if (active_sampler != WINED3D_UNMAPPED_STAGE)
547 IWineD3DDeviceImpl_MarkStateDirty(This->resource.device, STATE_SAMPLER(active_sampler));
549 IWineD3DSurface_BindTexture((IWineD3DSurface *)This, srgb);
552 /* This function checks if the primary render target uses the 8bit paletted format. */
553 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
555 if (device->render_targets && device->render_targets[0])
557 IWineD3DSurfaceImpl *render_target = device->render_targets[0];
558 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
559 && (render_target->resource.format_desc->format == WINED3DFMT_P8_UINT))
565 /* This call just downloads data, the caller is responsible for binding the
566 * correct texture. */
567 /* Context activation is done by the caller. */
568 static void surface_download_data(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info)
570 const struct wined3d_format_desc *format_desc = This->resource.format_desc;
572 /* Only support read back of converted P8 surfaces */
573 if (This->Flags & SFLAG_CONVERTED && format_desc->format != WINED3DFMT_P8_UINT)
575 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(format_desc->format));
581 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
583 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
584 This, This->texture_level, format_desc->glFormat, format_desc->glType,
585 This->resource.allocatedMemory);
587 if (This->Flags & SFLAG_PBO)
589 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
590 checkGLcall("glBindBufferARB");
591 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target, This->texture_level, NULL));
592 checkGLcall("glGetCompressedTexImageARB");
593 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
594 checkGLcall("glBindBufferARB");
598 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target,
599 This->texture_level, This->resource.allocatedMemory));
600 checkGLcall("glGetCompressedTexImageARB");
606 GLenum format = format_desc->glFormat;
607 GLenum type = format_desc->glType;
611 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
612 if (format_desc->format == WINED3DFMT_P8_UINT && primary_render_target_is_p8(This->resource.device))
615 type = GL_UNSIGNED_BYTE;
618 if (This->Flags & SFLAG_NONPOW2) {
619 unsigned char alignment = This->resource.device->surface_alignment;
620 src_pitch = format_desc->byte_count * This->pow2Width;
621 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
622 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
623 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
625 mem = This->resource.allocatedMemory;
628 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
629 This, This->texture_level, format, type, mem);
631 if(This->Flags & SFLAG_PBO) {
632 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
633 checkGLcall("glBindBufferARB");
635 glGetTexImage(This->texture_target, This->texture_level, format, type, NULL);
636 checkGLcall("glGetTexImage");
638 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
639 checkGLcall("glBindBufferARB");
641 glGetTexImage(This->texture_target, This->texture_level, format, type, mem);
642 checkGLcall("glGetTexImage");
646 if (This->Flags & SFLAG_NONPOW2) {
647 const BYTE *src_data;
651 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
652 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
653 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
655 * We're doing this...
657 * instead of boxing the texture :
658 * |<-texture width ->| -->pow2width| /\
659 * |111111111111111111| | |
660 * |222 Texture 222222| boxed empty | texture height
661 * |3333 Data 33333333| | |
662 * |444444444444444444| | \/
663 * ----------------------------------- |
664 * | boxed empty | boxed empty | pow2height
666 * -----------------------------------
669 * we're repacking the data to the expected texture width
671 * |<-texture width ->| -->pow2width| /\
672 * |111111111111111111222222222222222| |
673 * |222333333333333333333444444444444| texture height
677 * | empty | pow2height
679 * -----------------------------------
683 * |<-texture width ->| /\
684 * |111111111111111111|
685 * |222222222222222222|texture height
686 * |333333333333333333|
687 * |444444444444444444| \/
688 * --------------------
690 * this also means that any references to allocatedMemory should work with the data as if were a
691 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
693 * internally the texture is still stored in a boxed format so any references to textureName will
694 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
696 * Performance should not be an issue, because applications normally do not lock the surfaces when
697 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
698 * and doesn't have to be re-read.
701 dst_data = This->resource.allocatedMemory;
702 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
703 for (y = 1 ; y < This->currentDesc.Height; y++) {
704 /* skip the first row */
705 src_data += src_pitch;
706 dst_data += dst_pitch;
707 memcpy(dst_data, src_data, dst_pitch);
710 HeapFree(GetProcessHeap(), 0, mem);
714 /* Surface has now been downloaded */
715 This->Flags |= SFLAG_INSYSMEM;
718 /* This call just uploads data, the caller is responsible for binding the
719 * correct texture. */
720 /* Context activation is done by the caller. */
721 static void surface_upload_data(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info,
722 const struct wined3d_format_desc *format_desc, BOOL srgb, const GLvoid *data)
724 GLsizei width = This->currentDesc.Width;
725 GLsizei height = This->currentDesc.Height;
730 internal = format_desc->glGammaInternal;
732 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(This))
734 internal = format_desc->rtInternal;
738 internal = format_desc->glInternal;
741 TRACE("This %p, internal %#x, width %d, height %d, format %#x, type %#x, data %p.\n",
742 This, internal, width, height, format_desc->glFormat, format_desc->glType, data);
743 TRACE("target %#x, level %u, resource size %u.\n",
744 This->texture_target, This->texture_level, This->resource.size);
746 if (format_desc->heightscale != 1.0f && format_desc->heightscale != 0.0f) height *= format_desc->heightscale;
750 if (This->Flags & SFLAG_PBO)
752 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
753 checkGLcall("glBindBufferARB");
755 TRACE("(%p) pbo: %#x, data: %p.\n", This, This->pbo, data);
759 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
761 TRACE("Calling glCompressedTexSubImage2DARB.\n");
763 GL_EXTCALL(glCompressedTexSubImage2DARB(This->texture_target, This->texture_level,
764 0, 0, width, height, internal, This->resource.size, data));
765 checkGLcall("glCompressedTexSubImage2DARB");
769 TRACE("Calling glTexSubImage2D.\n");
771 glTexSubImage2D(This->texture_target, This->texture_level,
772 0, 0, width, height, format_desc->glFormat, format_desc->glType, data);
773 checkGLcall("glTexSubImage2D");
776 if (This->Flags & SFLAG_PBO)
778 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
779 checkGLcall("glBindBufferARB");
784 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
786 IWineD3DDeviceImpl *device = This->resource.device;
789 for (i = 0; i < device->numContexts; ++i)
791 context_surface_update(device->contexts[i], This);
796 /* This call just allocates the texture, the caller is responsible for binding
797 * the correct texture. */
798 /* Context activation is done by the caller. */
799 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info,
800 const struct wined3d_format_desc *format_desc, BOOL srgb)
802 BOOL enable_client_storage = FALSE;
803 GLsizei width = This->pow2Width;
804 GLsizei height = This->pow2Height;
805 const BYTE *mem = NULL;
810 internal = format_desc->glGammaInternal;
812 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET && surface_is_offscreen(This))
814 internal = format_desc->rtInternal;
818 internal = format_desc->glInternal;
821 if (format_desc->heightscale != 1.0f && format_desc->heightscale != 0.0f) height *= format_desc->heightscale;
823 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",
824 This, This->texture_target, This->texture_level, debug_d3dformat(format_desc->format),
825 internal, width, height, format_desc->glFormat, format_desc->glType);
829 if (gl_info->supported[APPLE_CLIENT_STORAGE])
831 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
832 /* In some cases we want to disable client storage.
833 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
834 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
835 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
836 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
838 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
839 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
840 This->Flags &= ~SFLAG_CLIENT;
841 enable_client_storage = TRUE;
843 This->Flags |= SFLAG_CLIENT;
845 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
846 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
848 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
852 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED && mem)
854 GL_EXTCALL(glCompressedTexImage2DARB(This->texture_target, This->texture_level,
855 internal, width, height, 0, This->resource.size, mem));
859 glTexImage2D(This->texture_target, This->texture_level,
860 internal, width, height, 0, format_desc->glFormat, format_desc->glType, mem);
861 checkGLcall("glTexImage2D");
864 if(enable_client_storage) {
865 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
866 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
871 /* In D3D the depth stencil dimensions have to be greater than or equal to the
872 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
873 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
874 /* GL locking is done by the caller */
875 void surface_set_compatible_renderbuffer(IWineD3DSurfaceImpl *surface, unsigned int width, unsigned int height)
877 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
878 renderbuffer_entry_t *entry;
879 GLuint renderbuffer = 0;
880 unsigned int src_width, src_height;
882 src_width = surface->pow2Width;
883 src_height = surface->pow2Height;
885 /* A depth stencil smaller than the render target is not valid */
886 if (width > src_width || height > src_height) return;
888 /* Remove any renderbuffer set if the sizes match */
889 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
890 || (width == src_width && height == src_height))
892 surface->current_renderbuffer = NULL;
896 /* Look if we've already got a renderbuffer of the correct dimensions */
897 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, renderbuffer_entry_t, entry)
899 if (entry->width == width && entry->height == height)
901 renderbuffer = entry->id;
902 surface->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 surface->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(&surface->renderbuffers, &entry->entry);
920 surface->current_renderbuffer = entry;
923 checkGLcall("set_compatible_renderbuffer");
926 GLenum surface_get_gl_buffer(IWineD3DSurfaceImpl *surface)
928 IWineD3DSwapChainImpl *swapchain = (IWineD3DSwapChainImpl *)surface->container;
930 TRACE("surface %p.\n", surface);
932 if (!(surface->Flags & SFLAG_SWAPCHAIN))
934 ERR("Surface %p is not on a swapchain.\n", surface);
938 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface)
940 if (swapchain->render_to_fbo)
942 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
943 return GL_COLOR_ATTACHMENT0;
945 TRACE("Returning GL_BACK\n");
948 else if (surface == swapchain->front_buffer)
950 TRACE("Returning GL_FRONT\n");
954 FIXME("Higher back buffer, returning GL_BACK\n");
958 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
959 void surface_add_dirty_rect(IWineD3DSurface *iface, const RECT *dirty_rect)
961 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
962 IWineD3DBaseTexture *baseTexture = NULL;
964 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
965 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
967 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
970 This->dirtyRect.left = min(This->dirtyRect.left, dirty_rect->left);
971 This->dirtyRect.top = min(This->dirtyRect.top, dirty_rect->top);
972 This->dirtyRect.right = max(This->dirtyRect.right, dirty_rect->right);
973 This->dirtyRect.bottom = max(This->dirtyRect.bottom, dirty_rect->bottom);
977 This->dirtyRect.left = 0;
978 This->dirtyRect.top = 0;
979 This->dirtyRect.right = This->currentDesc.Width;
980 This->dirtyRect.bottom = This->currentDesc.Height;
983 TRACE("(%p) : Dirty: yes, Rect:(%d, %d, %d, %d)\n", This, This->dirtyRect.left,
984 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
986 /* if the container is a basetexture then mark it dirty. */
987 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture)))
989 TRACE("Passing to container\n");
990 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
991 IWineD3DBaseTexture_Release(baseTexture);
995 static BOOL surface_convert_color_to_argb(IWineD3DSurfaceImpl *This, DWORD color, DWORD *argb_color)
997 IWineD3DDeviceImpl *device = This->resource.device;
999 switch(This->resource.format_desc->format)
1001 case WINED3DFMT_P8_UINT:
1005 if (primary_render_target_is_p8(device))
1006 alpha = color << 24;
1010 if (This->palette) {
1011 *argb_color = (alpha |
1012 (This->palette->palents[color].peRed << 16) |
1013 (This->palette->palents[color].peGreen << 8) |
1014 (This->palette->palents[color].peBlue));
1016 *argb_color = alpha;
1021 case WINED3DFMT_B5G6R5_UNORM:
1023 if (color == 0xFFFF) {
1024 *argb_color = 0xFFFFFFFF;
1026 *argb_color = ((0xFF000000) |
1027 ((color & 0xF800) << 8) |
1028 ((color & 0x07E0) << 5) |
1029 ((color & 0x001F) << 3));
1034 case WINED3DFMT_B8G8R8_UNORM:
1035 case WINED3DFMT_B8G8R8X8_UNORM:
1036 *argb_color = 0xFF000000 | color;
1039 case WINED3DFMT_B8G8R8A8_UNORM:
1040 *argb_color = color;
1044 ERR("Unhandled conversion from %s to ARGB!\n", debug_d3dformat(This->resource.format_desc->format));
1050 static ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface)
1052 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1053 ULONG ref = InterlockedDecrement(&This->resource.ref);
1054 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
1058 surface_cleanup(This);
1059 This->resource.parent_ops->wined3d_object_destroyed(This->resource.parent);
1061 TRACE("(%p) Released.\n", This);
1062 HeapFree(GetProcessHeap(), 0, This);
1068 /* ****************************************************
1069 IWineD3DSurface IWineD3DResource parts follow
1070 **************************************************** */
1072 void surface_internal_preload(IWineD3DSurfaceImpl *surface, enum WINED3DSRGB srgb)
1074 /* TODO: check for locks */
1075 IWineD3DDeviceImpl *device = surface->resource.device;
1076 IWineD3DBaseTexture *baseTexture = NULL;
1078 TRACE("(%p)Checking to see if the container is a base texture\n", surface);
1079 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)surface,
1080 &IID_IWineD3DBaseTexture, (void **)&baseTexture)))
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", surface);
1091 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
1093 if (surface->resource.format_desc->format == WINED3DFMT_P8_UINT
1094 || surface->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
1096 if (palette9_changed(surface))
1098 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
1099 /* TODO: This is not necessarily needed with hw palettized texture support */
1100 IWineD3DSurface_LoadLocation((IWineD3DSurface *)surface, SFLAG_INSYSMEM, NULL);
1101 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
1102 IWineD3DSurface_ModifyLocation((IWineD3DSurface *)surface, SFLAG_INTEXTURE, FALSE);
1106 IWineD3DSurface_LoadTexture((IWineD3DSurface *)surface, srgb == SRGB_SRGB ? TRUE : FALSE);
1108 if (surface->resource.pool == WINED3DPOOL_DEFAULT)
1110 /* Tell opengl to try and keep this texture in video ram (well mostly) */
1114 glPrioritizeTextures(1, &surface->texture_name, &tmp);
1118 if (context) context_release(context);
1122 static void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface)
1124 surface_internal_preload((IWineD3DSurfaceImpl *)iface, SRGB_ANY);
1127 /* Context activation is done by the caller. */
1128 static void surface_remove_pbo(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info)
1130 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1131 This->resource.allocatedMemory =
1132 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1135 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1136 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
1137 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
1138 checkGLcall("glGetBufferSubDataARB");
1139 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
1140 checkGLcall("glDeleteBuffersARB");
1144 This->Flags &= ~SFLAG_PBO;
1147 BOOL surface_init_sysmem(IWineD3DSurface *iface)
1149 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
1151 if(!This->resource.allocatedMemory)
1153 This->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->resource.size + RESOURCE_ALIGNMENT);
1154 if(!This->resource.heapMemory)
1156 ERR("Out of memory\n");
1159 This->resource.allocatedMemory =
1160 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1164 memset(This->resource.allocatedMemory, 0, This->resource.size);
1167 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
1171 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
1172 IWineD3DBaseTexture *texture = NULL;
1173 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
1174 IWineD3DDeviceImpl *device = This->resource.device;
1175 const struct wined3d_gl_info *gl_info;
1176 renderbuffer_entry_t *entry, *entry2;
1177 struct wined3d_context *context;
1179 TRACE("(%p)\n", iface);
1181 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
1182 /* Default pool resources are supposed to be destroyed before Reset is called.
1183 * Implicit resources stay however. So this means we have an implicit render target
1184 * or depth stencil. The content may be destroyed, but we still have to tear down
1185 * opengl resources, so we cannot leave early.
1187 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1188 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1189 * or the depth stencil into an FBO the texture or render buffer will be removed
1190 * and all flags get lost
1192 surface_init_sysmem(iface);
1194 /* Load the surface into system memory */
1195 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
1196 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
1198 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
1199 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSRGBTEX, FALSE);
1200 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1202 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
1203 gl_info = context->gl_info;
1205 /* Destroy PBOs, but load them into real sysmem before */
1206 if (This->Flags & SFLAG_PBO)
1207 surface_remove_pbo(This, gl_info);
1209 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1210 * all application-created targets the application has to release the surface
1211 * before calling _Reset
1213 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
1215 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1217 list_remove(&entry->entry);
1218 HeapFree(GetProcessHeap(), 0, entry);
1220 list_init(&This->renderbuffers);
1221 This->current_renderbuffer = NULL;
1223 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
1226 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
1229 glDeleteTextures(1, &This->texture_name);
1230 This->texture_name = 0;
1231 glDeleteTextures(1, &This->texture_name_srgb);
1232 This->texture_name_srgb = 0;
1235 IWineD3DBaseTexture_Release(texture);
1238 context_release(context);
1241 /* ******************************************************
1242 IWineD3DSurface IWineD3DSurface parts follow
1243 ****************************************************** */
1245 /* Read the framebuffer back into the surface */
1246 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, const RECT *rect, void *dest, UINT pitch)
1248 IWineD3DDeviceImpl *device = This->resource.device;
1249 const struct wined3d_gl_info *gl_info;
1250 struct wined3d_context *context;
1254 BYTE *row, *top, *bottom;
1258 BOOL srcIsUpsideDown;
1263 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1264 static BOOL warned = FALSE;
1266 ERR("The application tries to lock the render target, but render target locking is disabled\n");
1272 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
1273 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
1274 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
1275 * context->last_was_blit set on the unlock.
1277 context = context_acquire(device, This, CTXUSAGE_BLIT);
1278 gl_info = context->gl_info;
1282 /* Select the correct read buffer, and give some debug output.
1283 * There is no need to keep track of the current read buffer or reset it, every part of the code
1284 * that reads sets the read buffer as desired.
1286 if (surface_is_offscreen(This))
1288 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1289 * Read from the back buffer
1291 TRACE("Locking offscreen render target\n");
1292 glReadBuffer(device->offscreenBuffer);
1293 srcIsUpsideDown = TRUE;
1297 /* Onscreen surfaces are always part of a swapchain */
1298 GLenum buffer = surface_get_gl_buffer(This);
1299 TRACE("Locking %#x buffer\n", buffer);
1300 glReadBuffer(buffer);
1301 checkGLcall("glReadBuffer");
1302 srcIsUpsideDown = FALSE;
1305 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
1307 local_rect.left = 0;
1309 local_rect.right = This->currentDesc.Width;
1310 local_rect.bottom = This->currentDesc.Height;
1314 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
1316 switch(This->resource.format_desc->format)
1318 case WINED3DFMT_P8_UINT:
1320 if (primary_render_target_is_p8(device))
1322 /* In case of P8 render targets the index is stored in the alpha component */
1324 type = GL_UNSIGNED_BYTE;
1326 bpp = This->resource.format_desc->byte_count;
1328 /* GL can't return palettized data, so read ARGB pixels into a
1329 * separate block of memory and convert them into palettized format
1330 * in software. Slow, but if the app means to use palettized render
1331 * targets and locks it...
1333 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
1334 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
1335 * for the color channels when palettizing the colors.
1338 type = GL_UNSIGNED_BYTE;
1340 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
1342 ERR("Out of memory\n");
1346 bpp = This->resource.format_desc->byte_count * 3;
1353 fmt = This->resource.format_desc->glFormat;
1354 type = This->resource.format_desc->glType;
1355 bpp = This->resource.format_desc->byte_count;
1358 if(This->Flags & SFLAG_PBO) {
1359 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
1360 checkGLcall("glBindBufferARB");
1362 ERR("mem not null for pbo -- unexpected\n");
1367 /* Save old pixel store pack state */
1368 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
1369 checkGLcall("glGetIntegerv");
1370 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
1371 checkGLcall("glGetIntegerv");
1372 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
1373 checkGLcall("glGetIntegerv");
1375 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1376 glPixelStorei(GL_PACK_ROW_LENGTH, This->currentDesc.Width);
1377 checkGLcall("glPixelStorei");
1378 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
1379 checkGLcall("glPixelStorei");
1380 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
1381 checkGLcall("glPixelStorei");
1383 glReadPixels(local_rect.left, (!srcIsUpsideDown) ? (This->currentDesc.Height - local_rect.bottom) : local_rect.top ,
1384 local_rect.right - local_rect.left,
1385 local_rect.bottom - local_rect.top,
1387 checkGLcall("glReadPixels");
1389 /* Reset previous pixel store pack state */
1390 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
1391 checkGLcall("glPixelStorei");
1392 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
1393 checkGLcall("glPixelStorei");
1394 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
1395 checkGLcall("glPixelStorei");
1397 if(This->Flags & SFLAG_PBO) {
1398 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1399 checkGLcall("glBindBufferARB");
1401 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
1402 * to get a pointer to it and perform the flipping in software. This is a lot
1403 * faster than calling glReadPixels for each line. In case we want more speed
1404 * we should rerender it flipped in a FBO and read the data back from the FBO. */
1405 if(!srcIsUpsideDown) {
1406 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1407 checkGLcall("glBindBufferARB");
1409 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1410 checkGLcall("glMapBufferARB");
1414 /* TODO: Merge this with the palettization loop below for P8 targets */
1415 if(!srcIsUpsideDown) {
1417 /* glReadPixels returns the image upside down, and there is no way to prevent this.
1418 Flip the lines in software */
1419 len = (local_rect.right - local_rect.left) * bpp;
1420 off = local_rect.left * bpp;
1422 row = HeapAlloc(GetProcessHeap(), 0, len);
1424 ERR("Out of memory\n");
1425 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT) HeapFree(GetProcessHeap(), 0, mem);
1430 top = mem + pitch * local_rect.top;
1431 bottom = mem + pitch * (local_rect.bottom - 1);
1432 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
1433 memcpy(row, top + off, len);
1434 memcpy(top + off, bottom + off, len);
1435 memcpy(bottom + off, row, len);
1439 HeapFree(GetProcessHeap(), 0, row);
1441 /* Unmap the temp PBO buffer */
1442 if(This->Flags & SFLAG_PBO) {
1443 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1444 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1449 context_release(context);
1451 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
1452 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
1453 * the same color but we have no choice.
1454 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
1456 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(device))
1458 const PALETTEENTRY *pal = NULL;
1459 DWORD width = pitch / 3;
1463 pal = This->palette->palents;
1465 ERR("Palette is missing, cannot perform inverse palette lookup\n");
1466 HeapFree(GetProcessHeap(), 0, mem);
1470 for(y = local_rect.top; y < local_rect.bottom; y++) {
1471 for(x = local_rect.left; x < local_rect.right; x++) {
1472 /* start lines pixels */
1473 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
1474 const BYTE *green = blue + 1;
1475 const BYTE *red = green + 1;
1477 for(c = 0; c < 256; c++) {
1478 if(*red == pal[c].peRed &&
1479 *green == pal[c].peGreen &&
1480 *blue == pal[c].peBlue)
1482 *((BYTE *) dest + y * width + x) = c;
1488 HeapFree(GetProcessHeap(), 0, mem);
1492 /* Read the framebuffer contents into a texture */
1493 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This, BOOL srgb)
1495 IWineD3DDeviceImpl *device = This->resource.device;
1496 const struct wined3d_gl_info *gl_info;
1497 struct wined3d_context *context;
1499 BOOL alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
1501 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
1502 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
1503 * states in the stateblock, and no driver was found yet that had bugs in that regard.
1505 context = context_acquire(device, This, CTXUSAGE_RESOURCELOAD);
1506 gl_info = context->gl_info;
1508 surface_bind_and_dirtify(This, srgb);
1511 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1514 /* Select the correct read buffer, and give some debug output.
1515 * There is no need to keep track of the current read buffer or reset it, every part of the code
1516 * that reads sets the read buffer as desired.
1518 if (!surface_is_offscreen(This))
1520 GLenum buffer = surface_get_gl_buffer(This);
1521 TRACE("Locking %#x buffer\n", buffer);
1524 glReadBuffer(buffer);
1525 checkGLcall("glReadBuffer");
1530 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1531 * Read from the back buffer
1533 TRACE("Locking offscreen render target\n");
1535 glReadBuffer(device->offscreenBuffer);
1536 checkGLcall("glReadBuffer");
1540 if (!(This->Flags & alloc_flag))
1542 surface_allocate_surface(This, gl_info, This->resource.format_desc, srgb);
1543 This->Flags |= alloc_flag;
1547 /* If !SrcIsUpsideDown we should flip the surface.
1548 * This can be done using glCopyTexSubImage2D but this
1549 * is VERY slow, so don't do that. We should prevent
1550 * this code from getting called in such cases or perhaps
1551 * we can use FBOs */
1553 glCopyTexSubImage2D(This->texture_target, This->texture_level,
1554 0, 0, 0, 0, This->currentDesc.Width, This->currentDesc.Height);
1555 checkGLcall("glCopyTexSubImage2D");
1557 glReadBuffer(prevRead);
1558 checkGLcall("glReadBuffer");
1562 context_release(context);
1564 TRACE("Updated target %d\n", This->texture_target);
1567 /* Context activation is done by the caller. */
1568 static void surface_prepare_texture_internal(IWineD3DSurfaceImpl *surface,
1569 const struct wined3d_gl_info *gl_info, BOOL srgb)
1571 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
1572 CONVERT_TYPES convert;
1573 struct wined3d_format_desc desc;
1575 if (surface->Flags & alloc_flag) return;
1577 d3dfmt_get_conv(surface, TRUE, TRUE, &desc, &convert);
1578 if(convert != NO_CONVERSION) surface->Flags |= SFLAG_CONVERTED;
1579 else surface->Flags &= ~SFLAG_CONVERTED;
1581 surface_bind_and_dirtify(surface, srgb);
1582 surface_allocate_surface(surface, gl_info, &desc, srgb);
1583 surface->Flags |= alloc_flag;
1586 /* Context activation is done by the caller. */
1587 void surface_prepare_texture(IWineD3DSurfaceImpl *surface, const struct wined3d_gl_info *gl_info, BOOL srgb)
1589 IWineD3DBaseTextureImpl *texture;
1591 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)surface,
1592 &IID_IWineD3DBaseTexture, (void **)&texture)))
1594 UINT sub_count = texture->baseTexture.level_count * texture->baseTexture.layer_count;
1597 TRACE("surface %p is a subresource of texture %p.\n", surface, texture);
1599 for (i = 0; i < sub_count; ++i)
1601 IWineD3DSurfaceImpl *s = (IWineD3DSurfaceImpl *)texture->baseTexture.sub_resources[i];
1602 surface_prepare_texture_internal(s, gl_info, srgb);
1605 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *)texture);
1608 surface_prepare_texture_internal(surface, gl_info, srgb);
1611 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This)
1613 IWineD3DDeviceImpl *device = This->resource.device;
1614 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1616 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
1617 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
1620 if(!(This->Flags & SFLAG_DYNLOCK)) {
1622 /* MAXLOCKCOUNT is defined in wined3d_private.h */
1623 if(This->lockCount > MAXLOCKCOUNT) {
1624 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
1625 This->Flags |= SFLAG_DYNLOCK;
1629 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
1630 * Also don't create a PBO for systemmem surfaces.
1632 if (gl_info->supported[ARB_PIXEL_BUFFER_OBJECT] && (This->Flags & SFLAG_DYNLOCK)
1633 && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2))
1634 && (This->resource.pool != WINED3DPOOL_SYSTEMMEM))
1637 struct wined3d_context *context;
1639 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
1642 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
1643 error = glGetError();
1644 if(This->pbo == 0 || error != GL_NO_ERROR) {
1645 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
1648 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
1650 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1651 checkGLcall("glBindBufferARB");
1653 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1654 checkGLcall("glBufferDataARB");
1656 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1657 checkGLcall("glBindBufferARB");
1659 /* We don't need the system memory anymore and we can't even use it for PBOs */
1660 if(!(This->Flags & SFLAG_CLIENT)) {
1661 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1662 This->resource.heapMemory = NULL;
1664 This->resource.allocatedMemory = NULL;
1665 This->Flags |= SFLAG_PBO;
1667 context_release(context);
1669 else if (!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO))
1671 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1674 if(!This->resource.heapMemory) {
1675 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1677 This->resource.allocatedMemory =
1678 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1679 if(This->Flags & SFLAG_INSYSMEM) {
1680 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1685 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1686 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1687 IWineD3DDeviceImpl *device = This->resource.device;
1688 const RECT *pass_rect = pRect;
1690 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1692 /* This is also done in the base class, but we have to verify this before loading any data from
1693 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1694 * may interfere, and all other bad things may happen
1696 if (This->Flags & SFLAG_LOCKED) {
1697 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1698 return WINED3DERR_INVALIDCALL;
1700 This->Flags |= SFLAG_LOCKED;
1702 if (!(This->Flags & SFLAG_LOCKABLE))
1704 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1707 if (Flags & WINED3DLOCK_DISCARD) {
1708 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1709 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1710 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1711 This->Flags |= SFLAG_INSYSMEM;
1715 if (This->Flags & SFLAG_INSYSMEM) {
1716 TRACE("Local copy is up to date, not downloading data\n");
1717 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1721 /* IWineD3DSurface_LoadLocation() does not check if the rectangle specifies
1722 * the full surface. Most callers don't need that, so do it here. */
1723 if (pRect && pRect->top == 0 && pRect->left == 0
1724 && pRect->right == This->currentDesc.Width
1725 && pRect->bottom == This->currentDesc.Height)
1730 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
1731 && ((This->Flags & SFLAG_SWAPCHAIN) || This == device->render_targets[0])))
1733 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1737 if (This->Flags & SFLAG_PBO)
1739 const struct wined3d_gl_info *gl_info;
1740 struct wined3d_context *context;
1742 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
1743 gl_info = context->gl_info;
1746 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1747 checkGLcall("glBindBufferARB");
1749 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1750 if(This->resource.allocatedMemory) {
1751 ERR("The surface already has PBO memory allocated!\n");
1754 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1755 checkGLcall("glMapBufferARB");
1757 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1758 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1759 checkGLcall("glBindBufferARB");
1762 context_release(context);
1765 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1768 IWineD3DBaseTexture *pBaseTexture;
1771 * as seen in msdn docs
1773 surface_add_dirty_rect(iface, pRect);
1775 /** Dirtify Container if needed */
1776 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1777 TRACE("Making container dirty\n");
1778 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1779 IWineD3DBaseTexture_Release(pBaseTexture);
1781 TRACE("Surface is standalone, no need to dirty the container\n");
1785 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1788 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1790 GLint prev_rasterpos[4];
1791 GLint skipBytes = 0;
1792 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1793 IWineD3DDeviceImpl *device = This->resource.device;
1794 const struct wined3d_gl_info *gl_info;
1795 struct wined3d_context *context;
1797 /* Activate the correct context for the render target */
1798 context = context_acquire(device, This, CTXUSAGE_BLIT);
1799 gl_info = context->gl_info;
1803 if (!surface_is_offscreen(This))
1805 GLenum buffer = surface_get_gl_buffer(This);
1806 TRACE("Unlocking %#x buffer.\n", buffer);
1807 context_set_draw_buffer(context, buffer);
1811 /* Primary offscreen render target */
1812 TRACE("Offscreen render target.\n");
1813 context_set_draw_buffer(context, device->offscreenBuffer);
1816 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1817 checkGLcall("glGetIntegerv");
1818 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1819 checkGLcall("glGetIntegerv");
1820 glPixelZoom(1.0f, -1.0f);
1821 checkGLcall("glPixelZoom");
1823 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1824 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1825 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1827 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1828 checkGLcall("glRasterPos3i");
1830 /* Some drivers(radeon dri, others?) don't like exceptions during
1831 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1832 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1833 * catch to put the dib section in InSync mode, which leads to a crash
1834 * and a blocked x server on my radeon card.
1836 * The following lines read the dib section so it is put in InSync mode
1837 * before glDrawPixels is called and the crash is prevented. There won't
1838 * be any interfering gdi accesses, because UnlockRect is called from
1839 * ReleaseDC, and the app won't use the dc any more afterwards.
1841 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1843 read = This->resource.allocatedMemory[0];
1846 if(This->Flags & SFLAG_PBO) {
1847 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1848 checkGLcall("glBindBufferARB");
1851 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1852 if(This->Flags & SFLAG_LOCKED) {
1853 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1854 (This->lockedRect.bottom - This->lockedRect.top)-1,
1856 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1857 checkGLcall("glDrawPixels");
1859 glDrawPixels(This->currentDesc.Width,
1860 This->currentDesc.Height,
1862 checkGLcall("glDrawPixels");
1865 if(This->Flags & SFLAG_PBO) {
1866 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1867 checkGLcall("glBindBufferARB");
1870 glPixelZoom(1.0f, 1.0f);
1871 checkGLcall("glPixelZoom");
1873 glRasterPos3iv(&prev_rasterpos[0]);
1874 checkGLcall("glRasterPos3iv");
1876 /* Reset to previous pack row length */
1877 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1878 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH)");
1881 context_release(context);
1884 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1885 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1886 IWineD3DDeviceImpl *device = This->resource.device;
1889 if (!(This->Flags & SFLAG_LOCKED)) {
1890 WARN("trying to Unlock an unlocked surf@%p\n", This);
1891 return WINEDDERR_NOTLOCKED;
1894 if (This->Flags & SFLAG_PBO)
1896 const struct wined3d_gl_info *gl_info;
1897 struct wined3d_context *context;
1899 TRACE("Freeing PBO memory\n");
1901 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
1902 gl_info = context->gl_info;
1905 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1906 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1907 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1908 checkGLcall("glUnmapBufferARB");
1910 context_release(context);
1912 This->resource.allocatedMemory = NULL;
1915 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1917 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1918 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1922 if ((This->Flags & SFLAG_SWAPCHAIN) || (device->render_targets && This == device->render_targets[0]))
1924 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1925 static BOOL warned = FALSE;
1927 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1933 if(This->dirtyRect.left == 0 &&
1934 This->dirtyRect.top == 0 &&
1935 This->dirtyRect.right == This->currentDesc.Width &&
1936 This->dirtyRect.bottom == This->currentDesc.Height) {
1939 /* TODO: Proper partial rectangle tracking */
1940 fullsurface = FALSE;
1941 This->Flags |= SFLAG_INSYSMEM;
1944 switch(wined3d_settings.rendertargetlock_mode) {
1946 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1950 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1955 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1956 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1957 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1958 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1959 * not fully up to date because only a subrectangle was read in LockRect.
1961 This->Flags &= ~SFLAG_INSYSMEM;
1962 This->Flags |= SFLAG_INDRAWABLE;
1965 This->dirtyRect.left = This->currentDesc.Width;
1966 This->dirtyRect.top = This->currentDesc.Height;
1967 This->dirtyRect.right = 0;
1968 This->dirtyRect.bottom = 0;
1970 else if (This == device->depth_stencil)
1972 FIXME("Depth Stencil buffer locking is not implemented\n");
1974 /* The rest should be a normal texture */
1975 IWineD3DBaseTextureImpl *impl;
1976 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1977 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1978 * states need resetting
1980 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1981 if (impl->baseTexture.bindCount)
1982 IWineD3DDeviceImpl_MarkStateDirty(device, STATE_SAMPLER(impl->baseTexture.sampler));
1983 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1988 This->Flags &= ~SFLAG_LOCKED;
1989 memset(&This->lockedRect, 0, sizeof(RECT));
1991 /* Overlays have to be redrawn manually after changes with the GL implementation */
1992 if(This->overlay_dest) {
1993 IWineD3DSurface_DrawOverlay(iface);
1998 static void surface_release_client_storage(IWineD3DSurface *iface)
2000 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2001 struct wined3d_context *context;
2003 context = context_acquire(This->resource.device, NULL, CTXUSAGE_RESOURCELOAD);
2006 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
2007 if(This->texture_name)
2009 surface_bind_and_dirtify(This, FALSE);
2010 glTexImage2D(This->texture_target, This->texture_level,
2011 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
2013 if(This->texture_name_srgb)
2015 surface_bind_and_dirtify(This, TRUE);
2016 glTexImage2D(This->texture_target, This->texture_level,
2017 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
2019 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2022 context_release(context);
2024 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSRGBTEX, FALSE);
2025 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
2026 surface_force_reload(This);
2029 static HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC)
2031 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2032 WINED3DLOCKED_RECT lock;
2036 TRACE("(%p)->(%p)\n",This,pHDC);
2038 if(This->Flags & SFLAG_USERPTR) {
2039 ERR("Not supported on surfaces with an application-provided surfaces\n");
2040 return WINEDDERR_NODC;
2043 /* Give more detailed info for ddraw */
2044 if (This->Flags & SFLAG_DCINUSE)
2045 return WINEDDERR_DCALREADYCREATED;
2047 /* Can't GetDC if the surface is locked */
2048 if (This->Flags & SFLAG_LOCKED)
2049 return WINED3DERR_INVALIDCALL;
2051 memset(&lock, 0, sizeof(lock)); /* To be sure */
2053 /* Create a DIB section if there isn't a hdc yet */
2055 if(This->Flags & SFLAG_CLIENT) {
2056 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2057 surface_release_client_storage(iface);
2059 hr = IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
2060 if(FAILED(hr)) return WINED3DERR_INVALIDCALL;
2062 /* Use the dib section from now on if we are not using a PBO */
2063 if(!(This->Flags & SFLAG_PBO))
2064 This->resource.allocatedMemory = This->dib.bitmap_data;
2067 /* Lock the surface */
2068 hr = IWineD3DSurface_LockRect(iface,
2073 if(This->Flags & SFLAG_PBO) {
2074 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
2075 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
2079 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
2080 /* keep the dib section */
2084 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
2085 || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
2087 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
2088 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
2090 const PALETTEENTRY *pal = NULL;
2093 pal = This->palette->palents;
2095 IWineD3DSurfaceImpl *dds_primary;
2096 IWineD3DSwapChainImpl *swapchain;
2097 swapchain = (IWineD3DSwapChainImpl *)This->resource.device->swapchains[0];
2098 dds_primary = swapchain->front_buffer;
2099 if (dds_primary && dds_primary->palette)
2100 pal = dds_primary->palette->palents;
2104 for (n=0; n<256; n++) {
2105 col[n].rgbRed = pal[n].peRed;
2106 col[n].rgbGreen = pal[n].peGreen;
2107 col[n].rgbBlue = pal[n].peBlue;
2108 col[n].rgbReserved = 0;
2110 SetDIBColorTable(This->hDC, 0, 256, col);
2115 TRACE("returning %p\n",*pHDC);
2116 This->Flags |= SFLAG_DCINUSE;
2121 static HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC)
2123 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2125 TRACE("(%p)->(%p)\n",This,hDC);
2127 if (!(This->Flags & SFLAG_DCINUSE))
2128 return WINEDDERR_NODC;
2130 if (This->hDC !=hDC) {
2131 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
2132 return WINEDDERR_NODC;
2135 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
2136 /* Copy the contents of the DIB over to the PBO */
2137 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
2140 /* we locked first, so unlock now */
2141 IWineD3DSurface_UnlockRect(iface);
2143 This->Flags &= ~SFLAG_DCINUSE;
2148 /* ******************************************************
2149 IWineD3DSurface Internal (No mapping to directx api) parts follow
2150 ****************************************************** */
2152 HRESULT d3dfmt_get_conv(IWineD3DSurfaceImpl *This, BOOL need_alpha_ck, BOOL use_texturing, struct wined3d_format_desc *desc, CONVERT_TYPES *convert)
2154 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
2155 IWineD3DDeviceImpl *device = This->resource.device;
2156 BOOL blit_supported = FALSE;
2157 RECT rect = {0, 0, This->pow2Width, This->pow2Height};
2159 /* Copy the default values from the surface. Below we might perform fixups */
2160 /* TODO: get rid of color keying desc fixups by using e.g. a table. */
2161 *desc = *This->resource.format_desc;
2162 *convert = NO_CONVERSION;
2164 /* Ok, now look if we have to do any conversion */
2165 switch(This->resource.format_desc->format)
2167 case WINED3DFMT_P8_UINT:
2172 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, BLIT_OP_BLIT,
2173 &rect, This->resource.usage, This->resource.pool,
2174 This->resource.format_desc, &rect, This->resource.usage,
2175 This->resource.pool, This->resource.format_desc);
2177 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
2178 * texturing. Further also use conversion in case of color keying.
2179 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
2180 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
2181 * conflicts with this.
2183 if (!((blit_supported && device->render_targets && This == device->render_targets[0]))
2184 || colorkey_active || !use_texturing)
2186 desc->glFormat = GL_RGBA;
2187 desc->glInternal = GL_RGBA;
2188 desc->glType = GL_UNSIGNED_BYTE;
2189 desc->conv_byte_count = 4;
2190 if(colorkey_active) {
2191 *convert = CONVERT_PALETTED_CK;
2193 *convert = CONVERT_PALETTED;
2198 case WINED3DFMT_B2G3R3_UNORM:
2199 /* **********************
2200 GL_UNSIGNED_BYTE_3_3_2
2201 ********************** */
2202 if (colorkey_active) {
2203 /* This texture format will never be used.. So do not care about color keying
2204 up until the point in time it will be needed :-) */
2205 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
2209 case WINED3DFMT_B5G6R5_UNORM:
2210 if (colorkey_active) {
2211 *convert = CONVERT_CK_565;
2212 desc->glFormat = GL_RGBA;
2213 desc->glInternal = GL_RGB5_A1;
2214 desc->glType = GL_UNSIGNED_SHORT_5_5_5_1;
2215 desc->conv_byte_count = 2;
2219 case WINED3DFMT_B5G5R5X1_UNORM:
2220 if (colorkey_active) {
2221 *convert = CONVERT_CK_5551;
2222 desc->glFormat = GL_BGRA;
2223 desc->glInternal = GL_RGB5_A1;
2224 desc->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
2225 desc->conv_byte_count = 2;
2229 case WINED3DFMT_B8G8R8_UNORM:
2230 if (colorkey_active) {
2231 *convert = CONVERT_CK_RGB24;
2232 desc->glFormat = GL_RGBA;
2233 desc->glInternal = GL_RGBA8;
2234 desc->glType = GL_UNSIGNED_INT_8_8_8_8;
2235 desc->conv_byte_count = 4;
2239 case WINED3DFMT_B8G8R8X8_UNORM:
2240 if (colorkey_active) {
2241 *convert = CONVERT_RGB32_888;
2242 desc->glFormat = GL_RGBA;
2243 desc->glInternal = GL_RGBA8;
2244 desc->glType = GL_UNSIGNED_INT_8_8_8_8;
2245 desc->conv_byte_count = 4;
2256 void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey)
2258 IWineD3DDeviceImpl *device = This->resource.device;
2259 IWineD3DPaletteImpl *pal = This->palette;
2260 BOOL index_in_alpha = FALSE;
2263 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2264 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2265 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2266 * duplicate entries. Store the color key in the unused alpha component to speed the
2267 * download up and to make conversion unneeded. */
2268 index_in_alpha = primary_render_target_is_p8(device);
2272 UINT dxVersion = ((IWineD3DImpl *)device->wined3d)->dxVersion;
2274 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2277 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2280 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
2281 * there's no palette at this time. */
2282 for (i = 0; i < 256; i++) table[i][3] = i;
2287 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2288 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2289 * capability flag is present (wine does advertise this capability) */
2290 for (i = 0; i < 256; ++i)
2292 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2293 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2294 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2295 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2301 TRACE("Using surface palette %p\n", pal);
2302 /* Get the surface's palette */
2303 for (i = 0; i < 256; ++i)
2305 table[i][0] = pal->palents[i].peRed;
2306 table[i][1] = pal->palents[i].peGreen;
2307 table[i][2] = pal->palents[i].peBlue;
2309 /* When index_in_alpha is set the palette index is stored in the
2310 * alpha component. In case of a readback we can then read
2311 * GL_ALPHA. Color keying is handled in BltOverride using a
2312 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
2313 * color key itself is passed to glAlphaFunc in other cases the
2314 * alpha component of pixels that should be masked away is set to 0. */
2319 else if (colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue)
2320 && (i <= This->SrcBltCKey.dwColorSpaceHighValue))
2324 else if(pal->Flags & WINEDDPCAPS_ALPHA)
2326 table[i][3] = pal->palents[i].peFlags;
2336 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
2337 UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This)
2341 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
2346 memcpy(dst, src, pitch * height);
2349 case CONVERT_PALETTED:
2350 case CONVERT_PALETTED_CK:
2352 IWineD3DPaletteImpl* pal = This->palette;
2357 /* TODO: If we are a sublevel, try to get the palette from level 0 */
2360 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2362 for (y = 0; y < height; y++)
2364 source = src + pitch * y;
2365 dest = dst + outpitch * y;
2366 /* This is an 1 bpp format, using the width here is fine */
2367 for (x = 0; x < width; x++) {
2368 BYTE color = *source++;
2369 *dest++ = table[color][0];
2370 *dest++ = table[color][1];
2371 *dest++ = table[color][2];
2372 *dest++ = table[color][3];
2378 case CONVERT_CK_565:
2380 /* Converting the 565 format in 5551 packed to emulate color-keying.
2382 Note : in all these conversion, it would be best to average the averaging
2383 pixels to get the color of the pixel that will be color-keyed to
2384 prevent 'color bleeding'. This will be done later on if ever it is
2387 Note2: Nvidia documents say that their driver does not support alpha + color keying
2388 on the same surface and disables color keying in such a case
2394 TRACE("Color keyed 565\n");
2396 for (y = 0; y < height; y++) {
2397 Source = (const WORD *)(src + y * pitch);
2398 Dest = (WORD *) (dst + y * outpitch);
2399 for (x = 0; x < width; x++ ) {
2400 WORD color = *Source++;
2401 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
2402 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2403 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2412 case CONVERT_CK_5551:
2414 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
2418 TRACE("Color keyed 5551\n");
2419 for (y = 0; y < height; y++) {
2420 Source = (const WORD *)(src + y * pitch);
2421 Dest = (WORD *) (dst + y * outpitch);
2422 for (x = 0; x < width; x++ ) {
2423 WORD color = *Source++;
2425 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2426 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2430 *Dest &= ~(1 << 15);
2438 case CONVERT_CK_RGB24:
2440 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
2442 for (y = 0; y < height; y++)
2444 source = src + pitch * y;
2445 dest = dst + outpitch * y;
2446 for (x = 0; x < width; x++) {
2447 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
2448 DWORD dstcolor = color << 8;
2449 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2450 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2453 *(DWORD*)dest = dstcolor;
2461 case CONVERT_RGB32_888:
2463 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
2465 for (y = 0; y < height; y++)
2467 source = src + pitch * y;
2468 dest = dst + outpitch * y;
2469 for (x = 0; x < width; x++) {
2470 DWORD color = 0xffffff & *(const DWORD*)source;
2471 DWORD dstcolor = color << 8;
2472 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2473 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2476 *(DWORD*)dest = dstcolor;
2485 ERR("Unsupported conversion type %#x.\n", convert);
2490 BOOL palette9_changed(IWineD3DSurfaceImpl *This)
2492 IWineD3DDeviceImpl *device = This->resource.device;
2494 if (This->palette || (This->resource.format_desc->format != WINED3DFMT_P8_UINT
2495 && This->resource.format_desc->format != WINED3DFMT_P8_UINT_A8_UNORM))
2497 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2498 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2505 if (!memcmp(This->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256))
2510 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2512 memcpy(This->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2516 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2517 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2518 DWORD flag = srgb_mode ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2520 if (!(This->Flags & flag)) {
2521 TRACE("Reloading because surface is dirty\n");
2522 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2523 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2524 /* Reload: vice versa OR */
2525 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2526 /* Also reload: Color key is active AND the color key has changed */
2527 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2528 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2529 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2530 TRACE("Reloading because of color keying\n");
2531 /* To perform the color key conversion we need a sysmem copy of
2532 * the surface. Make sure we have it
2535 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2536 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2537 /* TODO: This is not necessarily needed with hw palettized texture support */
2538 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2540 TRACE("surface is already in texture\n");
2544 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2545 * These resources are not bound by device size or format restrictions. Because of this,
2546 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2547 * However, these resources can always be created, locked, and copied.
2549 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2551 FIXME("(%p) Operation not supported for scratch textures\n",This);
2552 return WINED3DERR_INVALIDCALL;
2555 IWineD3DSurface_LoadLocation(iface, flag, NULL /* no partial locking for textures yet */);
2559 static unsigned int gen = 0;
2562 if ((gen % 10) == 0) {
2563 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm",
2564 This, This->texture_target, This->texture_level, gen);
2565 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2568 * debugging crash code
2577 if (!(This->Flags & SFLAG_DONOTFREE)) {
2578 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2579 This->resource.allocatedMemory = NULL;
2580 This->resource.heapMemory = NULL;
2581 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2587 /* Context activation is done by the caller. */
2588 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface, BOOL srgb) {
2589 /* TODO: check for locks */
2590 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2591 IWineD3DBaseTexture *baseTexture = NULL;
2593 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2594 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2595 TRACE("Passing to container\n");
2596 IWineD3DBaseTexture_BindTexture(baseTexture, srgb);
2597 IWineD3DBaseTexture_Release(baseTexture);
2603 TRACE("(%p) : Binding surface\n", This);
2605 name = srgb ? &This->texture_name_srgb : &This->texture_name;
2609 if (!This->texture_level)
2612 glGenTextures(1, name);
2613 checkGLcall("glGenTextures");
2614 TRACE("Surface %p given name %d\n", This, *name);
2616 glBindTexture(This->texture_target, *name);
2617 checkGLcall("glBindTexture");
2618 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2619 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2620 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2621 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2622 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2623 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2624 glTexParameteri(This->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2625 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2626 glTexParameteri(This->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2627 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2629 /* This is where we should be reducing the amount of GLMemoryUsed */
2631 /* Mipmap surfaces should have a base texture container */
2632 ERR("Mipmap surface has a glTexture bound to it!\n");
2635 glBindTexture(This->texture_target, *name);
2636 checkGLcall("glBindTexture");
2644 static HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename)
2647 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2648 char *allocatedMemory;
2649 const char *textureRow;
2650 IWineD3DSwapChain *swapChain = NULL;
2651 int width, height, i, y;
2652 GLuint tmpTexture = 0;
2655 Textures may not be stored in ->allocatedgMemory and a GlTexture
2656 so we should lock the surface before saving a snapshot, or at least check that
2658 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2659 by calling GetTexImage and in compressed form by calling
2660 GetCompressedTexImageARB. Queried compressed images can be saved and
2661 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2662 texture images do not need to be processed by the GL and should
2663 significantly improve texture loading performance relative to uncompressed
2666 /* Setup the width and height to be the internal texture width and height. */
2667 width = This->pow2Width;
2668 height = This->pow2Height;
2669 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2670 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2672 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2673 /* if were not a real texture then read the back buffer into a real texture */
2674 /* we don't want to interfere with the back buffer so read the data into a temporary
2675 * texture and then save the data out of the temporary texture
2679 TRACE("(%p) Reading render target into texture\n", This);
2681 glGenTextures(1, &tmpTexture);
2682 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2684 glTexImage2D(GL_TEXTURE_2D,
2691 GL_UNSIGNED_INT_8_8_8_8_REV,
2694 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2695 checkGLcall("glGetIntegerv");
2696 glReadBuffer(swapChain ? GL_BACK : This->resource.device->offscreenBuffer);
2697 checkGLcall("glReadBuffer");
2698 glCopyTexImage2D(GL_TEXTURE_2D,
2707 checkGLcall("glCopyTexImage2D");
2708 glReadBuffer(prevRead);
2711 } else { /* bind the real texture, and make sure it up to date */
2712 surface_internal_preload(This, SRGB_RGB);
2713 surface_bind_and_dirtify(This, FALSE);
2715 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2717 FIXME("Saving texture level %d width %d height %d\n", This->texture_level, width, height);
2718 glGetTexImage(GL_TEXTURE_2D, This->texture_level, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, allocatedMemory);
2719 checkGLcall("glGetTexImage");
2721 glBindTexture(GL_TEXTURE_2D, 0);
2722 glDeleteTextures(1, &tmpTexture);
2726 f = fopen(filename, "w+");
2728 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2729 return WINED3DERR_INVALIDCALL;
2731 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2732 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format_desc->format));
2747 fwrite(&width,2,1,f);
2749 fwrite(&height,2,1,f);
2754 /* 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 */
2756 textureRow = allocatedMemory + (width * (height - 1) *4);
2758 textureRow = allocatedMemory;
2759 for (y = 0 ; y < height; y++) {
2760 for (i = 0; i < width; i++) {
2761 color = *((const DWORD*)textureRow);
2762 fputc((color >> 16) & 0xFF, f); /* B */
2763 fputc((color >> 8) & 0xFF, f); /* G */
2764 fputc((color >> 0) & 0xFF, f); /* R */
2765 fputc((color >> 24) & 0xFF, f); /* A */
2768 /* take two rows of the pointer to the texture memory */
2770 (textureRow-= width << 3);
2773 TRACE("Closing file\n");
2777 IWineD3DSwapChain_Release(swapChain);
2779 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2783 static HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2784 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2787 TRACE("(%p) : Calling base function first\n", This);
2788 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2790 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2791 TRACE("(%p) : glFormat %d, glFormatInternal %d, glType %d\n", This, This->resource.format_desc->glFormat,
2792 This->resource.format_desc->glInternal, This->resource.format_desc->glType);
2797 static HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2798 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2800 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2801 WARN("Surface is locked or the HDC is in use\n");
2802 return WINED3DERR_INVALIDCALL;
2805 if(Mem && Mem != This->resource.allocatedMemory) {
2806 void *release = NULL;
2808 /* Do I have to copy the old surface content? */
2809 if(This->Flags & SFLAG_DIBSECTION) {
2810 /* Release the DC. No need to hold the critical section for the update
2811 * Thread because this thread runs only on front buffers, but this method
2812 * fails for render targets in the check above.
2814 SelectObject(This->hDC, This->dib.holdbitmap);
2815 DeleteDC(This->hDC);
2816 /* Release the DIB section */
2817 DeleteObject(This->dib.DIBsection);
2818 This->dib.bitmap_data = NULL;
2819 This->resource.allocatedMemory = NULL;
2821 This->Flags &= ~SFLAG_DIBSECTION;
2822 } else if(!(This->Flags & SFLAG_USERPTR)) {
2823 release = This->resource.heapMemory;
2824 This->resource.heapMemory = NULL;
2826 This->resource.allocatedMemory = Mem;
2827 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2829 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2830 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2832 /* For client textures opengl has to be notified */
2833 if(This->Flags & SFLAG_CLIENT) {
2834 surface_release_client_storage(iface);
2837 /* Now free the old memory if any */
2838 HeapFree(GetProcessHeap(), 0, release);
2839 } else if(This->Flags & SFLAG_USERPTR) {
2840 /* LockRect and GetDC will re-create the dib section and allocated memory */
2841 This->resource.allocatedMemory = NULL;
2842 /* HeapMemory should be NULL already */
2843 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2844 This->Flags &= ~SFLAG_USERPTR;
2846 if(This->Flags & SFLAG_CLIENT) {
2847 surface_release_client_storage(iface);
2853 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
2855 /* Flip the surface contents */
2860 front->hDC = back->hDC;
2864 /* Flip the DIBsection */
2867 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
2868 tmp = front->dib.DIBsection;
2869 front->dib.DIBsection = back->dib.DIBsection;
2870 back->dib.DIBsection = tmp;
2872 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
2873 else front->Flags &= ~SFLAG_DIBSECTION;
2874 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
2875 else back->Flags &= ~SFLAG_DIBSECTION;
2878 /* Flip the surface data */
2882 tmp = front->dib.bitmap_data;
2883 front->dib.bitmap_data = back->dib.bitmap_data;
2884 back->dib.bitmap_data = tmp;
2886 tmp = front->resource.allocatedMemory;
2887 front->resource.allocatedMemory = back->resource.allocatedMemory;
2888 back->resource.allocatedMemory = tmp;
2890 tmp = front->resource.heapMemory;
2891 front->resource.heapMemory = back->resource.heapMemory;
2892 back->resource.heapMemory = tmp;
2897 GLuint tmp_pbo = front->pbo;
2898 front->pbo = back->pbo;
2899 back->pbo = tmp_pbo;
2902 /* client_memory should not be different, but just in case */
2905 tmp = front->dib.client_memory;
2906 front->dib.client_memory = back->dib.client_memory;
2907 back->dib.client_memory = tmp;
2910 /* Flip the opengl texture */
2914 tmp = back->texture_name;
2915 back->texture_name = front->texture_name;
2916 front->texture_name = tmp;
2918 tmp = back->texture_name_srgb;
2919 back->texture_name_srgb = front->texture_name_srgb;
2920 front->texture_name_srgb = tmp;
2924 DWORD tmp_flags = back->Flags;
2925 back->Flags = front->Flags;
2926 front->Flags = tmp_flags;
2930 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
2931 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2932 IWineD3DSwapChainImpl *swapchain = NULL;
2934 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
2936 /* Flipping is only supported on RenderTargets and overlays*/
2937 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
2938 WARN("Tried to flip a non-render target, non-overlay surface\n");
2939 return WINEDDERR_NOTFLIPPABLE;
2942 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
2943 flip_surface(This, (IWineD3DSurfaceImpl *) override);
2945 /* Update the overlay if it is visible */
2946 if(This->overlay_dest) {
2947 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
2954 /* DDraw sets this for the X11 surfaces, so don't confuse the user
2955 * FIXME("(%p) Target override is not supported by now\n", This);
2956 * Additionally, it isn't really possible to support triple-buffering
2957 * properly on opengl at all
2961 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
2963 ERR("Flipped surface is not on a swapchain\n");
2964 return WINEDDERR_NOTFLIPPABLE;
2967 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
2968 * and only d3d8 and d3d9 apps specify the presentation interval
2970 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
2971 /* Most common case first to avoid wasting time on all the other cases */
2972 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
2973 } else if(Flags & WINEDDFLIP_NOVSYNC) {
2974 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
2975 } else if(Flags & WINEDDFLIP_INTERVAL2) {
2976 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
2977 } else if(Flags & WINEDDFLIP_INTERVAL3) {
2978 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
2980 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
2983 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
2984 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *)swapchain,
2985 NULL, NULL, swapchain->win_handle, NULL, 0);
2986 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
2990 /* Does a direct frame buffer -> texture copy. Stretching is done
2991 * with single pixel copy calls
2993 static void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *dst_surface, IWineD3DSurfaceImpl *src_surface,
2994 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
2996 IWineD3DDeviceImpl *device = dst_surface->resource.device;
2999 struct wined3d_context *context;
3000 BOOL upsidedown = FALSE;
3001 RECT dst_rect = *dst_rect_in;
3003 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3004 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3006 if(dst_rect.top > dst_rect.bottom) {
3007 UINT tmp = dst_rect.bottom;
3008 dst_rect.bottom = dst_rect.top;
3013 context = context_acquire(device, src_surface, CTXUSAGE_BLIT);
3014 surface_internal_preload(dst_surface, SRGB_RGB);
3017 /* Bind the target texture */
3018 glBindTexture(dst_surface->texture_target, dst_surface->texture_name);
3019 checkGLcall("glBindTexture");
3020 if (surface_is_offscreen(src_surface))
3022 TRACE("Reading from an offscreen target\n");
3023 upsidedown = !upsidedown;
3024 glReadBuffer(device->offscreenBuffer);
3028 glReadBuffer(surface_get_gl_buffer(src_surface));
3030 checkGLcall("glReadBuffer");
3032 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
3033 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
3035 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3037 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3039 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
3040 ERR("Texture filtering not supported in direct blit\n");
3043 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
3044 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3046 ERR("Texture filtering not supported in direct blit\n");
3050 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3051 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3053 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
3055 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3056 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
3057 src_rect->left, src_surface->currentDesc.Height - src_rect->bottom,
3058 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3060 UINT yoffset = src_surface->currentDesc.Height - src_rect->top + dst_rect.top - 1;
3061 /* I have to process this row by row to swap the image,
3062 * otherwise it would be upside down, so stretching in y direction
3063 * doesn't cost extra time
3065 * However, stretching in x direction can be avoided if not necessary
3067 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
3068 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3070 /* Well, that stuff works, but it's very slow.
3071 * find a better way instead
3075 for (col = dst_rect.left; col < dst_rect.right; ++col)
3077 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3078 dst_rect.left + col /* x offset */, row /* y offset */,
3079 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3084 glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3085 dst_rect.left /* x offset */, row /* y offset */,
3086 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
3090 checkGLcall("glCopyTexSubImage2D");
3093 context_release(context);
3095 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3096 * path is never entered
3098 IWineD3DSurface_ModifyLocation((IWineD3DSurface *)dst_surface, SFLAG_INTEXTURE, TRUE);
3101 /* Uses the hardware to stretch and flip the image */
3102 static void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *dst_surface, IWineD3DSurfaceImpl *src_surface,
3103 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
3105 IWineD3DDeviceImpl *device = dst_surface->resource.device;
3106 GLuint src, backup = 0;
3107 IWineD3DSwapChainImpl *src_swapchain = NULL;
3108 float left, right, top, bottom; /* Texture coordinates */
3109 UINT fbwidth = src_surface->currentDesc.Width;
3110 UINT fbheight = src_surface->currentDesc.Height;
3111 struct wined3d_context *context;
3112 GLenum drawBuffer = GL_BACK;
3113 GLenum texture_target;
3114 BOOL noBackBufferBackup;
3116 BOOL upsidedown = FALSE;
3117 RECT dst_rect = *dst_rect_in;
3119 TRACE("Using hwstretch blit\n");
3120 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3121 context = context_acquire(device, src_surface, CTXUSAGE_BLIT);
3122 surface_internal_preload(dst_surface, SRGB_RGB);
3124 src_offscreen = surface_is_offscreen(src_surface);
3125 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3126 if (!noBackBufferBackup && !src_surface->texture_name)
3128 /* Get it a description */
3129 surface_internal_preload(src_surface, SRGB_RGB);
3133 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3134 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3136 if (context->aux_buffers >= 2)
3138 /* Got more than one aux buffer? Use the 2nd aux buffer */
3139 drawBuffer = GL_AUX1;
3141 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3143 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3144 drawBuffer = GL_AUX0;
3147 if(noBackBufferBackup) {
3148 glGenTextures(1, &backup);
3149 checkGLcall("glGenTextures");
3150 glBindTexture(GL_TEXTURE_2D, backup);
3151 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3152 texture_target = GL_TEXTURE_2D;
3154 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3155 * we are reading from the back buffer, the backup can be used as source texture
3157 texture_target = src_surface->texture_target;
3158 glBindTexture(texture_target, src_surface->texture_name);
3159 checkGLcall("glBindTexture(texture_target, src_surface->texture_name)");
3160 glEnable(texture_target);
3161 checkGLcall("glEnable(texture_target)");
3163 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3164 src_surface->Flags &= ~SFLAG_INTEXTURE;
3167 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3168 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3170 if(dst_rect.top > dst_rect.bottom) {
3171 UINT tmp = dst_rect.bottom;
3172 dst_rect.bottom = dst_rect.top;
3179 TRACE("Reading from an offscreen target\n");
3180 upsidedown = !upsidedown;
3181 glReadBuffer(device->offscreenBuffer);
3185 glReadBuffer(surface_get_gl_buffer(src_surface));
3188 /* TODO: Only back up the part that will be overwritten */
3189 glCopyTexSubImage2D(texture_target, 0,
3190 0, 0 /* read offsets */,
3195 checkGLcall("glCopyTexSubImage2D");
3197 /* No issue with overriding these - the sampler is dirty due to blit usage */
3198 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3199 wined3d_gl_mag_filter(magLookup, Filter));
3200 checkGLcall("glTexParameteri");
3201 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3202 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
3203 checkGLcall("glTexParameteri");
3205 IWineD3DSurface_GetContainer((IWineD3DSurface *)src_surface, &IID_IWineD3DSwapChain, (void **)&src_swapchain);
3206 if (src_swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *)src_swapchain);
3207 if (!src_swapchain || src_surface == src_swapchain->back_buffers[0])
3209 src = backup ? backup : src_surface->texture_name;
3213 glReadBuffer(GL_FRONT);
3214 checkGLcall("glReadBuffer(GL_FRONT)");
3216 glGenTextures(1, &src);
3217 checkGLcall("glGenTextures(1, &src)");
3218 glBindTexture(GL_TEXTURE_2D, src);
3219 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
3221 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3222 * out for power of 2 sizes
3224 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
3225 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3226 checkGLcall("glTexImage2D");
3227 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
3228 0, 0 /* read offsets */,
3233 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3234 checkGLcall("glTexParameteri");
3235 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3236 checkGLcall("glTexParameteri");
3238 glReadBuffer(GL_BACK);
3239 checkGLcall("glReadBuffer(GL_BACK)");
3241 if(texture_target != GL_TEXTURE_2D) {
3242 glDisable(texture_target);
3243 glEnable(GL_TEXTURE_2D);
3244 texture_target = GL_TEXTURE_2D;
3247 checkGLcall("glEnd and previous");
3249 left = src_rect->left;
3250 right = src_rect->right;
3254 top = src_surface->currentDesc.Height - src_rect->top;
3255 bottom = src_surface->currentDesc.Height - src_rect->bottom;
3259 top = src_surface->currentDesc.Height - src_rect->bottom;
3260 bottom = src_surface->currentDesc.Height - src_rect->top;
3263 if (src_surface->Flags & SFLAG_NORMCOORD)
3265 left /= src_surface->pow2Width;
3266 right /= src_surface->pow2Width;
3267 top /= src_surface->pow2Height;
3268 bottom /= src_surface->pow2Height;
3271 /* draw the source texture stretched and upside down. The correct surface is bound already */
3272 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3273 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3275 context_set_draw_buffer(context, drawBuffer);
3276 glReadBuffer(drawBuffer);
3280 glTexCoord2f(left, bottom);
3281 glVertex2i(0, fbheight);
3284 glTexCoord2f(left, top);
3285 glVertex2i(0, fbheight - dst_rect.bottom - dst_rect.top);
3288 glTexCoord2f(right, top);
3289 glVertex2i(dst_rect.right - dst_rect.left, fbheight - dst_rect.bottom - dst_rect.top);
3292 glTexCoord2f(right, bottom);
3293 glVertex2i(dst_rect.right - dst_rect.left, fbheight);
3295 checkGLcall("glEnd and previous");
3297 if (texture_target != dst_surface->texture_target)
3299 glDisable(texture_target);
3300 glEnable(dst_surface->texture_target);
3301 texture_target = dst_surface->texture_target;
3304 /* Now read the stretched and upside down image into the destination texture */
3305 glBindTexture(texture_target, dst_surface->texture_name);
3306 checkGLcall("glBindTexture");
3307 glCopyTexSubImage2D(texture_target,
3309 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
3310 0, 0, /* We blitted the image to the origin */
3311 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3312 checkGLcall("glCopyTexSubImage2D");
3314 if(drawBuffer == GL_BACK) {
3315 /* Write the back buffer backup back */
3317 if(texture_target != GL_TEXTURE_2D) {
3318 glDisable(texture_target);
3319 glEnable(GL_TEXTURE_2D);
3320 texture_target = GL_TEXTURE_2D;
3322 glBindTexture(GL_TEXTURE_2D, backup);
3323 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3327 if (texture_target != src_surface->texture_target)
3329 glDisable(texture_target);
3330 glEnable(src_surface->texture_target);
3331 texture_target = src_surface->texture_target;
3333 glBindTexture(src_surface->texture_target, src_surface->texture_name);
3334 checkGLcall("glBindTexture(src_surface->texture_target, src_surface->texture_name)");
3339 glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
3343 glTexCoord2f(0.0f, 0.0f);
3344 glVertex2i(0, fbheight);
3347 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
3348 glVertex2i(fbwidth, src_surface->currentDesc.Height);
3351 glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
3352 (float)fbheight / (float)src_surface->pow2Height);
3353 glVertex2i(fbwidth, 0);
3356 glDisable(texture_target);
3357 checkGLcall("glDisable(texture_target)");
3360 if (src != src_surface->texture_name && src != backup)
3362 glDeleteTextures(1, &src);
3363 checkGLcall("glDeleteTextures(1, &src)");
3366 glDeleteTextures(1, &backup);
3367 checkGLcall("glDeleteTextures(1, &backup)");
3372 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
3374 context_release(context);
3376 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3377 * path is never entered
3379 IWineD3DSurface_ModifyLocation((IWineD3DSurface *)dst_surface, SFLAG_INTEXTURE, TRUE);
3382 /* Until the blit_shader is ready, define some prototypes here. */
3383 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum blit_operation blit_op,
3384 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool,
3385 const struct wined3d_format_desc *src_format_desc,
3386 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool,
3387 const struct wined3d_format_desc *dst_format_desc);
3389 /* Not called from the VTable */
3390 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *dst_surface, const RECT *DestRect,
3391 IWineD3DSurfaceImpl *src_surface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx,
3392 WINED3DTEXTUREFILTERTYPE Filter)
3394 IWineD3DDeviceImpl *device = dst_surface->resource.device;
3395 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3396 RECT dst_rect, src_rect;
3398 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
3399 dst_surface, wine_dbgstr_rect(DestRect), src_surface, wine_dbgstr_rect(SrcRect),
3400 Flags, DDBltFx, debug_d3dtexturefiltertype(Filter));
3402 /* Get the swapchain. One of the surfaces has to be a primary surface */
3403 if (dst_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
3405 WARN("Destination is in sysmem, rejecting gl blt\n");
3406 return WINED3DERR_INVALIDCALL;
3408 IWineD3DSurface_GetContainer((IWineD3DSurface *)dst_surface, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3409 if (dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *)dstSwapchain);
3412 if (src_surface->resource.pool == WINED3DPOOL_SYSTEMMEM)
3414 WARN("Src is in sysmem, rejecting gl blt\n");
3415 return WINED3DERR_INVALIDCALL;
3417 IWineD3DSurface_GetContainer((IWineD3DSurface *)src_surface, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3418 if (srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *)srcSwapchain);
3421 /* Early sort out of cases where no render target is used */
3422 if (!dstSwapchain && !srcSwapchain
3423 && src_surface != device->render_targets[0]
3424 && dst_surface != device->render_targets[0])
3426 TRACE("No surface is render target, not using hardware blit.\n");
3427 return WINED3DERR_INVALIDCALL;
3430 /* No destination color keying supported */
3431 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3432 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3433 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3434 return WINED3DERR_INVALIDCALL;
3437 surface_get_rect(dst_surface, DestRect, &dst_rect);
3438 if (src_surface) surface_get_rect(src_surface, SrcRect, &src_rect);
3440 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3441 if (dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->back_buffers
3442 && dst_surface == dstSwapchain->front_buffer
3443 && src_surface == dstSwapchain->back_buffers[0])
3445 /* Half-life does a Blt from the back buffer to the front buffer,
3446 * Full surface size, no flags... Use present instead
3448 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3451 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3454 TRACE("Looking if a Present can be done...\n");
3455 /* Source Rectangle must be full surface */
3456 if (src_rect.left || src_rect.top
3457 || src_rect.right != src_surface->currentDesc.Width
3458 || src_rect.bottom != src_surface->currentDesc.Height)
3460 TRACE("No, Source rectangle doesn't match\n");
3464 /* No stretching may occur */
3465 if(src_rect.right != dst_rect.right - dst_rect.left ||
3466 src_rect.bottom != dst_rect.bottom - dst_rect.top) {
3467 TRACE("No, stretching is done\n");
3471 /* Destination must be full surface or match the clipping rectangle */
3472 if (dst_surface->clipper && ((IWineD3DClipperImpl *)dst_surface->clipper)->hWnd)
3476 GetClientRect(((IWineD3DClipperImpl *)dst_surface->clipper)->hWnd, &cliprect);
3477 pos[0].x = dst_rect.left;
3478 pos[0].y = dst_rect.top;
3479 pos[1].x = dst_rect.right;
3480 pos[1].y = dst_rect.bottom;
3481 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *)dst_surface->clipper)->hWnd, pos, 2);
3483 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3484 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3486 TRACE("No, dest rectangle doesn't match(clipper)\n");
3487 TRACE("Clip rect at %s\n", wine_dbgstr_rect(&cliprect));
3488 TRACE("Blt dest: %s\n", wine_dbgstr_rect(&dst_rect));
3492 else if (dst_rect.left || dst_rect.top
3493 || dst_rect.right != dst_surface->currentDesc.Width
3494 || dst_rect.bottom != dst_surface->currentDesc.Height)
3496 TRACE("No, dest rectangle doesn't match(surface size)\n");
3502 /* These flags are unimportant for the flag check, remove them */
3503 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3504 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3506 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3507 * take very long, while a flip is fast.
3508 * This applies to Half-Life, which does such Blts every time it finished
3509 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3510 * menu. This is also used by all apps when they do windowed rendering
3512 * The problem is that flipping is not really the same as copying. After a
3513 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3514 * untouched. Therefore it's necessary to override the swap effect
3515 * and to set it back after the flip.
3517 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3521 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3522 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3524 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3525 IWineD3DSwapChain_Present((IWineD3DSwapChain *)dstSwapchain,
3526 NULL, NULL, dstSwapchain->win_handle, NULL, 0);
3528 dstSwapchain->presentParms.SwapEffect = orig_swap;
3535 TRACE("Unsupported blit between buffers on the same swapchain\n");
3536 return WINED3DERR_INVALIDCALL;
3537 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3538 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3539 return WINED3DERR_INVALIDCALL;
3540 } else if(dstSwapchain && srcSwapchain) {
3541 FIXME("Implement hardware blit between two different swapchains\n");
3542 return WINED3DERR_INVALIDCALL;
3544 else if (dstSwapchain)
3546 /* Handled with regular texture -> swapchain blit */
3547 if (src_surface == device->render_targets[0])
3548 TRACE("Blit from active render target to a swapchain\n");
3550 else if (srcSwapchain && dst_surface == device->render_targets[0])
3552 FIXME("Implement blit from a swapchain to the active render target\n");
3553 return WINED3DERR_INVALIDCALL;
3556 if ((srcSwapchain || src_surface == device->render_targets[0]) && !dstSwapchain)
3558 /* Blit from render target to texture */
3561 /* P8 read back is not implemented */
3562 if (src_surface->resource.format_desc->format == WINED3DFMT_P8_UINT
3563 || dst_surface->resource.format_desc->format == WINED3DFMT_P8_UINT)
3565 TRACE("P8 read back not supported by frame buffer to texture blit\n");
3566 return WINED3DERR_INVALIDCALL;
3569 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3570 TRACE("Color keying not supported by frame buffer to texture blit\n");
3571 return WINED3DERR_INVALIDCALL;
3572 /* Destination color key is checked above */
3575 if(dst_rect.right - dst_rect.left != src_rect.right - src_rect.left) {
3581 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3582 * flip the image nor scale it.
3584 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3585 * -> If the app wants a image width an unscaled width, copy it line per line
3586 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3587 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3588 * back buffer. This is slower than reading line per line, thus not used for flipping
3589 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3592 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3593 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3596 if (fbo_blit_supported(&device->adapter->gl_info, BLIT_OP_BLIT,
3597 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format_desc,
3598 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format_desc))
3600 stretch_rect_fbo(device, src_surface, &src_rect, dst_surface, &dst_rect, Filter);
3602 else if (!stretchx || dst_rect.right - dst_rect.left > src_surface->currentDesc.Width
3603 || dst_rect.bottom - dst_rect.top > src_surface->currentDesc.Height)
3605 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3606 fb_copy_to_texture_direct(dst_surface, src_surface, &src_rect, &dst_rect, Filter);
3608 TRACE("Using hardware stretching to flip / stretch the texture\n");
3609 fb_copy_to_texture_hwstretch(dst_surface, src_surface, &src_rect, &dst_rect, Filter);
3612 if (!(dst_surface->Flags & SFLAG_DONOTFREE))
3614 HeapFree(GetProcessHeap(), 0, dst_surface->resource.heapMemory);
3615 dst_surface->resource.allocatedMemory = NULL;
3616 dst_surface->resource.heapMemory = NULL;
3620 dst_surface->Flags &= ~SFLAG_INSYSMEM;
3625 else if (src_surface)
3627 /* Blit from offscreen surface to render target */
3628 DWORD oldCKeyFlags = src_surface->CKeyFlags;
3629 WINEDDCOLORKEY oldBltCKey = src_surface->SrcBltCKey;
3630 struct wined3d_context *context;
3632 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
3634 if (!(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3635 && fbo_blit_supported(&device->adapter->gl_info, BLIT_OP_BLIT,
3636 &src_rect, src_surface->resource.usage, src_surface->resource.pool,
3637 src_surface->resource.format_desc,
3638 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool,
3639 dst_surface->resource.format_desc))
3641 TRACE("Using stretch_rect_fbo\n");
3642 /* The source is always a texture, but never the currently active render target, and the texture
3643 * contents are never upside down. */
3644 stretch_rect_fbo(device, src_surface, &src_rect, dst_surface, &dst_rect, Filter);
3648 if (!(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3649 && arbfp_blit.blit_supported(&device->adapter->gl_info, BLIT_OP_BLIT,
3650 &src_rect, src_surface->resource.usage, src_surface->resource.pool,
3651 src_surface->resource.format_desc,
3652 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool,
3653 dst_surface->resource.format_desc))
3655 return arbfp_blit_surface(device, src_surface, &src_rect, dst_surface, &dst_rect, BLIT_OP_BLIT, Filter);
3658 /* Color keying: Check if we have to do a color keyed blt,
3659 * and if not check if a color key is activated.
3661 * Just modify the color keying parameters in the surface and restore them afterwards
3662 * The surface keeps track of the color key last used to load the opengl surface.
3663 * PreLoad will catch the change to the flags and color key and reload if necessary.
3665 if(Flags & WINEDDBLT_KEYSRC) {
3666 /* Use color key from surface */
3667 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3668 /* Use color key from DDBltFx */
3669 src_surface->CKeyFlags |= WINEDDSD_CKSRCBLT;
3670 src_surface->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3672 /* Do not use color key */
3673 src_surface->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3676 /* Now load the surface */
3677 surface_internal_preload(src_surface, SRGB_RGB);
3679 /* Activate the destination context, set it up for blitting */
3680 context = context_acquire(device, dst_surface, CTXUSAGE_BLIT);
3682 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3683 * while OpenGL coordinates are window relative.
3684 * Also beware of the origin difference(top left vs bottom left).
3685 * Also beware that the front buffer's surface size is screen width x screen height,
3686 * whereas the real gl drawable size is the size of the window.
3688 if (dstSwapchain && dst_surface == dstSwapchain->front_buffer)
3691 POINT offset = {0,0};
3693 ClientToScreen(context->win_handle, &offset);
3694 GetClientRect(context->win_handle, &windowsize);
3695 h = windowsize.bottom - windowsize.top;
3696 dst_rect.left -= offset.x; dst_rect.right -=offset.x;
3697 dst_rect.top -= offset.y; dst_rect.bottom -=offset.y;
3698 dst_rect.top += dst_surface->currentDesc.Height - h;
3699 dst_rect.bottom += dst_surface->currentDesc.Height - h;
3702 if (!device->blitter->blit_supported(&device->adapter->gl_info, BLIT_OP_BLIT,
3703 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format_desc,
3704 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format_desc))
3706 FIXME("Unsupported blit operation falling back to software\n");
3707 return WINED3DERR_INVALIDCALL;
3710 device->blitter->set_shader((IWineD3DDevice *)device, src_surface);
3714 /* This is for color keying */
3715 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3716 glEnable(GL_ALPHA_TEST);
3717 checkGLcall("glEnable(GL_ALPHA_TEST)");
3719 /* When the primary render target uses P8, the alpha component contains the palette index.
3720 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3721 * should be masked away have alpha set to 0. */
3722 if (primary_render_target_is_p8(device))
3723 glAlphaFunc(GL_NOTEQUAL, (float)src_surface->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
3725 glAlphaFunc(GL_NOTEQUAL, 0.0f);
3726 checkGLcall("glAlphaFunc");
3728 glDisable(GL_ALPHA_TEST);
3729 checkGLcall("glDisable(GL_ALPHA_TEST)");
3732 /* Draw a textured quad
3734 draw_textured_quad(src_surface, &src_rect, &dst_rect, Filter);
3736 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3737 glDisable(GL_ALPHA_TEST);
3738 checkGLcall("glDisable(GL_ALPHA_TEST)");
3741 /* Restore the color key parameters */
3742 src_surface->CKeyFlags = oldCKeyFlags;
3743 src_surface->SrcBltCKey = oldBltCKey;
3747 /* Leave the opengl state valid for blitting */
3748 device->blitter->unset_shader((IWineD3DDevice *)device);
3750 if (wined3d_settings.strict_draw_ordering || (dstSwapchain
3751 && (dst_surface == dstSwapchain->front_buffer
3752 || dstSwapchain->num_contexts > 1)))
3753 wglFlush(); /* Flush to ensure ordering across contexts. */
3755 context_release(context);
3757 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3758 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3761 IWineD3DSurface_ModifyLocation((IWineD3DSurface *)dst_surface, SFLAG_INDRAWABLE, TRUE);
3765 /* Source-Less Blit to render target */
3766 if (Flags & WINEDDBLT_COLORFILL) {
3769 TRACE("Colorfill\n");
3771 /* The color as given in the Blt function is in the format of the frame-buffer...
3772 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3774 if (!surface_convert_color_to_argb(dst_surface, DDBltFx->u5.dwFillColor, &color))
3776 /* The color conversion function already prints an error, so need to do it here */
3777 return WINED3DERR_INVALIDCALL;
3780 if (ffp_blit.blit_supported(&device->adapter->gl_info, BLIT_OP_COLOR_FILL,
3782 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool,
3783 dst_surface->resource.format_desc))
3785 return ffp_blit.color_fill(device, dst_surface, &dst_rect, color);
3787 else if (cpu_blit.blit_supported(&device->adapter->gl_info, BLIT_OP_COLOR_FILL,
3789 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool,
3790 dst_surface->resource.format_desc))
3792 return cpu_blit.color_fill(device, dst_surface, &dst_rect, color);
3794 return WINED3DERR_INVALIDCALL;
3798 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3799 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3800 return WINED3DERR_INVALIDCALL;
3803 static HRESULT IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3804 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx)
3806 IWineD3DDeviceImpl *device = This->resource.device;
3809 if (Flags & WINEDDBLT_DEPTHFILL) {
3810 switch(This->resource.format_desc->format)
3812 case WINED3DFMT_D16_UNORM:
3813 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3815 case WINED3DFMT_S1_UINT_D15_UNORM:
3816 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3818 case WINED3DFMT_D24_UNORM_S8_UINT:
3819 case WINED3DFMT_X8D24_UNORM:
3820 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3822 case WINED3DFMT_D32_UNORM:
3823 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3827 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format_desc->format));
3830 return IWineD3DDevice_Clear((IWineD3DDevice *)device, DestRect ? 1 : 0, (const WINED3DRECT *)DestRect,
3831 WINED3DCLEAR_ZBUFFER, 0x00000000, depth, 0x00000000);
3834 FIXME("(%p): Unsupp depthstencil blit\n", This);
3835 return WINED3DERR_INVALIDCALL;
3838 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
3839 const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3840 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3841 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3842 IWineD3DDeviceImpl *device = This->resource.device;
3844 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3845 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3847 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3849 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3850 return WINEDDERR_SURFACEBUSY;
3853 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3854 * except depth blits, which seem to work
3856 if (This == device->depth_stencil || (Src && Src == device->depth_stencil))
3858 if (device->inScene && !(Flags & WINEDDBLT_DEPTHFILL))
3860 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3861 return WINED3DERR_INVALIDCALL;
3862 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3863 TRACE("Z Blit override handled the blit\n");
3868 /* Special cases for RenderTargets */
3869 if ((This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3870 || (Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET)))
3872 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(This, DestRect, Src, SrcRect, Flags, DDBltFx, Filter)))
3876 /* For the rest call the X11 surface implementation.
3877 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3878 * other Blts are rather rare
3880 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3883 static HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
3884 IWineD3DSurface *Source, const RECT *rsrc, DWORD trans)
3886 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3887 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3888 IWineD3DDeviceImpl *device = This->resource.device;
3890 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3892 if ( (This->Flags & SFLAG_LOCKED) || (srcImpl->Flags & SFLAG_LOCKED))
3894 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3895 return WINEDDERR_SURFACEBUSY;
3898 if (device->inScene && (This == device->depth_stencil || srcImpl == device->depth_stencil))
3900 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3901 return WINED3DERR_INVALIDCALL;
3904 /* Special cases for RenderTargets */
3905 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3906 (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) ) {
3908 RECT SrcRect, DstRect;
3911 surface_get_rect(srcImpl, rsrc, &SrcRect);
3913 DstRect.left = dstx;
3915 DstRect.right = dstx + SrcRect.right - SrcRect.left;
3916 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
3918 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
3919 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
3920 Flags |= WINEDDBLT_KEYSRC;
3921 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
3922 Flags |= WINEDDBLT_KEYDEST;
3923 if(trans & WINEDDBLTFAST_WAIT)
3924 Flags |= WINEDDBLT_WAIT;
3925 if(trans & WINEDDBLTFAST_DONOTWAIT)
3926 Flags |= WINEDDBLT_DONOTWAIT;
3928 if (SUCCEEDED(IWineD3DSurfaceImpl_BltOverride(This,
3929 &DstRect, srcImpl, &SrcRect, Flags, NULL, WINED3DTEXF_POINT)))
3934 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
3937 static HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface)
3939 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3941 IWineD3DPaletteImpl *pal = This->palette;
3943 TRACE("(%p)\n", This);
3945 if (!pal) return WINED3D_OK;
3947 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
3948 || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
3950 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3952 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
3953 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
3955 /* We want to force a palette refresh, so mark the drawable as not being up to date */
3956 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
3958 if(!(This->Flags & SFLAG_INSYSMEM)) {
3959 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
3960 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
3962 TRACE("Dirtifying surface\n");
3963 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3967 if(This->Flags & SFLAG_DIBSECTION) {
3968 TRACE("(%p): Updating the hdc's palette\n", This);
3969 for (n=0; n<256; n++) {
3970 col[n].rgbRed = pal->palents[n].peRed;
3971 col[n].rgbGreen = pal->palents[n].peGreen;
3972 col[n].rgbBlue = pal->palents[n].peBlue;
3973 col[n].rgbReserved = 0;
3975 SetDIBColorTable(This->hDC, 0, 256, col);
3978 /* Propagate the changes to the drawable when we have a palette. */
3979 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
3980 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
3985 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
3986 /** Check against the maximum texture sizes supported by the video card **/
3987 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3988 const struct wined3d_gl_info *gl_info = &This->resource.device->adapter->gl_info;
3989 unsigned int pow2Width, pow2Height;
3991 This->texture_name = 0;
3992 This->texture_target = GL_TEXTURE_2D;
3994 /* Non-power2 support */
3995 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINE_NORMALIZED_TEXRECT])
3997 pow2Width = This->currentDesc.Width;
3998 pow2Height = This->currentDesc.Height;
4002 /* Find the nearest pow2 match */
4003 pow2Width = pow2Height = 1;
4004 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
4005 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
4007 This->pow2Width = pow2Width;
4008 This->pow2Height = pow2Height;
4010 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
4011 /** TODO: add support for non power two compressed textures **/
4012 if (This->resource.format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
4014 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
4015 This, This->currentDesc.Width, This->currentDesc.Height);
4016 return WINED3DERR_NOTAVAILABLE;
4020 if(pow2Width != This->currentDesc.Width ||
4021 pow2Height != This->currentDesc.Height) {
4022 This->Flags |= SFLAG_NONPOW2;
4025 TRACE("%p\n", This);
4026 if ((This->pow2Width > gl_info->limits.texture_size || This->pow2Height > gl_info->limits.texture_size)
4027 && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
4029 /* one of three options
4030 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)
4031 2: Set the texture to the maximum size (bad idea)
4032 3: WARN and return WINED3DERR_NOTAVAILABLE;
4033 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.
4035 if(This->resource.pool == WINED3DPOOL_DEFAULT || This->resource.pool == WINED3DPOOL_MANAGED)
4037 WARN("(%p) Unable to allocate a surface which exceeds the maximum OpenGL texture size\n", This);
4038 return WINED3DERR_NOTAVAILABLE;
4041 /* We should never use this surface in combination with OpenGL! */
4042 TRACE("(%p) Creating an oversized surface: %ux%u\n", This, This->pow2Width, This->pow2Height);
4046 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
4047 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
4048 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
4050 if (This->Flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
4051 && !(This->resource.format_desc->format == WINED3DFMT_P8_UINT
4052 && gl_info->supported[EXT_PALETTED_TEXTURE]
4053 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
4055 This->texture_target = GL_TEXTURE_RECTANGLE_ARB;
4056 This->pow2Width = This->currentDesc.Width;
4057 This->pow2Height = This->currentDesc.Height;
4058 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
4062 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4063 switch(wined3d_settings.offscreen_rendering_mode) {
4064 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4065 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4069 This->Flags |= SFLAG_INSYSMEM;
4074 /* GL locking is done by the caller */
4075 static void surface_depth_blt(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info,
4076 GLuint texture, GLsizei w, GLsizei h, GLenum target)
4078 IWineD3DDeviceImpl *device = This->resource.device;
4079 struct blt_info info;
4080 GLint old_binding = 0;
4082 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4084 glDisable(GL_CULL_FACE);
4085 glDisable(GL_BLEND);
4086 glDisable(GL_ALPHA_TEST);
4087 glDisable(GL_SCISSOR_TEST);
4088 glDisable(GL_STENCIL_TEST);
4089 glEnable(GL_DEPTH_TEST);
4090 glDepthFunc(GL_ALWAYS);
4091 glDepthMask(GL_TRUE);
4092 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4093 glViewport(0, 0, w, h);
4095 surface_get_blt_info(target, NULL, w, h, &info);
4096 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4097 glGetIntegerv(info.binding, &old_binding);
4098 glBindTexture(info.bind_target, texture);
4100 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4102 glBegin(GL_TRIANGLE_STRIP);
4103 glTexCoord3fv(info.coords[0]);
4104 glVertex2f(-1.0f, -1.0f);
4105 glTexCoord3fv(info.coords[1]);
4106 glVertex2f(1.0f, -1.0f);
4107 glTexCoord3fv(info.coords[2]);
4108 glVertex2f(-1.0f, 1.0f);
4109 glTexCoord3fv(info.coords[3]);
4110 glVertex2f(1.0f, 1.0f);
4113 glBindTexture(info.bind_target, old_binding);
4117 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4120 void surface_modify_ds_location(IWineD3DSurfaceImpl *surface, DWORD location)
4122 TRACE("surface %p, new location %#x.\n", surface, location);
4124 if (location & ~SFLAG_DS_LOCATIONS)
4125 FIXME("Invalid location (%#x) specified.\n", location);
4127 surface->Flags &= ~SFLAG_DS_LOCATIONS;
4128 surface->Flags |= location;
4131 /* Context activation is done by the caller. */
4132 void surface_load_ds_location(IWineD3DSurfaceImpl *surface, struct wined3d_context *context, DWORD location)
4134 IWineD3DDeviceImpl *device = surface->resource.device;
4135 const struct wined3d_gl_info *gl_info = context->gl_info;
4137 TRACE("surface %p, new location %#x.\n", surface, location);
4139 /* TODO: Make this work for modes other than FBO */
4140 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4142 if (surface->Flags & location)
4144 TRACE("Location (%#x) is already up to date.\n", location);
4148 if (surface->current_renderbuffer)
4150 FIXME("Not supported with fixed up depth stencil.\n");
4154 if (location == SFLAG_DS_OFFSCREEN)
4156 if (surface->Flags & SFLAG_DS_ONSCREEN)
4158 GLint old_binding = 0;
4161 TRACE("Copying onscreen depth buffer to depth texture.\n");
4165 if (!device->depth_blt_texture) {
4166 glGenTextures(1, &device->depth_blt_texture);
4169 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4170 * directly on the FBO texture. That's because we need to flip. */
4171 context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4172 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4174 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4175 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4177 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4178 bind_target = GL_TEXTURE_2D;
4180 glBindTexture(bind_target, device->depth_blt_texture);
4181 glCopyTexImage2D(bind_target, surface->texture_level, surface->resource.format_desc->glInternal,
4182 0, 0, surface->currentDesc.Width, surface->currentDesc.Height, 0);
4183 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4184 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4185 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4186 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4187 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4188 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4189 glBindTexture(bind_target, old_binding);
4191 /* Setup the destination */
4192 if (!device->depth_blt_rb) {
4193 gl_info->fbo_ops.glGenRenderbuffers(1, &device->depth_blt_rb);
4194 checkGLcall("glGenRenderbuffersEXT");
4196 if (device->depth_blt_rb_w != surface->currentDesc.Width
4197 || device->depth_blt_rb_h != surface->currentDesc.Height)
4199 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, device->depth_blt_rb);
4200 checkGLcall("glBindRenderbufferEXT");
4201 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8,
4202 surface->currentDesc.Width, surface->currentDesc.Height);
4203 checkGLcall("glRenderbufferStorageEXT");
4204 device->depth_blt_rb_w = surface->currentDesc.Width;
4205 device->depth_blt_rb_h = surface->currentDesc.Height;
4208 context_bind_fbo(context, GL_FRAMEBUFFER, &context->dst_fbo);
4209 gl_info->fbo_ops.glFramebufferRenderbuffer(GL_FRAMEBUFFER,
4210 GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, device->depth_blt_rb);
4211 checkGLcall("glFramebufferRenderbufferEXT");
4212 context_attach_depth_stencil_fbo(context, GL_FRAMEBUFFER, surface, FALSE);
4214 /* Do the actual blit */
4215 surface_depth_blt(surface, gl_info, device->depth_blt_texture,
4216 surface->currentDesc.Width, surface->currentDesc.Height, bind_target);
4217 checkGLcall("depth_blt");
4219 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER, &context->current_fbo->id);
4220 else context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4224 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
4228 FIXME("No up to date depth stencil location\n");
4231 else if (location == SFLAG_DS_ONSCREEN)
4233 if (surface->Flags & SFLAG_DS_OFFSCREEN)
4235 TRACE("Copying depth texture to onscreen depth buffer.\n");
4239 context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4240 surface_depth_blt(surface, gl_info, surface->texture_name,
4241 surface->currentDesc.Width, surface->currentDesc.Height, surface->texture_target);
4242 checkGLcall("depth_blt");
4244 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER, &context->current_fbo->id);
4248 if (wined3d_settings.strict_draw_ordering) wglFlush(); /* Flush to ensure ordering across contexts. */
4252 FIXME("No up to date depth stencil location\n");
4257 ERR("Invalid location (%#x) specified.\n", location);
4260 surface->Flags |= location;
4263 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4264 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4265 IWineD3DBaseTexture *texture;
4266 IWineD3DSurfaceImpl *overlay;
4268 TRACE("(%p)->(%s, %s)\n", iface, debug_surflocation(flag),
4269 persistent ? "TRUE" : "FALSE");
4271 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
4273 if (surface_is_offscreen(This))
4275 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4276 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4280 TRACE("Surface %p is an onscreen surface\n", iface);
4285 if(((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) ||
4286 ((This->Flags & SFLAG_INSRGBTEX) && !(flag & SFLAG_INSRGBTEX))) {
4287 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4288 TRACE("Passing to container\n");
4289 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4290 IWineD3DBaseTexture_Release(texture);
4293 This->Flags &= ~SFLAG_LOCATIONS;
4294 This->Flags |= flag;
4296 /* Redraw emulated overlays, if any */
4297 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4298 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4299 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4303 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))) {
4304 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4305 TRACE("Passing to container\n");
4306 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4307 IWineD3DBaseTexture_Release(texture);
4310 This->Flags &= ~flag;
4313 if(!(This->Flags & SFLAG_LOCATIONS)) {
4314 ERR("%p: Surface does not have any up to date location\n", This);
4318 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in)
4320 IWineD3DDeviceImpl *device = This->resource.device;
4321 IWineD3DSwapChainImpl *swapchain;
4322 struct wined3d_context *context;
4323 RECT src_rect, dst_rect;
4325 surface_get_rect(This, rect_in, &src_rect);
4327 context = context_acquire(device, This, CTXUSAGE_BLIT);
4328 if (context->render_offscreen)
4330 dst_rect.left = src_rect.left;
4331 dst_rect.right = src_rect.right;
4332 dst_rect.top = src_rect.bottom;
4333 dst_rect.bottom = src_rect.top;
4337 dst_rect = src_rect;
4340 device->blitter->set_shader((IWineD3DDevice *) device, This);
4343 draw_textured_quad(This, &src_rect, &dst_rect, WINED3DTEXF_POINT);
4346 device->blitter->set_shader((IWineD3DDevice *) device, This);
4348 swapchain = (This->Flags & SFLAG_SWAPCHAIN) ? (IWineD3DSwapChainImpl *)This->container : NULL;
4349 if (wined3d_settings.strict_draw_ordering || (swapchain
4350 && (This == swapchain->front_buffer || swapchain->num_contexts > 1)))
4351 wglFlush(); /* Flush to ensure ordering across contexts. */
4353 context_release(context);
4356 /*****************************************************************************
4357 * IWineD3DSurface::LoadLocation
4359 * Copies the current surface data from wherever it is to the requested
4360 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4361 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4362 * multiple locations, the gl texture is preferred over the drawable, which is
4363 * preferred over system memory. The PBO counts as system memory. If rect is
4364 * not NULL, only the specified rectangle is copied (only supported for
4365 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4366 * location is marked up to date after the copy.
4369 * flag: Surface location flag to be updated
4370 * rect: rectangle to be copied
4373 * WINED3D_OK on success
4374 * WINED3DERR_DEVICELOST on an internal error
4376 *****************************************************************************/
4377 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4378 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4379 IWineD3DDeviceImpl *device = This->resource.device;
4380 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4381 struct wined3d_format_desc desc;
4382 CONVERT_TYPES convert;
4383 int width, pitch, outpitch;
4385 BOOL drawable_read_ok = TRUE;
4386 BOOL in_fbo = FALSE;
4388 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
4390 if (surface_is_offscreen(This))
4392 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4393 * Prefer SFLAG_INTEXTURE. */
4394 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4395 drawable_read_ok = FALSE;
4400 TRACE("Surface %p is an onscreen surface\n", iface);
4404 TRACE("(%p)->(%s, %p)\n", iface, debug_surflocation(flag), rect);
4406 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4409 if(This->Flags & flag) {
4410 TRACE("Location already up to date\n");
4414 if(!(This->Flags & SFLAG_LOCATIONS)) {
4415 ERR("%p: Surface does not have any up to date location\n", This);
4416 This->Flags |= SFLAG_LOST;
4417 return WINED3DERR_DEVICELOST;
4420 if(flag == SFLAG_INSYSMEM) {
4421 surface_prepare_system_memory(This);
4423 /* Download the surface to system memory */
4424 if (This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
4426 struct wined3d_context *context = NULL;
4428 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4430 surface_bind_and_dirtify(This, !(This->Flags & SFLAG_INTEXTURE));
4431 surface_download_data(This, gl_info);
4433 if (context) context_release(context);
4437 /* Note: It might be faster to download into a texture first. */
4438 read_from_framebuffer(This, rect,
4439 This->resource.allocatedMemory,
4440 IWineD3DSurface_GetPitch(iface));
4442 } else if(flag == SFLAG_INDRAWABLE) {
4443 if(This->Flags & SFLAG_INTEXTURE) {
4444 surface_blt_to_drawable(This, rect);
4447 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4448 /* This needs a shader to convert the srgb data sampled from the GL texture into RGB
4449 * values, otherwise we get incorrect values in the target. For now go the slow way
4450 * via a system memory copy
4452 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4455 d3dfmt_get_conv(This, FALSE /* We need color keying */, FALSE /* We won't use textures */, &desc, &convert);
4457 /* The width is in 'length' not in bytes */
4458 width = This->currentDesc.Width;
4459 pitch = IWineD3DSurface_GetPitch(iface);
4461 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4462 * but it isn't set (yet) in all cases it is getting called. */
4463 if ((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO))
4465 struct wined3d_context *context = NULL;
4467 TRACE("Removing the pbo attached to surface %p\n", This);
4469 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4470 surface_remove_pbo(This, gl_info);
4471 if (context) context_release(context);
4474 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4475 int height = This->currentDesc.Height;
4476 byte_count = desc.conv_byte_count;
4478 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4479 outpitch = width * byte_count;
4480 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4482 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4484 ERR("Out of memory %d, %d!\n", outpitch, height);
4485 return WINED3DERR_OUTOFVIDEOMEMORY;
4487 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4489 This->Flags |= SFLAG_CONVERTED;
4491 This->Flags &= ~SFLAG_CONVERTED;
4492 mem = This->resource.allocatedMemory;
4493 byte_count = desc.byte_count;
4496 flush_to_framebuffer_drawpixels(This, desc.glFormat, desc.glType, byte_count, mem);
4498 /* Don't delete PBO memory */
4499 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4500 HeapFree(GetProcessHeap(), 0, mem);
4502 } else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */ {
4503 if (drawable_read_ok && (This->Flags & SFLAG_INDRAWABLE)) {
4504 read_from_framebuffer_texture(This, flag == SFLAG_INSRGBTEX);
4508 /* Upload from system memory */
4509 BOOL srgb = flag == SFLAG_INSRGBTEX;
4510 struct wined3d_context *context = NULL;
4512 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */,
4516 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE) {
4517 /* Performance warning ... */
4518 FIXME("%p: Downloading rgb texture to reload it as srgb\n", This);
4519 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4522 if((This->Flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX) {
4523 /* Performance warning ... */
4524 FIXME("%p: Downloading srgb texture to reload it as rgb\n", This);
4525 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4528 if(!(This->Flags & SFLAG_INSYSMEM)) {
4529 /* Should not happen */
4530 ERR("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set\n");
4531 /* Lets hope we get it from somewhere... */
4532 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4535 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4537 surface_prepare_texture(This, gl_info, srgb);
4538 surface_bind_and_dirtify(This, srgb);
4540 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4541 This->Flags |= SFLAG_GLCKEY;
4542 This->glCKey = This->SrcBltCKey;
4544 else This->Flags &= ~SFLAG_GLCKEY;
4546 /* The width is in 'length' not in bytes */
4547 width = This->currentDesc.Width;
4548 pitch = IWineD3DSurface_GetPitch(iface);
4550 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4551 * but it isn't set (yet) in all cases it is getting called. */
4552 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4553 TRACE("Removing the pbo attached to surface %p\n", This);
4554 surface_remove_pbo(This, gl_info);
4558 /* This code is entered for texture formats which need a fixup. */
4559 int height = This->currentDesc.Height;
4561 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4562 outpitch = width * desc.conv_byte_count;
4563 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4565 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4567 ERR("Out of memory %d, %d!\n", outpitch, height);
4568 if (context) context_release(context);
4569 return WINED3DERR_OUTOFVIDEOMEMORY;
4571 desc.convert(This->resource.allocatedMemory, mem, pitch, width, height);
4572 } else if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4573 /* This code is only entered for color keying fixups */
4574 int height = This->currentDesc.Height;
4576 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4577 outpitch = width * desc.conv_byte_count;
4578 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4580 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4582 ERR("Out of memory %d, %d!\n", outpitch, height);
4583 if (context) context_release(context);
4584 return WINED3DERR_OUTOFVIDEOMEMORY;
4586 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4588 mem = This->resource.allocatedMemory;
4591 /* Make sure the correct pitch is used */
4593 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4596 if (mem || (This->Flags & SFLAG_PBO))
4597 surface_upload_data(This, gl_info, &desc, srgb, mem);
4599 /* Restore the default pitch */
4601 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4604 if (context) context_release(context);
4606 /* Don't delete PBO memory */
4607 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4608 HeapFree(GetProcessHeap(), 0, mem);
4613 This->Flags |= flag;
4616 if (in_fbo && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4617 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4618 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4624 static HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container)
4626 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4627 IWineD3DSwapChain *swapchain = NULL;
4629 /* Update the drawable size method */
4631 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4634 This->get_drawable_size = get_drawable_size_swapchain;
4635 IWineD3DSwapChain_Release(swapchain);
4636 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4637 switch(wined3d_settings.offscreen_rendering_mode) {
4638 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4639 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4643 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4646 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4647 return SURFACE_OPENGL;
4650 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
4651 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4654 /* If there's no destination surface there is nothing to do */
4655 if(!This->overlay_dest) return WINED3D_OK;
4657 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
4658 * update the overlay. Prevent an endless recursion
4660 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
4663 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
4664 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
4665 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
4666 NULL, WINED3DTEXF_LINEAR);
4667 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
4672 BOOL surface_is_offscreen(IWineD3DSurfaceImpl *surface)
4674 IWineD3DSwapChainImpl *swapchain = (IWineD3DSwapChainImpl *)surface->container;
4676 /* Not on a swapchain - must be offscreen */
4677 if (!(surface->Flags & SFLAG_SWAPCHAIN)) return TRUE;
4679 /* The front buffer is always onscreen */
4680 if (surface == swapchain->front_buffer) return FALSE;
4682 /* If the swapchain is rendered to an FBO, the backbuffer is
4683 * offscreen, otherwise onscreen */
4684 return swapchain->render_to_fbo;
4687 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4690 IWineD3DBaseSurfaceImpl_QueryInterface,
4691 IWineD3DBaseSurfaceImpl_AddRef,
4692 IWineD3DSurfaceImpl_Release,
4693 /* IWineD3DResource */
4694 IWineD3DBaseSurfaceImpl_GetParent,
4695 IWineD3DBaseSurfaceImpl_SetPrivateData,
4696 IWineD3DBaseSurfaceImpl_GetPrivateData,
4697 IWineD3DBaseSurfaceImpl_FreePrivateData,
4698 IWineD3DBaseSurfaceImpl_SetPriority,
4699 IWineD3DBaseSurfaceImpl_GetPriority,
4700 IWineD3DSurfaceImpl_PreLoad,
4701 IWineD3DSurfaceImpl_UnLoad,
4702 IWineD3DBaseSurfaceImpl_GetType,
4703 /* IWineD3DSurface */
4704 IWineD3DBaseSurfaceImpl_GetContainer,
4705 IWineD3DBaseSurfaceImpl_GetDesc,
4706 IWineD3DSurfaceImpl_LockRect,
4707 IWineD3DSurfaceImpl_UnlockRect,
4708 IWineD3DSurfaceImpl_GetDC,
4709 IWineD3DSurfaceImpl_ReleaseDC,
4710 IWineD3DSurfaceImpl_Flip,
4711 IWineD3DSurfaceImpl_Blt,
4712 IWineD3DBaseSurfaceImpl_GetBltStatus,
4713 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4714 IWineD3DBaseSurfaceImpl_IsLost,
4715 IWineD3DBaseSurfaceImpl_Restore,
4716 IWineD3DSurfaceImpl_BltFast,
4717 IWineD3DBaseSurfaceImpl_GetPalette,
4718 IWineD3DBaseSurfaceImpl_SetPalette,
4719 IWineD3DSurfaceImpl_RealizePalette,
4720 IWineD3DBaseSurfaceImpl_SetColorKey,
4721 IWineD3DBaseSurfaceImpl_GetPitch,
4722 IWineD3DSurfaceImpl_SetMem,
4723 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4724 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4725 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4726 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4727 IWineD3DBaseSurfaceImpl_SetClipper,
4728 IWineD3DBaseSurfaceImpl_GetClipper,
4730 IWineD3DSurfaceImpl_LoadTexture,
4731 IWineD3DSurfaceImpl_BindTexture,
4732 IWineD3DSurfaceImpl_SaveSnapshot,
4733 IWineD3DSurfaceImpl_SetContainer,
4734 IWineD3DBaseSurfaceImpl_GetData,
4735 IWineD3DSurfaceImpl_SetFormat,
4736 IWineD3DSurfaceImpl_PrivateSetup,
4737 IWineD3DSurfaceImpl_ModifyLocation,
4738 IWineD3DSurfaceImpl_LoadLocation,
4739 IWineD3DSurfaceImpl_GetImplType,
4740 IWineD3DSurfaceImpl_DrawOverlay
4743 static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
4744 /* Context activation is done by the caller. */
4745 static void ffp_blit_free(IWineD3DDevice *iface) { }
4747 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
4748 /* Context activation is done by the caller. */
4749 static void ffp_blit_p8_upload_palette(IWineD3DSurfaceImpl *surface, const struct wined3d_gl_info *gl_info)
4752 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
4754 d3dfmt_p8_init_palette(surface, table, colorkey_active);
4756 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
4758 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
4762 /* Context activation is done by the caller. */
4763 static HRESULT ffp_blit_set(IWineD3DDevice *iface, IWineD3DSurfaceImpl *surface)
4765 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
4766 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4767 enum complex_fixup fixup = get_complex_fixup(surface->resource.format_desc->color_fixup);
4769 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
4770 * else the surface is converted in software at upload time in LoadLocation.
4772 if(fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
4773 ffp_blit_p8_upload_palette(surface, gl_info);
4776 glEnable(surface->texture_target);
4777 checkGLcall("glEnable(surface->texture_target)");
4782 /* Context activation is done by the caller. */
4783 static void ffp_blit_unset(IWineD3DDevice *iface)
4785 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
4786 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4789 glDisable(GL_TEXTURE_2D);
4790 checkGLcall("glDisable(GL_TEXTURE_2D)");
4791 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
4793 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4794 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4796 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
4798 glDisable(GL_TEXTURE_RECTANGLE_ARB);
4799 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4804 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum blit_operation blit_op,
4805 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool,
4806 const struct wined3d_format_desc *src_format_desc,
4807 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool,
4808 const struct wined3d_format_desc *dst_format_desc)
4810 enum complex_fixup src_fixup;
4812 if (blit_op == BLIT_OP_COLOR_FILL)
4814 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4816 TRACE("Color fill not supported\n");
4823 src_fixup = get_complex_fixup(src_format_desc->color_fixup);
4824 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4826 TRACE("Checking support for fixup:\n");
4827 dump_color_fixup_desc(src_format_desc->color_fixup);
4830 if (blit_op != BLIT_OP_BLIT)
4832 TRACE("Unsupported blit_op=%d\n", blit_op);
4836 if (!is_identity_fixup(dst_format_desc->color_fixup))
4838 TRACE("Destination fixups are not supported\n");
4842 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
4844 TRACE("P8 fixup supported\n");
4848 /* We only support identity conversions. */
4849 if (is_identity_fixup(src_format_desc->color_fixup))
4855 TRACE("[FAILED]\n");
4859 static HRESULT ffp_blit_color_fill(IWineD3DDeviceImpl *device, IWineD3DSurfaceImpl *dst_surface, const RECT *dst_rect, DWORD fill_color)
4861 return IWineD3DDeviceImpl_ClearSurface(device, dst_surface, 1 /* Number of rectangles */,
4862 (const WINED3DRECT*)dst_rect, WINED3DCLEAR_TARGET, fill_color, 0.0f /* Z */, 0 /* Stencil */);
4865 const struct blit_shader ffp_blit = {
4874 static HRESULT cpu_blit_alloc(IWineD3DDevice *iface)
4879 /* Context activation is done by the caller. */
4880 static void cpu_blit_free(IWineD3DDevice *iface)
4884 /* Context activation is done by the caller. */
4885 static HRESULT cpu_blit_set(IWineD3DDevice *iface, IWineD3DSurfaceImpl *surface)
4890 /* Context activation is done by the caller. */
4891 static void cpu_blit_unset(IWineD3DDevice *iface)
4895 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum blit_operation blit_op,
4896 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool,
4897 const struct wined3d_format_desc *src_format_desc,
4898 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool,
4899 const struct wined3d_format_desc *dst_format_desc)
4901 if (blit_op == BLIT_OP_COLOR_FILL)
4909 static HRESULT cpu_blit_color_fill(IWineD3DDeviceImpl *device, IWineD3DSurfaceImpl *dst_surface, const RECT *dst_rect, DWORD fill_color)
4912 memset(&BltFx, 0, sizeof(BltFx));
4913 BltFx.dwSize = sizeof(BltFx);
4914 BltFx.u5.dwFillColor = color_convert_argb_to_fmt(fill_color, dst_surface->resource.format_desc->format);
4915 return IWineD3DBaseSurfaceImpl_Blt((IWineD3DSurface*)dst_surface, dst_rect, NULL, NULL, WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
4918 const struct blit_shader cpu_blit = {
4927 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum blit_operation blit_op,
4928 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool,
4929 const struct wined3d_format_desc *src_format_desc,
4930 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool,
4931 const struct wined3d_format_desc *dst_format_desc)
4933 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
4936 /* We only support blitting. Things like color keying / color fill should
4937 * be handled by other blitters.
4939 if (blit_op != BLIT_OP_BLIT)
4942 /* Source and/or destination need to be on the GL side */
4943 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
4946 if(!((src_format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET))
4947 && ((dst_format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
4950 if (!is_identity_fixup(src_format_desc->color_fixup) ||
4951 !is_identity_fixup(dst_format_desc->color_fixup))
4954 if (!(src_format_desc->format == dst_format_desc->format
4955 || (is_identity_fixup(src_format_desc->color_fixup)
4956 && is_identity_fixup(dst_format_desc->color_fixup))))