2 * IWineD3DSurface Implementation
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006-2008 Stefan Dösinger for CodeWeavers
11 * Copyright 2007-2008 Henri Verbeet
12 * Copyright 2006-2008 Roderick Colenbrander
13 * Copyright 2009 Henri Verbeet for CodeWeavers
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wine/port.h"
32 #include "wined3d_private.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(d3d_surface);
35 WINE_DECLARE_DEBUG_CHANNEL(d3d);
37 #define GLINFO_LOCATION (*gl_info)
39 static void surface_cleanup(IWineD3DSurfaceImpl *This)
41 IWineD3DDeviceImpl *device = This->resource.device;
42 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
43 struct wined3d_context *context = NULL;
44 renderbuffer_entry_t *entry, *entry2;
46 TRACE("(%p) : Cleaning up.\n", This);
48 /* Need a context to destroy the texture. Use the currently active render
49 * target, but only if the primary render target exists. Otherwise
50 * lastActiveRenderTarget is garbage. When destroying the primary render
51 * target, Uninit3D() will activate a context before doing anything. */
52 if (device->render_targets && device->render_targets[0])
54 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
59 if (This->texture_name)
61 /* Release the OpenGL texture. */
62 TRACE("Deleting texture %u.\n", This->texture_name);
63 glDeleteTextures(1, &This->texture_name);
66 if (This->Flags & SFLAG_PBO)
69 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
72 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry)
74 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
75 HeapFree(GetProcessHeap(), 0, entry);
80 if (This->Flags & SFLAG_DIBSECTION)
83 SelectObject(This->hDC, This->dib.holdbitmap);
85 /* Release the DIB section. */
86 DeleteObject(This->dib.DIBsection);
87 This->dib.bitmap_data = NULL;
88 This->resource.allocatedMemory = NULL;
91 if (This->Flags & SFLAG_USERPTR) IWineD3DSurface_SetMem((IWineD3DSurface *)This, NULL);
92 if (This->overlay_dest) list_remove(&This->overlay_entry);
94 HeapFree(GetProcessHeap(), 0, This->palette9);
96 resource_cleanup((IWineD3DResource *)This);
98 if (context) context_release(context);
101 UINT surface_calculate_size(const struct wined3d_format_desc *format_desc, UINT alignment, UINT width, UINT height)
105 if (format_desc->format == WINED3DFMT_UNKNOWN)
109 else if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
111 UINT row_block_count = (width + format_desc->block_width - 1) / format_desc->block_width;
112 UINT row_count = (height + format_desc->block_height - 1) / format_desc->block_height;
113 size = row_count * row_block_count * format_desc->block_byte_count;
117 /* The pitch is a multiple of 4 bytes. */
118 size = height * (((width * format_desc->byte_count) + alignment - 1) & ~(alignment - 1));
121 if (format_desc->heightscale != 0.0f) size *= format_desc->heightscale;
130 enum tex_types tex_type;
131 GLfloat coords[4][3];
142 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
144 f->l = ((r->left * 2.0f) / w) - 1.0f;
145 f->t = ((r->top * 2.0f) / h) - 1.0f;
146 f->r = ((r->right * 2.0f) / w) - 1.0f;
147 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
150 static void surface_get_blt_info(GLenum target, const RECT *rect_in, GLsizei w, GLsizei h, struct blt_info *info)
152 GLfloat (*coords)[3] = info->coords;
169 FIXME("Unsupported texture target %#x\n", target);
170 /* Fall back to GL_TEXTURE_2D */
172 info->binding = GL_TEXTURE_BINDING_2D;
173 info->bind_target = GL_TEXTURE_2D;
174 info->tex_type = tex_2d;
175 coords[0][0] = (float)rect.left / w;
176 coords[0][1] = (float)rect.top / h;
179 coords[1][0] = (float)rect.right / w;
180 coords[1][1] = (float)rect.top / h;
183 coords[2][0] = (float)rect.left / w;
184 coords[2][1] = (float)rect.bottom / h;
187 coords[3][0] = (float)rect.right / w;
188 coords[3][1] = (float)rect.bottom / h;
192 case GL_TEXTURE_RECTANGLE_ARB:
193 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
194 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
195 info->tex_type = tex_rect;
196 coords[0][0] = rect.left; coords[0][1] = rect.top; coords[0][2] = 0.0f;
197 coords[1][0] = rect.right; coords[1][1] = rect.top; coords[1][2] = 0.0f;
198 coords[2][0] = rect.left; coords[2][1] = rect.bottom; coords[2][2] = 0.0f;
199 coords[3][0] = rect.right; coords[3][1] = rect.bottom; coords[3][2] = 0.0f;
202 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
203 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
204 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
205 info->tex_type = tex_cube;
206 cube_coords_float(&rect, w, h, &f);
208 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
209 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
210 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
211 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
214 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
215 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
216 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
217 info->tex_type = tex_cube;
218 cube_coords_float(&rect, w, h, &f);
220 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
221 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
222 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
223 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
226 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
227 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
228 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
229 info->tex_type = tex_cube;
230 cube_coords_float(&rect, w, h, &f);
232 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
233 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
234 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
235 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
238 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
239 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
240 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
241 info->tex_type = tex_cube;
242 cube_coords_float(&rect, w, h, &f);
244 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
245 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
246 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
247 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
250 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
251 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
252 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
253 info->tex_type = tex_cube;
254 cube_coords_float(&rect, w, h, &f);
256 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
257 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
258 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
259 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
262 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
263 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
264 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
265 info->tex_type = tex_cube;
266 cube_coords_float(&rect, w, h, &f);
268 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
269 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
270 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
271 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
276 static inline void surface_get_rect(IWineD3DSurfaceImpl *This, const RECT *rect_in, RECT *rect_out)
279 *rect_out = *rect_in;
284 rect_out->right = This->currentDesc.Width;
285 rect_out->bottom = This->currentDesc.Height;
289 /* GL locking and context activation is done by the caller */
290 void draw_textured_quad(IWineD3DSurfaceImpl *src_surface, const RECT *src_rect, const RECT *dst_rect, WINED3DTEXTUREFILTERTYPE Filter)
292 IWineD3DBaseTextureImpl *texture;
293 struct blt_info info;
295 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
297 glEnable(info.bind_target);
298 checkGLcall("glEnable(bind_target)");
300 /* Bind the texture */
301 glBindTexture(info.bind_target, src_surface->texture_name);
302 checkGLcall("glBindTexture");
304 /* Filtering for StretchRect */
305 glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
306 wined3d_gl_mag_filter(magLookup, Filter));
307 checkGLcall("glTexParameteri");
308 glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
309 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
310 checkGLcall("glTexParameteri");
311 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
312 glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
313 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
314 checkGLcall("glTexEnvi");
317 glBegin(GL_TRIANGLE_STRIP);
318 glTexCoord3fv(info.coords[0]);
319 glVertex2i(dst_rect->left, dst_rect->top);
321 glTexCoord3fv(info.coords[1]);
322 glVertex2i(dst_rect->right, dst_rect->top);
324 glTexCoord3fv(info.coords[2]);
325 glVertex2i(dst_rect->left, dst_rect->bottom);
327 glTexCoord3fv(info.coords[3]);
328 glVertex2i(dst_rect->right, dst_rect->bottom);
331 /* Unbind the texture */
332 glBindTexture(info.bind_target, 0);
333 checkGLcall("glBindTexture(info->bind_target, 0)");
335 /* We changed the filtering settings on the texture. Inform the
336 * container about this to get the filters reset properly next draw. */
337 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)src_surface, &IID_IWineD3DBaseTexture, (void **)&texture)))
339 texture->baseTexture.texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
340 texture->baseTexture.texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
341 texture->baseTexture.texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
342 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *)texture);
346 HRESULT surface_init(IWineD3DSurfaceImpl *surface, WINED3DSURFTYPE surface_type, UINT alignment,
347 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
348 UINT multisample_quality, IWineD3DDeviceImpl *device, DWORD usage, WINED3DFORMAT format,
349 WINED3DPOOL pool, IUnknown *parent, const struct wined3d_parent_ops *parent_ops)
351 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
352 const struct wined3d_format_desc *format_desc = getFormatDescEntry(format, gl_info);
353 void (*cleanup)(IWineD3DSurfaceImpl *This);
354 unsigned int resource_size;
357 if (multisample_quality > 0)
359 FIXME("multisample_quality set to %u, substituting 0\n", multisample_quality);
360 multisample_quality = 0;
363 /* FIXME: Check that the format is supported by the device. */
365 resource_size = surface_calculate_size(format_desc, alignment, width, height);
367 /* Look at the implementation and set the correct Vtable. */
368 switch (surface_type)
371 surface->lpVtbl = &IWineD3DSurface_Vtbl;
372 cleanup = surface_cleanup;
376 surface->lpVtbl = &IWineGDISurface_Vtbl;
377 cleanup = surface_gdi_cleanup;
381 ERR("Requested unknown surface implementation %#x.\n", surface_type);
382 return WINED3DERR_INVALIDCALL;
385 hr = resource_init((IWineD3DResource *)surface, WINED3DRTYPE_SURFACE,
386 device, resource_size, usage, format_desc, pool, parent, parent_ops);
389 WARN("Failed to initialize resource, returning %#x.\n", hr);
393 /* "Standalone" surface. */
394 IWineD3DSurface_SetContainer((IWineD3DSurface *)surface, NULL);
396 surface->currentDesc.Width = width;
397 surface->currentDesc.Height = height;
398 surface->currentDesc.MultiSampleType = multisample_type;
399 surface->currentDesc.MultiSampleQuality = multisample_quality;
400 surface->texture_level = level;
401 list_init(&surface->overlays);
404 surface->Flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
405 if (discard) surface->Flags |= SFLAG_DISCARD;
406 if (lockable || format == WINED3DFMT_D16_LOCKABLE) surface->Flags |= SFLAG_LOCKABLE;
408 /* Quick lockable sanity check.
409 * TODO: remove this after surfaces, usage and lockability have been debugged properly
410 * this function is too deep to need to care about things like this.
411 * Levels need to be checked too, since they all affect what can be done. */
414 case WINED3DPOOL_SCRATCH:
417 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
418 "which are mutually exclusive, setting lockable to TRUE.\n");
423 case WINED3DPOOL_SYSTEMMEM:
425 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
428 case WINED3DPOOL_MANAGED:
429 if (usage & WINED3DUSAGE_DYNAMIC)
430 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
433 case WINED3DPOOL_DEFAULT:
434 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
435 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
439 FIXME("Unknown pool %#x.\n", pool);
443 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
445 FIXME("Trying to create a render target that isn't in the default pool.\n");
448 /* Mark the texture as dirty so that it gets loaded first time around. */
449 surface_add_dirty_rect((IWineD3DSurface *)surface, NULL);
450 list_init(&surface->renderbuffers);
452 TRACE("surface %p, memory %p, size %u\n", surface, surface->resource.allocatedMemory, surface->resource.size);
454 /* Call the private setup routine */
455 hr = IWineD3DSurface_PrivateSetup((IWineD3DSurface *)surface);
458 ERR("Private setup failed, returning %#x\n", hr);
466 static void surface_force_reload(IWineD3DSurface *iface)
468 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
470 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
473 void surface_set_texture_name(IWineD3DSurface *iface, GLuint new_name, BOOL srgb)
475 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
481 name = &This->texture_name_srgb;
482 flag = SFLAG_INSRGBTEX;
486 name = &This->texture_name;
487 flag = SFLAG_INTEXTURE;
490 TRACE("(%p) : setting texture name %u\n", This, new_name);
492 if (!*name && new_name)
494 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
495 * surface has no texture name yet. See if we can get rid of this. */
496 if (This->Flags & flag)
497 ERR("Surface has SFLAG_INTEXTURE set, but no texture name\n");
498 IWineD3DSurface_ModifyLocation(iface, flag, FALSE);
502 surface_force_reload(iface);
505 void surface_set_texture_target(IWineD3DSurface *iface, GLenum target)
507 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
509 TRACE("(%p) : setting target %#x\n", This, target);
511 if (This->texture_target != target)
513 if (target == GL_TEXTURE_RECTANGLE_ARB)
515 This->Flags &= ~SFLAG_NORMCOORD;
517 else if (This->texture_target == GL_TEXTURE_RECTANGLE_ARB)
519 This->Flags |= SFLAG_NORMCOORD;
522 This->texture_target = target;
523 surface_force_reload(iface);
526 /* Context activation is done by the caller. */
527 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This, BOOL srgb) {
528 DWORD active_sampler;
530 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
531 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
532 * gl states. The current texture unit should always be a valid one.
534 * To be more specific, this is tricky because we can implicitly be called
535 * from sampler() in state.c. This means we can't touch anything other than
536 * whatever happens to be the currently active texture, or we would risk
537 * marking already applied sampler states dirty again.
539 * TODO: Track the current active texture per GL context instead of using glGet
541 GLint active_texture;
543 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
545 active_sampler = This->resource.device->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
547 if (active_sampler != WINED3D_UNMAPPED_STAGE)
549 IWineD3DDeviceImpl_MarkStateDirty(This->resource.device, STATE_SAMPLER(active_sampler));
551 IWineD3DSurface_BindTexture((IWineD3DSurface *)This, srgb);
554 /* This function checks if the primary render target uses the 8bit paletted format. */
555 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
557 if (device->render_targets && device->render_targets[0]) {
558 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
559 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
560 && (render_target->resource.format_desc->format == WINED3DFMT_P8_UINT))
566 /* This call just downloads data, the caller is responsible for binding the
567 * correct texture. */
568 /* Context activation is done by the caller. */
569 static void surface_download_data(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info)
571 const struct wined3d_format_desc *format_desc = This->resource.format_desc;
573 /* Only support read back of converted P8 surfaces */
574 if (This->Flags & SFLAG_CONVERTED && format_desc->format != WINED3DFMT_P8_UINT)
576 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(format_desc->format));
582 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
584 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
585 This, This->texture_level, format_desc->glFormat, format_desc->glType,
586 This->resource.allocatedMemory);
588 if (This->Flags & SFLAG_PBO)
590 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
591 checkGLcall("glBindBufferARB");
592 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target, This->texture_level, NULL));
593 checkGLcall("glGetCompressedTexImageARB");
594 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
595 checkGLcall("glBindBufferARB");
599 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target,
600 This->texture_level, This->resource.allocatedMemory));
601 checkGLcall("glGetCompressedTexImageARB");
607 GLenum format = format_desc->glFormat;
608 GLenum type = format_desc->glType;
612 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
613 if (format_desc->format == WINED3DFMT_P8_UINT && primary_render_target_is_p8(This->resource.device))
616 type = GL_UNSIGNED_BYTE;
619 if (This->Flags & SFLAG_NONPOW2) {
620 unsigned char alignment = This->resource.device->surface_alignment;
621 src_pitch = format_desc->byte_count * This->pow2Width;
622 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
623 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
624 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
626 mem = This->resource.allocatedMemory;
629 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
630 This, This->texture_level, format, type, mem);
632 if(This->Flags & SFLAG_PBO) {
633 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
634 checkGLcall("glBindBufferARB");
636 glGetTexImage(This->texture_target, This->texture_level, format, type, NULL);
637 checkGLcall("glGetTexImage");
639 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
640 checkGLcall("glBindBufferARB");
642 glGetTexImage(This->texture_target, This->texture_level, format, type, mem);
643 checkGLcall("glGetTexImage");
647 if (This->Flags & SFLAG_NONPOW2) {
648 const BYTE *src_data;
652 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
653 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
654 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
656 * We're doing this...
658 * instead of boxing the texture :
659 * |<-texture width ->| -->pow2width| /\
660 * |111111111111111111| | |
661 * |222 Texture 222222| boxed empty | texture height
662 * |3333 Data 33333333| | |
663 * |444444444444444444| | \/
664 * ----------------------------------- |
665 * | boxed empty | boxed empty | pow2height
667 * -----------------------------------
670 * we're repacking the data to the expected texture width
672 * |<-texture width ->| -->pow2width| /\
673 * |111111111111111111222222222222222| |
674 * |222333333333333333333444444444444| texture height
678 * | empty | pow2height
680 * -----------------------------------
684 * |<-texture width ->| /\
685 * |111111111111111111|
686 * |222222222222222222|texture height
687 * |333333333333333333|
688 * |444444444444444444| \/
689 * --------------------
691 * this also means that any references to allocatedMemory should work with the data as if were a
692 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
694 * internally the texture is still stored in a boxed format so any references to textureName will
695 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
697 * Performance should not be an issue, because applications normally do not lock the surfaces when
698 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
699 * and doesn't have to be re-read.
702 dst_data = This->resource.allocatedMemory;
703 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
704 for (y = 1 ; y < This->currentDesc.Height; y++) {
705 /* skip the first row */
706 src_data += src_pitch;
707 dst_data += dst_pitch;
708 memcpy(dst_data, src_data, dst_pitch);
711 HeapFree(GetProcessHeap(), 0, mem);
715 /* Surface has now been downloaded */
716 This->Flags |= SFLAG_INSYSMEM;
719 /* This call just uploads data, the caller is responsible for binding the
720 * correct texture. */
721 /* Context activation is done by the caller. */
722 static void surface_upload_data(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info,
723 const struct wined3d_format_desc *format_desc, BOOL srgb, GLsizei width, GLsizei height, const GLvoid *data)
729 internal = format_desc->glGammaInternal;
731 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET
732 && surface_is_offscreen((IWineD3DSurface *)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, GLsizei width, GLsizei height)
802 BOOL enable_client_storage = FALSE;
803 const BYTE *mem = NULL;
808 internal = format_desc->glGammaInternal;
810 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET
811 && surface_is_offscreen((IWineD3DSurface *)This))
813 internal = format_desc->rtInternal;
817 internal = format_desc->glInternal;
820 if (format_desc->heightscale != 1.0f && format_desc->heightscale != 0.0f) height *= format_desc->heightscale;
822 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",
823 This, This->texture_target, This->texture_level, debug_d3dformat(format_desc->format),
824 internal, width, height, format_desc->glFormat, format_desc->glType);
828 if (gl_info->supported[APPLE_CLIENT_STORAGE])
830 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
831 /* In some cases we want to disable client storage.
832 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
833 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
834 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
835 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
837 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
838 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
839 This->Flags &= ~SFLAG_CLIENT;
840 enable_client_storage = TRUE;
842 This->Flags |= SFLAG_CLIENT;
844 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
845 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
847 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
851 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED && mem)
853 GL_EXTCALL(glCompressedTexImage2DARB(This->texture_target, This->texture_level,
854 internal, width, height, 0, This->resource.size, mem));
858 glTexImage2D(This->texture_target, This->texture_level,
859 internal, width, height, 0, format_desc->glFormat, format_desc->glType, mem);
860 checkGLcall("glTexImage2D");
863 if(enable_client_storage) {
864 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
865 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
870 /* In D3D the depth stencil dimensions have to be greater than or equal to the
871 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
872 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
873 /* GL locking is done by the caller */
874 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
875 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
876 const struct wined3d_gl_info *gl_info = &This->resource.device->adapter->gl_info;
877 renderbuffer_entry_t *entry;
878 GLuint renderbuffer = 0;
879 unsigned int src_width, src_height;
881 src_width = This->pow2Width;
882 src_height = This->pow2Height;
884 /* A depth stencil smaller than the render target is not valid */
885 if (width > src_width || height > src_height) return;
887 /* Remove any renderbuffer set if the sizes match */
888 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
889 || (width == src_width && height == src_height))
891 This->current_renderbuffer = NULL;
895 /* Look if we've already got a renderbuffer of the correct dimensions */
896 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
897 if (entry->width == width && entry->height == height) {
898 renderbuffer = entry->id;
899 This->current_renderbuffer = entry;
905 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
906 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
907 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
908 This->resource.format_desc->glInternal, width, height);
910 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
911 entry->width = width;
912 entry->height = height;
913 entry->id = renderbuffer;
914 list_add_head(&This->renderbuffers, &entry->entry);
916 This->current_renderbuffer = entry;
919 checkGLcall("set_compatible_renderbuffer");
922 GLenum surface_get_gl_buffer(IWineD3DSurface *iface)
924 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
925 IWineD3DSwapChainImpl *swapchain = (IWineD3DSwapChainImpl *)This->container;
927 TRACE("iface %p.\n", iface);
929 if (!(This->Flags & SFLAG_SWAPCHAIN))
931 ERR("Surface %p is not on a swapchain.\n", iface);
935 if (swapchain->backBuffer && swapchain->backBuffer[0] == iface)
937 if (swapchain->render_to_fbo)
939 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
940 return GL_COLOR_ATTACHMENT0;
942 TRACE("Returning GL_BACK\n");
945 else if (swapchain->frontBuffer == iface)
947 TRACE("Returning GL_FRONT\n");
951 FIXME("Higher back buffer, returning GL_BACK\n");
955 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
956 void surface_add_dirty_rect(IWineD3DSurface *iface, const RECT *dirty_rect)
958 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
959 IWineD3DBaseTexture *baseTexture = NULL;
961 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
962 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
964 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
967 This->dirtyRect.left = min(This->dirtyRect.left, dirty_rect->left);
968 This->dirtyRect.top = min(This->dirtyRect.top, dirty_rect->top);
969 This->dirtyRect.right = max(This->dirtyRect.right, dirty_rect->right);
970 This->dirtyRect.bottom = max(This->dirtyRect.bottom, dirty_rect->bottom);
974 This->dirtyRect.left = 0;
975 This->dirtyRect.top = 0;
976 This->dirtyRect.right = This->currentDesc.Width;
977 This->dirtyRect.bottom = This->currentDesc.Height;
980 TRACE("(%p) : Dirty: yes, Rect:(%d, %d, %d, %d)\n", This, This->dirtyRect.left,
981 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
983 /* if the container is a basetexture then mark it dirty. */
984 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture)))
986 TRACE("Passing to container\n");
987 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
988 IWineD3DBaseTexture_Release(baseTexture);
992 static BOOL surface_convert_color_to_argb(IWineD3DSurfaceImpl *This, DWORD color, DWORD *argb_color)
994 IWineD3DDeviceImpl *device = This->resource.device;
996 switch(This->resource.format_desc->format)
998 case WINED3DFMT_P8_UINT:
1002 if (primary_render_target_is_p8(device))
1003 alpha = color << 24;
1007 if (This->palette) {
1008 *argb_color = (alpha |
1009 (This->palette->palents[color].peRed << 16) |
1010 (This->palette->palents[color].peGreen << 8) |
1011 (This->palette->palents[color].peBlue));
1013 *argb_color = alpha;
1018 case WINED3DFMT_B5G6R5_UNORM:
1020 if (color == 0xFFFF) {
1021 *argb_color = 0xFFFFFFFF;
1023 *argb_color = ((0xFF000000) |
1024 ((color & 0xF800) << 8) |
1025 ((color & 0x07E0) << 5) |
1026 ((color & 0x001F) << 3));
1031 case WINED3DFMT_B8G8R8_UNORM:
1032 case WINED3DFMT_B8G8R8X8_UNORM:
1033 *argb_color = 0xFF000000 | color;
1036 case WINED3DFMT_B8G8R8A8_UNORM:
1037 *argb_color = color;
1041 ERR("Unhandled conversion from %s to ARGB!\n", debug_d3dformat(This->resource.format_desc->format));
1047 static ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface)
1049 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1050 ULONG ref = InterlockedDecrement(&This->resource.ref);
1051 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
1055 surface_cleanup(This);
1056 This->resource.parent_ops->wined3d_object_destroyed(This->resource.parent);
1058 TRACE("(%p) Released.\n", This);
1059 HeapFree(GetProcessHeap(), 0, This);
1065 /* ****************************************************
1066 IWineD3DSurface IWineD3DResource parts follow
1067 **************************************************** */
1069 void surface_internal_preload(IWineD3DSurface *iface, enum WINED3DSRGB srgb)
1071 /* TODO: check for locks */
1072 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1073 IWineD3DDeviceImpl *device = This->resource.device;
1074 IWineD3DBaseTexture *baseTexture = NULL;
1076 TRACE("(%p)Checking to see if the container is a base texture\n", This);
1077 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
1078 IWineD3DBaseTextureImpl *tex_impl = (IWineD3DBaseTextureImpl *) baseTexture;
1079 TRACE("Passing to container\n");
1080 tex_impl->baseTexture.internal_preload(baseTexture, srgb);
1081 IWineD3DBaseTexture_Release(baseTexture);
1083 struct wined3d_context *context = NULL;
1085 TRACE("(%p) : About to load surface\n", This);
1087 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
1089 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
1090 || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
1092 if(palette9_changed(This)) {
1093 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
1094 /* TODO: This is not necessarily needed with hw palettized texture support */
1095 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
1096 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
1097 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
1101 IWineD3DSurface_LoadTexture(iface, srgb == SRGB_SRGB ? TRUE : FALSE);
1103 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
1104 /* Tell opengl to try and keep this texture in video ram (well mostly) */
1108 glPrioritizeTextures(1, &This->texture_name, &tmp);
1112 if (context) context_release(context);
1116 static void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
1117 surface_internal_preload(iface, SRGB_ANY);
1120 /* Context activation is done by the caller. */
1121 static void surface_remove_pbo(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info)
1123 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1124 This->resource.allocatedMemory =
1125 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1128 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1129 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
1130 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
1131 checkGLcall("glGetBufferSubDataARB");
1132 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
1133 checkGLcall("glDeleteBuffersARB");
1137 This->Flags &= ~SFLAG_PBO;
1140 BOOL surface_init_sysmem(IWineD3DSurface *iface)
1142 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
1144 if(!This->resource.allocatedMemory)
1146 This->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->resource.size + RESOURCE_ALIGNMENT);
1147 if(!This->resource.heapMemory)
1149 ERR("Out of memory\n");
1152 This->resource.allocatedMemory =
1153 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1157 memset(This->resource.allocatedMemory, 0, This->resource.size);
1160 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
1164 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
1165 IWineD3DBaseTexture *texture = NULL;
1166 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
1167 IWineD3DDeviceImpl *device = This->resource.device;
1168 const struct wined3d_gl_info *gl_info;
1169 renderbuffer_entry_t *entry, *entry2;
1170 struct wined3d_context *context;
1172 TRACE("(%p)\n", iface);
1174 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
1175 /* Default pool resources are supposed to be destroyed before Reset is called.
1176 * Implicit resources stay however. So this means we have an implicit render target
1177 * or depth stencil. The content may be destroyed, but we still have to tear down
1178 * opengl resources, so we cannot leave early.
1180 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1181 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1182 * or the depth stencil into an FBO the texture or render buffer will be removed
1183 * and all flags get lost
1185 surface_init_sysmem(iface);
1187 /* Load the surface into system memory */
1188 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
1189 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
1191 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
1192 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSRGBTEX, FALSE);
1193 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1195 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
1196 gl_info = context->gl_info;
1198 /* Destroy PBOs, but load them into real sysmem before */
1199 if (This->Flags & SFLAG_PBO)
1200 surface_remove_pbo(This, gl_info);
1202 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1203 * all application-created targets the application has to release the surface
1204 * before calling _Reset
1206 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
1208 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1210 list_remove(&entry->entry);
1211 HeapFree(GetProcessHeap(), 0, entry);
1213 list_init(&This->renderbuffers);
1214 This->current_renderbuffer = NULL;
1216 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
1219 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
1222 glDeleteTextures(1, &This->texture_name);
1223 This->texture_name = 0;
1224 glDeleteTextures(1, &This->texture_name_srgb);
1225 This->texture_name_srgb = 0;
1228 IWineD3DBaseTexture_Release(texture);
1231 context_release(context);
1234 /* ******************************************************
1235 IWineD3DSurface IWineD3DSurface parts follow
1236 ****************************************************** */
1238 /* Read the framebuffer back into the surface */
1239 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, const RECT *rect, void *dest, UINT pitch)
1241 IWineD3DDeviceImpl *myDevice = This->resource.device;
1242 const struct wined3d_gl_info *gl_info;
1243 struct wined3d_context *context;
1247 BYTE *row, *top, *bottom;
1251 BOOL srcIsUpsideDown;
1256 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1257 static BOOL warned = FALSE;
1259 ERR("The application tries to lock the render target, but render target locking is disabled\n");
1265 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
1266 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
1267 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
1268 * context->last_was_blit set on the unlock.
1270 context = context_acquire(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1271 gl_info = context->gl_info;
1275 /* Select the correct read buffer, and give some debug output.
1276 * There is no need to keep track of the current read buffer or reset it, every part of the code
1277 * that reads sets the read buffer as desired.
1279 if (surface_is_offscreen((IWineD3DSurface *) This))
1281 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1282 * Read from the back buffer
1284 TRACE("Locking offscreen render target\n");
1285 glReadBuffer(myDevice->offscreenBuffer);
1286 srcIsUpsideDown = TRUE;
1290 /* Onscreen surfaces are always part of a swapchain */
1291 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This);
1292 TRACE("Locking %#x buffer\n", buffer);
1293 glReadBuffer(buffer);
1294 checkGLcall("glReadBuffer");
1295 srcIsUpsideDown = FALSE;
1298 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
1300 local_rect.left = 0;
1302 local_rect.right = This->currentDesc.Width;
1303 local_rect.bottom = This->currentDesc.Height;
1307 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
1309 switch(This->resource.format_desc->format)
1311 case WINED3DFMT_P8_UINT:
1313 if(primary_render_target_is_p8(myDevice)) {
1314 /* In case of P8 render targets the index is stored in the alpha component */
1316 type = GL_UNSIGNED_BYTE;
1318 bpp = This->resource.format_desc->byte_count;
1320 /* GL can't return palettized data, so read ARGB pixels into a
1321 * separate block of memory and convert them into palettized format
1322 * in software. Slow, but if the app means to use palettized render
1323 * targets and locks it...
1325 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
1326 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
1327 * for the color channels when palettizing the colors.
1330 type = GL_UNSIGNED_BYTE;
1332 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
1334 ERR("Out of memory\n");
1338 bpp = This->resource.format_desc->byte_count * 3;
1345 fmt = This->resource.format_desc->glFormat;
1346 type = This->resource.format_desc->glType;
1347 bpp = This->resource.format_desc->byte_count;
1350 if(This->Flags & SFLAG_PBO) {
1351 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
1352 checkGLcall("glBindBufferARB");
1354 ERR("mem not null for pbo -- unexpected\n");
1359 /* Save old pixel store pack state */
1360 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
1361 checkGLcall("glGetIntegerv");
1362 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
1363 checkGLcall("glGetIntegerv");
1364 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
1365 checkGLcall("glGetIntegerv");
1367 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1368 glPixelStorei(GL_PACK_ROW_LENGTH, This->currentDesc.Width);
1369 checkGLcall("glPixelStorei");
1370 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
1371 checkGLcall("glPixelStorei");
1372 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
1373 checkGLcall("glPixelStorei");
1375 glReadPixels(local_rect.left, (!srcIsUpsideDown) ? (This->currentDesc.Height - local_rect.bottom) : local_rect.top ,
1376 local_rect.right - local_rect.left,
1377 local_rect.bottom - local_rect.top,
1379 checkGLcall("glReadPixels");
1381 /* Reset previous pixel store pack state */
1382 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
1383 checkGLcall("glPixelStorei");
1384 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
1385 checkGLcall("glPixelStorei");
1386 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
1387 checkGLcall("glPixelStorei");
1389 if(This->Flags & SFLAG_PBO) {
1390 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1391 checkGLcall("glBindBufferARB");
1393 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
1394 * to get a pointer to it and perform the flipping in software. This is a lot
1395 * faster than calling glReadPixels for each line. In case we want more speed
1396 * we should rerender it flipped in a FBO and read the data back from the FBO. */
1397 if(!srcIsUpsideDown) {
1398 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1399 checkGLcall("glBindBufferARB");
1401 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1402 checkGLcall("glMapBufferARB");
1406 /* TODO: Merge this with the palettization loop below for P8 targets */
1407 if(!srcIsUpsideDown) {
1409 /* glReadPixels returns the image upside down, and there is no way to prevent this.
1410 Flip the lines in software */
1411 len = (local_rect.right - local_rect.left) * bpp;
1412 off = local_rect.left * bpp;
1414 row = HeapAlloc(GetProcessHeap(), 0, len);
1416 ERR("Out of memory\n");
1417 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT) HeapFree(GetProcessHeap(), 0, mem);
1422 top = mem + pitch * local_rect.top;
1423 bottom = mem + pitch * (local_rect.bottom - 1);
1424 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
1425 memcpy(row, top + off, len);
1426 memcpy(top + off, bottom + off, len);
1427 memcpy(bottom + off, row, len);
1431 HeapFree(GetProcessHeap(), 0, row);
1433 /* Unmap the temp PBO buffer */
1434 if(This->Flags & SFLAG_PBO) {
1435 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1436 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1441 context_release(context);
1443 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
1444 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
1445 * the same color but we have no choice.
1446 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
1448 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(myDevice))
1450 const PALETTEENTRY *pal = NULL;
1451 DWORD width = pitch / 3;
1455 pal = This->palette->palents;
1457 ERR("Palette is missing, cannot perform inverse palette lookup\n");
1458 HeapFree(GetProcessHeap(), 0, mem);
1462 for(y = local_rect.top; y < local_rect.bottom; y++) {
1463 for(x = local_rect.left; x < local_rect.right; x++) {
1464 /* start lines pixels */
1465 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
1466 const BYTE *green = blue + 1;
1467 const BYTE *red = green + 1;
1469 for(c = 0; c < 256; c++) {
1470 if(*red == pal[c].peRed &&
1471 *green == pal[c].peGreen &&
1472 *blue == pal[c].peBlue)
1474 *((BYTE *) dest + y * width + x) = c;
1480 HeapFree(GetProcessHeap(), 0, mem);
1484 /* Read the framebuffer contents into a texture */
1485 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This, BOOL srgb)
1487 IWineD3DDeviceImpl *device = This->resource.device;
1488 const struct wined3d_gl_info *gl_info;
1489 struct wined3d_context *context;
1490 struct wined3d_format_desc desc;
1491 CONVERT_TYPES convert;
1493 BOOL alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
1495 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &desc, &convert);
1497 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
1498 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
1499 * states in the stateblock, and no driver was found yet that had bugs in that regard.
1501 context = context_acquire(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
1502 gl_info = context->gl_info;
1504 surface_bind_and_dirtify(This, srgb);
1507 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1510 /* Select the correct read buffer, and give some debug output.
1511 * There is no need to keep track of the current read buffer or reset it, every part of the code
1512 * that reads sets the read buffer as desired.
1514 if (!surface_is_offscreen((IWineD3DSurface *)This))
1516 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This);
1517 TRACE("Locking %#x buffer\n", buffer);
1520 glReadBuffer(buffer);
1521 checkGLcall("glReadBuffer");
1526 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1527 * Read from the back buffer
1529 TRACE("Locking offscreen render target\n");
1531 glReadBuffer(device->offscreenBuffer);
1532 checkGLcall("glReadBuffer");
1536 if (!(This->Flags & alloc_flag))
1538 surface_allocate_surface(This, gl_info, &desc, srgb, This->pow2Width, This->pow2Height);
1539 This->Flags |= alloc_flag;
1543 /* If !SrcIsUpsideDown we should flip the surface.
1544 * This can be done using glCopyTexSubImage2D but this
1545 * is VERY slow, so don't do that. We should prevent
1546 * this code from getting called in such cases or perhaps
1547 * we can use FBOs */
1549 glCopyTexSubImage2D(This->texture_target, This->texture_level,
1550 0, 0, 0, 0, This->currentDesc.Width, This->currentDesc.Height);
1551 checkGLcall("glCopyTexSubImage2D");
1553 glReadBuffer(prevRead);
1554 checkGLcall("glReadBuffer");
1558 context_release(context);
1560 TRACE("Updated target %d\n", This->texture_target);
1563 /* Context activation is done by the caller. */
1564 void surface_prepare_texture(IWineD3DSurfaceImpl *surface, const struct wined3d_gl_info *gl_info, BOOL srgb)
1566 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
1567 GLsizei width, height;
1568 CONVERT_TYPES convert;
1569 struct wined3d_format_desc desc;
1571 if (surface->Flags & alloc_flag) return;
1573 d3dfmt_get_conv(surface, TRUE, TRUE, &desc, &convert);
1574 if(convert != NO_CONVERSION) surface->Flags |= SFLAG_CONVERTED;
1575 else surface->Flags &= ~SFLAG_CONVERTED;
1577 if (surface->Flags & SFLAG_NONPOW2)
1579 width = surface->pow2Width;
1580 height = surface->pow2Height;
1584 width = surface->glRect.right - surface->glRect.left;
1585 height = surface->glRect.bottom - surface->glRect.top;
1588 surface_bind_and_dirtify(surface, srgb);
1589 surface_allocate_surface(surface, gl_info, &desc, srgb, width, height);
1590 surface->Flags |= alloc_flag;
1593 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This)
1595 IWineD3DDeviceImpl *device = This->resource.device;
1596 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1598 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
1599 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
1602 if(!(This->Flags & SFLAG_DYNLOCK)) {
1604 /* MAXLOCKCOUNT is defined in wined3d_private.h */
1605 if(This->lockCount > MAXLOCKCOUNT) {
1606 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
1607 This->Flags |= SFLAG_DYNLOCK;
1611 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
1612 * Also don't create a PBO for systemmem surfaces.
1614 if (gl_info->supported[ARB_PIXEL_BUFFER_OBJECT] && (This->Flags & SFLAG_DYNLOCK)
1615 && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2))
1616 && (This->resource.pool != WINED3DPOOL_SYSTEMMEM))
1619 struct wined3d_context *context;
1621 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
1624 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
1625 error = glGetError();
1626 if(This->pbo == 0 || error != GL_NO_ERROR) {
1627 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
1630 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
1632 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1633 checkGLcall("glBindBufferARB");
1635 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1636 checkGLcall("glBufferDataARB");
1638 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1639 checkGLcall("glBindBufferARB");
1641 /* We don't need the system memory anymore and we can't even use it for PBOs */
1642 if(!(This->Flags & SFLAG_CLIENT)) {
1643 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1644 This->resource.heapMemory = NULL;
1646 This->resource.allocatedMemory = NULL;
1647 This->Flags |= SFLAG_PBO;
1649 context_release(context);
1651 else if (!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO))
1653 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1656 if(!This->resource.heapMemory) {
1657 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1659 This->resource.allocatedMemory =
1660 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1661 if(This->Flags & SFLAG_INSYSMEM) {
1662 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1667 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1668 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1669 IWineD3DDeviceImpl *myDevice = This->resource.device;
1670 const RECT *pass_rect = pRect;
1672 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1674 /* This is also done in the base class, but we have to verify this before loading any data from
1675 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1676 * may interfere, and all other bad things may happen
1678 if (This->Flags & SFLAG_LOCKED) {
1679 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1680 return WINED3DERR_INVALIDCALL;
1682 This->Flags |= SFLAG_LOCKED;
1684 if (!(This->Flags & SFLAG_LOCKABLE))
1686 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1689 if (Flags & WINED3DLOCK_DISCARD) {
1690 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1691 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1692 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1693 This->Flags |= SFLAG_INSYSMEM;
1697 if (This->Flags & SFLAG_INSYSMEM) {
1698 TRACE("Local copy is up to date, not downloading data\n");
1699 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1703 /* IWineD3DSurface_LoadLocation() does not check if the rectangle specifies
1704 * the full surface. Most callers don't need that, so do it here. */
1705 if (pRect && pRect->top == 0 && pRect->left == 0
1706 && pRect->right == This->currentDesc.Width
1707 && pRect->bottom == This->currentDesc.Height)
1712 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
1713 && ((This->Flags & SFLAG_SWAPCHAIN) || iface == myDevice->render_targets[0])))
1715 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1719 if (This->Flags & SFLAG_PBO)
1721 const struct wined3d_gl_info *gl_info;
1722 struct wined3d_context *context;
1724 context = context_acquire(myDevice, NULL, CTXUSAGE_RESOURCELOAD);
1725 gl_info = context->gl_info;
1728 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1729 checkGLcall("glBindBufferARB");
1731 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1732 if(This->resource.allocatedMemory) {
1733 ERR("The surface already has PBO memory allocated!\n");
1736 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1737 checkGLcall("glMapBufferARB");
1739 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1740 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1741 checkGLcall("glBindBufferARB");
1744 context_release(context);
1747 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1750 IWineD3DBaseTexture *pBaseTexture;
1753 * as seen in msdn docs
1755 surface_add_dirty_rect(iface, pRect);
1757 /** Dirtify Container if needed */
1758 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1759 TRACE("Making container dirty\n");
1760 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1761 IWineD3DBaseTexture_Release(pBaseTexture);
1763 TRACE("Surface is standalone, no need to dirty the container\n");
1767 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1770 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1772 GLint prev_rasterpos[4];
1773 GLint skipBytes = 0;
1774 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1775 IWineD3DDeviceImpl *myDevice = This->resource.device;
1776 const struct wined3d_gl_info *gl_info;
1777 struct wined3d_context *context;
1779 /* Activate the correct context for the render target */
1780 context = context_acquire(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1781 gl_info = context->gl_info;
1785 if (!surface_is_offscreen((IWineD3DSurface *)This))
1787 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This);
1788 TRACE("Unlocking %#x buffer.\n", buffer);
1789 context_set_draw_buffer(context, buffer);
1793 /* Primary offscreen render target */
1794 TRACE("Offscreen render target.\n");
1795 context_set_draw_buffer(context, myDevice->offscreenBuffer);
1798 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1799 checkGLcall("glGetIntegerv");
1800 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1801 checkGLcall("glGetIntegerv");
1802 glPixelZoom(1.0f, -1.0f);
1803 checkGLcall("glPixelZoom");
1805 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1806 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1807 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1809 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1810 checkGLcall("glRasterPos3i");
1812 /* Some drivers(radeon dri, others?) don't like exceptions during
1813 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1814 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1815 * catch to put the dib section in InSync mode, which leads to a crash
1816 * and a blocked x server on my radeon card.
1818 * The following lines read the dib section so it is put in InSync mode
1819 * before glDrawPixels is called and the crash is prevented. There won't
1820 * be any interfering gdi accesses, because UnlockRect is called from
1821 * ReleaseDC, and the app won't use the dc any more afterwards.
1823 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1825 read = This->resource.allocatedMemory[0];
1828 if(This->Flags & SFLAG_PBO) {
1829 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1830 checkGLcall("glBindBufferARB");
1833 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1834 if(This->Flags & SFLAG_LOCKED) {
1835 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1836 (This->lockedRect.bottom - This->lockedRect.top)-1,
1838 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1839 checkGLcall("glDrawPixels");
1841 glDrawPixels(This->currentDesc.Width,
1842 This->currentDesc.Height,
1844 checkGLcall("glDrawPixels");
1847 if(This->Flags & SFLAG_PBO) {
1848 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1849 checkGLcall("glBindBufferARB");
1852 glPixelZoom(1.0f, 1.0f);
1853 checkGLcall("glPixelZoom");
1855 glRasterPos3iv(&prev_rasterpos[0]);
1856 checkGLcall("glRasterPos3iv");
1858 /* Reset to previous pack row length */
1859 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1860 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH)");
1863 context_release(context);
1866 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1867 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1868 IWineD3DDeviceImpl *myDevice = This->resource.device;
1871 if (!(This->Flags & SFLAG_LOCKED)) {
1872 WARN("trying to Unlock an unlocked surf@%p\n", This);
1873 return WINEDDERR_NOTLOCKED;
1876 if (This->Flags & SFLAG_PBO)
1878 const struct wined3d_gl_info *gl_info;
1879 struct wined3d_context *context;
1881 TRACE("Freeing PBO memory\n");
1883 context = context_acquire(myDevice, NULL, CTXUSAGE_RESOURCELOAD);
1884 gl_info = context->gl_info;
1887 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1888 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1889 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1890 checkGLcall("glUnmapBufferARB");
1892 context_release(context);
1894 This->resource.allocatedMemory = NULL;
1897 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1899 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1900 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1904 if ((This->Flags & SFLAG_SWAPCHAIN) || (myDevice->render_targets && iface == myDevice->render_targets[0]))
1906 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1907 static BOOL warned = FALSE;
1909 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1915 if(This->dirtyRect.left == 0 &&
1916 This->dirtyRect.top == 0 &&
1917 This->dirtyRect.right == This->currentDesc.Width &&
1918 This->dirtyRect.bottom == This->currentDesc.Height) {
1921 /* TODO: Proper partial rectangle tracking */
1922 fullsurface = FALSE;
1923 This->Flags |= SFLAG_INSYSMEM;
1926 switch(wined3d_settings.rendertargetlock_mode) {
1928 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1932 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1937 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1938 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1939 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1940 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1941 * not fully up to date because only a subrectangle was read in LockRect.
1943 This->Flags &= ~SFLAG_INSYSMEM;
1944 This->Flags |= SFLAG_INDRAWABLE;
1947 This->dirtyRect.left = This->currentDesc.Width;
1948 This->dirtyRect.top = This->currentDesc.Height;
1949 This->dirtyRect.right = 0;
1950 This->dirtyRect.bottom = 0;
1951 } else if(iface == myDevice->stencilBufferTarget) {
1952 FIXME("Depth Stencil buffer locking is not implemented\n");
1954 /* The rest should be a normal texture */
1955 IWineD3DBaseTextureImpl *impl;
1956 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1957 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1958 * states need resetting
1960 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1961 if(impl->baseTexture.bindCount) {
1962 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1964 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1969 This->Flags &= ~SFLAG_LOCKED;
1970 memset(&This->lockedRect, 0, sizeof(RECT));
1972 /* Overlays have to be redrawn manually after changes with the GL implementation */
1973 if(This->overlay_dest) {
1974 IWineD3DSurface_DrawOverlay(iface);
1979 static void surface_release_client_storage(IWineD3DSurface *iface)
1981 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
1982 struct wined3d_context *context;
1984 context = context_acquire(This->resource.device, NULL, CTXUSAGE_RESOURCELOAD);
1987 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
1988 if(This->texture_name)
1990 surface_bind_and_dirtify(This, FALSE);
1991 glTexImage2D(This->texture_target, This->texture_level,
1992 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
1994 if(This->texture_name_srgb)
1996 surface_bind_and_dirtify(This, TRUE);
1997 glTexImage2D(This->texture_target, This->texture_level,
1998 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
2000 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
2003 context_release(context);
2005 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSRGBTEX, FALSE);
2006 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
2007 surface_force_reload(iface);
2010 static HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC)
2012 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2013 WINED3DLOCKED_RECT lock;
2017 TRACE("(%p)->(%p)\n",This,pHDC);
2019 if(This->Flags & SFLAG_USERPTR) {
2020 ERR("Not supported on surfaces with an application-provided surfaces\n");
2021 return WINEDDERR_NODC;
2024 /* Give more detailed info for ddraw */
2025 if (This->Flags & SFLAG_DCINUSE)
2026 return WINEDDERR_DCALREADYCREATED;
2028 /* Can't GetDC if the surface is locked */
2029 if (This->Flags & SFLAG_LOCKED)
2030 return WINED3DERR_INVALIDCALL;
2032 memset(&lock, 0, sizeof(lock)); /* To be sure */
2034 /* Create a DIB section if there isn't a hdc yet */
2036 if(This->Flags & SFLAG_CLIENT) {
2037 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2038 surface_release_client_storage(iface);
2040 hr = IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
2041 if(FAILED(hr)) return WINED3DERR_INVALIDCALL;
2043 /* Use the dib section from now on if we are not using a PBO */
2044 if(!(This->Flags & SFLAG_PBO))
2045 This->resource.allocatedMemory = This->dib.bitmap_data;
2048 /* Lock the surface */
2049 hr = IWineD3DSurface_LockRect(iface,
2054 if(This->Flags & SFLAG_PBO) {
2055 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
2056 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
2060 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
2061 /* keep the dib section */
2065 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
2066 || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
2068 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
2069 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
2071 const PALETTEENTRY *pal = NULL;
2074 pal = This->palette->palents;
2076 IWineD3DSurfaceImpl *dds_primary;
2077 IWineD3DSwapChainImpl *swapchain;
2078 swapchain = (IWineD3DSwapChainImpl *)This->resource.device->swapchains[0];
2079 dds_primary = (IWineD3DSurfaceImpl *)swapchain->frontBuffer;
2080 if (dds_primary && dds_primary->palette)
2081 pal = dds_primary->palette->palents;
2085 for (n=0; n<256; n++) {
2086 col[n].rgbRed = pal[n].peRed;
2087 col[n].rgbGreen = pal[n].peGreen;
2088 col[n].rgbBlue = pal[n].peBlue;
2089 col[n].rgbReserved = 0;
2091 SetDIBColorTable(This->hDC, 0, 256, col);
2096 TRACE("returning %p\n",*pHDC);
2097 This->Flags |= SFLAG_DCINUSE;
2102 static HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC)
2104 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2106 TRACE("(%p)->(%p)\n",This,hDC);
2108 if (!(This->Flags & SFLAG_DCINUSE))
2109 return WINEDDERR_NODC;
2111 if (This->hDC !=hDC) {
2112 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
2113 return WINEDDERR_NODC;
2116 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
2117 /* Copy the contents of the DIB over to the PBO */
2118 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
2121 /* we locked first, so unlock now */
2122 IWineD3DSurface_UnlockRect(iface);
2124 This->Flags &= ~SFLAG_DCINUSE;
2129 /* ******************************************************
2130 IWineD3DSurface Internal (No mapping to directx api) parts follow
2131 ****************************************************** */
2133 HRESULT d3dfmt_get_conv(IWineD3DSurfaceImpl *This, BOOL need_alpha_ck, BOOL use_texturing, struct wined3d_format_desc *desc, CONVERT_TYPES *convert)
2135 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
2136 IWineD3DDeviceImpl *device = This->resource.device;
2137 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
2138 BOOL blit_supported = FALSE;
2139 RECT rect = {0, 0, This->pow2Width, This->pow2Height};
2141 /* Copy the default values from the surface. Below we might perform fixups */
2142 *desc = *This->resource.format_desc;
2143 *convert = NO_CONVERSION;
2145 /* Ok, now look if we have to do any conversion */
2146 switch(This->resource.format_desc->format)
2148 case WINED3DFMT_P8_UINT:
2153 blit_supported = device->blitter->blit_supported(&device->adapter->gl_info, BLIT_OP_BLIT,
2154 &rect, This->resource.usage, This->resource.pool,
2155 This->resource.format_desc, &rect, This->resource.usage,
2156 This->resource.pool, This->resource.format_desc);
2158 /* Use conversion when the blit_shader backend supports it. It only supports this in case of
2159 * texturing. Further also use conversion in case of color keying.
2160 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
2161 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
2162 * conflicts with this.
2164 if (!((blit_supported && device->render_targets && This == (IWineD3DSurfaceImpl*)device->render_targets[0]))
2165 || colorkey_active || !use_texturing)
2167 desc->glFormat = GL_RGBA;
2168 desc->glInternal = GL_RGBA;
2169 desc->glType = GL_UNSIGNED_BYTE;
2170 desc->conv_byte_count = 4;
2171 if(colorkey_active) {
2172 *convert = CONVERT_PALETTED_CK;
2174 *convert = CONVERT_PALETTED;
2179 case WINED3DFMT_B2G3R3_UNORM:
2180 /* **********************
2181 GL_UNSIGNED_BYTE_3_3_2
2182 ********************** */
2183 if (colorkey_active) {
2184 /* This texture format will never be used.. So do not care about color keying
2185 up until the point in time it will be needed :-) */
2186 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
2190 case WINED3DFMT_B5G6R5_UNORM:
2191 if (colorkey_active) {
2192 *convert = CONVERT_CK_565;
2193 desc->glFormat = GL_RGBA;
2194 desc->glInternal = GL_RGB5_A1;
2195 desc->glType = GL_UNSIGNED_SHORT_5_5_5_1;
2199 case WINED3DFMT_B5G5R5X1_UNORM:
2200 if (colorkey_active) {
2201 *convert = CONVERT_CK_5551;
2202 desc->glFormat = GL_BGRA;
2203 desc->glInternal = GL_RGB5_A1;
2204 desc->glType = GL_UNSIGNED_SHORT_1_5_5_5_REV;
2208 case WINED3DFMT_B8G8R8_UNORM:
2209 if (colorkey_active) {
2210 *convert = CONVERT_CK_RGB24;
2211 desc->glFormat = GL_RGBA;
2212 desc->glInternal = GL_RGBA8;
2213 desc->glType = GL_UNSIGNED_INT_8_8_8_8;
2214 desc->conv_byte_count = 4;
2218 case WINED3DFMT_B8G8R8X8_UNORM:
2219 if (colorkey_active) {
2220 *convert = CONVERT_RGB32_888;
2221 desc->glFormat = GL_RGBA;
2222 desc->glInternal = GL_RGBA8;
2223 desc->glType = GL_UNSIGNED_INT_8_8_8_8;
2227 case WINED3DFMT_L4A4_UNORM:
2228 /* WINED3DFMT_L4A4_UNORM exists as an internal gl format, but for some reason there is not
2229 * format+type combination to load it. Thus convert it to A8L8, then load it
2230 * with A4L4 internal, but A8L8 format+type
2232 *convert = CONVERT_A4L4;
2233 desc->conv_byte_count = 2;
2236 case WINED3DFMT_R16G16_UNORM:
2237 *convert = CONVERT_G16R16;
2238 desc->conv_byte_count = 6;
2241 case WINED3DFMT_R16G16_FLOAT:
2242 if (gl_info->supported[ARB_TEXTURE_RG]) break;
2243 *convert = CONVERT_R16G16F;
2244 desc->conv_byte_count = 6;
2247 case WINED3DFMT_R32G32_FLOAT:
2248 if (gl_info->supported[ARB_TEXTURE_RG]) break;
2249 *convert = CONVERT_R32G32F;
2250 desc->conv_byte_count = 12;
2253 case WINED3DFMT_S1_UINT_D15_UNORM:
2254 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2255 || gl_info->supported[EXT_PACKED_DEPTH_STENCIL])
2257 *convert = CONVERT_D15S1;
2258 desc->conv_byte_count = 4;
2266 /* At the moment we don't override the byte_count when it is the same before
2267 * and after conversion. Until that is fixed (which mostly applies to non-ck formats),
2268 * make sure we explicitly set conv_byte_count.
2269 * TODO: get rid of this.
2271 if(!desc->conv_byte_count)
2272 desc->conv_byte_count = desc->byte_count;
2277 void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey)
2279 IWineD3DDeviceImpl *device = This->resource.device;
2280 IWineD3DPaletteImpl *pal = This->palette;
2281 BOOL index_in_alpha = FALSE;
2284 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2285 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2286 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2287 * duplicate entries. Store the color key in the unused alpha component to speed the
2288 * download up and to make conversion unneeded. */
2289 index_in_alpha = primary_render_target_is_p8(device);
2293 UINT dxVersion = ((IWineD3DImpl *)device->wined3d)->dxVersion;
2295 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2298 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2301 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
2302 * there's no palette at this time. */
2303 for (i = 0; i < 256; i++) table[i][3] = i;
2308 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2309 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2310 * capability flag is present (wine does advertise this capability) */
2311 for (i = 0; i < 256; ++i)
2313 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2314 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2315 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2316 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2322 TRACE("Using surface palette %p\n", pal);
2323 /* Get the surface's palette */
2324 for (i = 0; i < 256; ++i)
2326 table[i][0] = pal->palents[i].peRed;
2327 table[i][1] = pal->palents[i].peGreen;
2328 table[i][2] = pal->palents[i].peBlue;
2330 /* When index_in_alpha is set the palette index is stored in the
2331 * alpha component. In case of a readback we can then read
2332 * GL_ALPHA. Color keying is handled in BltOverride using a
2333 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
2334 * color key itself is passed to glAlphaFunc in other cases the
2335 * alpha component of pixels that should be masked away is set to 0. */
2340 else if (colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue)
2341 && (i <= This->SrcBltCKey.dwColorSpaceHighValue))
2345 else if(pal->Flags & WINEDDPCAPS_ALPHA)
2347 table[i][3] = pal->palents[i].peFlags;
2357 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
2358 UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This)
2362 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
2367 memcpy(dst, src, pitch * height);
2370 case CONVERT_PALETTED:
2371 case CONVERT_PALETTED_CK:
2373 IWineD3DPaletteImpl* pal = This->palette;
2378 /* TODO: If we are a sublevel, try to get the palette from level 0 */
2381 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2383 for (y = 0; y < height; y++)
2385 source = src + pitch * y;
2386 dest = dst + outpitch * y;
2387 /* This is an 1 bpp format, using the width here is fine */
2388 for (x = 0; x < width; x++) {
2389 BYTE color = *source++;
2390 *dest++ = table[color][0];
2391 *dest++ = table[color][1];
2392 *dest++ = table[color][2];
2393 *dest++ = table[color][3];
2399 case CONVERT_CK_565:
2401 /* Converting the 565 format in 5551 packed to emulate color-keying.
2403 Note : in all these conversion, it would be best to average the averaging
2404 pixels to get the color of the pixel that will be color-keyed to
2405 prevent 'color bleeding'. This will be done later on if ever it is
2408 Note2: Nvidia documents say that their driver does not support alpha + color keying
2409 on the same surface and disables color keying in such a case
2415 TRACE("Color keyed 565\n");
2417 for (y = 0; y < height; y++) {
2418 Source = (const WORD *)(src + y * pitch);
2419 Dest = (WORD *) (dst + y * outpitch);
2420 for (x = 0; x < width; x++ ) {
2421 WORD color = *Source++;
2422 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
2423 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2424 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2433 case CONVERT_CK_5551:
2435 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
2439 TRACE("Color keyed 5551\n");
2440 for (y = 0; y < height; y++) {
2441 Source = (const WORD *)(src + y * pitch);
2442 Dest = (WORD *) (dst + y * outpitch);
2443 for (x = 0; x < width; x++ ) {
2444 WORD color = *Source++;
2446 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2447 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2451 *Dest &= ~(1 << 15);
2459 case CONVERT_CK_RGB24:
2461 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
2463 for (y = 0; y < height; y++)
2465 source = src + pitch * y;
2466 dest = dst + outpitch * y;
2467 for (x = 0; x < width; x++) {
2468 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
2469 DWORD dstcolor = color << 8;
2470 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2471 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2474 *(DWORD*)dest = dstcolor;
2482 case CONVERT_RGB32_888:
2484 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
2486 for (y = 0; y < height; y++)
2488 source = src + pitch * y;
2489 dest = dst + outpitch * y;
2490 for (x = 0; x < width; x++) {
2491 DWORD color = 0xffffff & *(const DWORD*)source;
2492 DWORD dstcolor = color << 8;
2493 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2494 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2497 *(DWORD*)dest = dstcolor;
2508 const unsigned char *Source;
2509 unsigned char *Dest;
2510 for(y = 0; y < height; y++) {
2511 Source = src + y * pitch;
2512 Dest = dst + y * outpitch;
2513 for (x = 0; x < width; x++ ) {
2514 unsigned char color = (*Source++);
2515 /* A */ Dest[1] = (color & 0xf0) << 0;
2516 /* L */ Dest[0] = (color & 0x0f) << 4;
2523 case CONVERT_G16R16:
2524 case CONVERT_R16G16F:
2530 for(y = 0; y < height; y++) {
2531 Source = (const WORD *)(src + y * pitch);
2532 Dest = (WORD *) (dst + y * outpitch);
2533 for (x = 0; x < width; x++ ) {
2534 WORD green = (*Source++);
2535 WORD red = (*Source++);
2538 /* Strictly speaking not correct for R16G16F, but it doesn't matter because the
2539 * shader overwrites it anyway
2548 case CONVERT_R32G32F:
2551 const float *Source;
2553 for(y = 0; y < height; y++) {
2554 Source = (const float *)(src + y * pitch);
2555 Dest = (float *) (dst + y * outpitch);
2556 for (x = 0; x < width; x++ ) {
2557 float green = (*Source++);
2558 float red = (*Source++);
2572 for (y = 0; y < height; ++y)
2574 const WORD *source = (const WORD *)(src + y * pitch);
2575 DWORD *dest = (DWORD *)(dst + y * outpitch);
2577 for (x = 0; x < width; ++x)
2579 /* The depth data is normalized, so needs to be scaled,
2580 * the stencil data isn't. Scale depth data by
2581 * (2^24-1)/(2^15-1) ~~ (2^9 + 2^-6). */
2582 WORD d15 = source[x] >> 1;
2583 DWORD d24 = (d15 << 9) + (d15 >> 6);
2584 dest[x] = (d24 << 8) | (source[x] & 0x1);
2591 ERR("Unsupported conversion type %#x.\n", convert);
2596 BOOL palette9_changed(IWineD3DSurfaceImpl *This)
2598 IWineD3DDeviceImpl *device = This->resource.device;
2600 if (This->palette || (This->resource.format_desc->format != WINED3DFMT_P8_UINT
2601 && This->resource.format_desc->format != WINED3DFMT_P8_UINT_A8_UNORM))
2603 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2604 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2611 if (!memcmp(This->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256))
2616 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2618 memcpy(This->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2622 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2623 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2624 DWORD flag = srgb_mode ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2626 if (!(This->Flags & flag)) {
2627 TRACE("Reloading because surface is dirty\n");
2628 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2629 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2630 /* Reload: vice versa OR */
2631 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2632 /* Also reload: Color key is active AND the color key has changed */
2633 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2634 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2635 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2636 TRACE("Reloading because of color keying\n");
2637 /* To perform the color key conversion we need a sysmem copy of
2638 * the surface. Make sure we have it
2641 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2642 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2643 /* TODO: This is not necessarily needed with hw palettized texture support */
2644 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2646 TRACE("surface is already in texture\n");
2650 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2651 * These resources are not bound by device size or format restrictions. Because of this,
2652 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2653 * However, these resources can always be created, locked, and copied.
2655 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2657 FIXME("(%p) Operation not supported for scratch textures\n",This);
2658 return WINED3DERR_INVALIDCALL;
2661 IWineD3DSurface_LoadLocation(iface, flag, NULL /* no partial locking for textures yet */);
2665 static unsigned int gen = 0;
2668 if ((gen % 10) == 0) {
2669 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm",
2670 This, This->texture_target, This->texture_level, gen);
2671 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2674 * debugging crash code
2683 if (!(This->Flags & SFLAG_DONOTFREE)) {
2684 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2685 This->resource.allocatedMemory = NULL;
2686 This->resource.heapMemory = NULL;
2687 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2693 /* Context activation is done by the caller. */
2694 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface, BOOL srgb) {
2695 /* TODO: check for locks */
2696 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2697 IWineD3DBaseTexture *baseTexture = NULL;
2699 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2700 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2701 TRACE("Passing to container\n");
2702 IWineD3DBaseTexture_BindTexture(baseTexture, srgb);
2703 IWineD3DBaseTexture_Release(baseTexture);
2709 TRACE("(%p) : Binding surface\n", This);
2711 name = srgb ? &This->texture_name_srgb : &This->texture_name;
2715 if (!This->texture_level)
2718 glGenTextures(1, name);
2719 checkGLcall("glGenTextures");
2720 TRACE("Surface %p given name %d\n", This, *name);
2722 glBindTexture(This->texture_target, *name);
2723 checkGLcall("glBindTexture");
2724 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2725 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2726 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2727 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2728 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2729 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2730 glTexParameteri(This->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2731 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2732 glTexParameteri(This->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2733 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2735 /* This is where we should be reducing the amount of GLMemoryUsed */
2737 /* Mipmap surfaces should have a base texture container */
2738 ERR("Mipmap surface has a glTexture bound to it!\n");
2741 glBindTexture(This->texture_target, *name);
2742 checkGLcall("glBindTexture");
2750 static HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename)
2753 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2754 char *allocatedMemory;
2755 const char *textureRow;
2756 IWineD3DSwapChain *swapChain = NULL;
2757 int width, height, i, y;
2758 GLuint tmpTexture = 0;
2761 Textures may not be stored in ->allocatedgMemory and a GlTexture
2762 so we should lock the surface before saving a snapshot, or at least check that
2764 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2765 by calling GetTexImage and in compressed form by calling
2766 GetCompressedTexImageARB. Queried compressed images can be saved and
2767 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2768 texture images do not need to be processed by the GL and should
2769 significantly improve texture loading performance relative to uncompressed
2772 /* Setup the width and height to be the internal texture width and height. */
2773 width = This->pow2Width;
2774 height = This->pow2Height;
2775 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2776 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2778 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2779 /* if were not a real texture then read the back buffer into a real texture */
2780 /* we don't want to interfere with the back buffer so read the data into a temporary
2781 * texture and then save the data out of the temporary texture
2785 TRACE("(%p) Reading render target into texture\n", This);
2787 glGenTextures(1, &tmpTexture);
2788 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2790 glTexImage2D(GL_TEXTURE_2D,
2797 GL_UNSIGNED_INT_8_8_8_8_REV,
2800 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2801 checkGLcall("glGetIntegerv");
2802 glReadBuffer(swapChain ? GL_BACK : This->resource.device->offscreenBuffer);
2803 checkGLcall("glReadBuffer");
2804 glCopyTexImage2D(GL_TEXTURE_2D,
2813 checkGLcall("glCopyTexImage2D");
2814 glReadBuffer(prevRead);
2817 } else { /* bind the real texture, and make sure it up to date */
2818 surface_internal_preload(iface, SRGB_RGB);
2819 surface_bind_and_dirtify(This, FALSE);
2821 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2823 FIXME("Saving texture level %d width %d height %d\n", This->texture_level, width, height);
2824 glGetTexImage(GL_TEXTURE_2D, This->texture_level, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, allocatedMemory);
2825 checkGLcall("glGetTexImage");
2827 glBindTexture(GL_TEXTURE_2D, 0);
2828 glDeleteTextures(1, &tmpTexture);
2832 f = fopen(filename, "w+");
2834 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2835 return WINED3DERR_INVALIDCALL;
2837 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
2838 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format_desc->format));
2853 fwrite(&width,2,1,f);
2855 fwrite(&height,2,1,f);
2860 /* 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 */
2862 textureRow = allocatedMemory + (width * (height - 1) *4);
2864 textureRow = allocatedMemory;
2865 for (y = 0 ; y < height; y++) {
2866 for (i = 0; i < width; i++) {
2867 color = *((const DWORD*)textureRow);
2868 fputc((color >> 16) & 0xFF, f); /* B */
2869 fputc((color >> 8) & 0xFF, f); /* G */
2870 fputc((color >> 0) & 0xFF, f); /* R */
2871 fputc((color >> 24) & 0xFF, f); /* A */
2874 /* take two rows of the pointer to the texture memory */
2876 (textureRow-= width << 3);
2879 TRACE("Closing file\n");
2883 IWineD3DSwapChain_Release(swapChain);
2885 HeapFree(GetProcessHeap(), 0, allocatedMemory);
2889 static HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
2890 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2893 TRACE("(%p) : Calling base function first\n", This);
2894 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
2896 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
2897 TRACE("(%p) : glFormat %d, glFormatInternal %d, glType %d\n", This, This->resource.format_desc->glFormat,
2898 This->resource.format_desc->glInternal, This->resource.format_desc->glType);
2903 static HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
2904 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
2906 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
2907 WARN("Surface is locked or the HDC is in use\n");
2908 return WINED3DERR_INVALIDCALL;
2911 if(Mem && Mem != This->resource.allocatedMemory) {
2912 void *release = NULL;
2914 /* Do I have to copy the old surface content? */
2915 if(This->Flags & SFLAG_DIBSECTION) {
2916 /* Release the DC. No need to hold the critical section for the update
2917 * Thread because this thread runs only on front buffers, but this method
2918 * fails for render targets in the check above.
2920 SelectObject(This->hDC, This->dib.holdbitmap);
2921 DeleteDC(This->hDC);
2922 /* Release the DIB section */
2923 DeleteObject(This->dib.DIBsection);
2924 This->dib.bitmap_data = NULL;
2925 This->resource.allocatedMemory = NULL;
2927 This->Flags &= ~SFLAG_DIBSECTION;
2928 } else if(!(This->Flags & SFLAG_USERPTR)) {
2929 release = This->resource.heapMemory;
2930 This->resource.heapMemory = NULL;
2932 This->resource.allocatedMemory = Mem;
2933 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
2935 /* Now the surface memory is most up do date. Invalidate drawable and texture */
2936 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2938 /* For client textures opengl has to be notified */
2939 if(This->Flags & SFLAG_CLIENT) {
2940 surface_release_client_storage(iface);
2943 /* Now free the old memory if any */
2944 HeapFree(GetProcessHeap(), 0, release);
2945 } else if(This->Flags & SFLAG_USERPTR) {
2946 /* LockRect and GetDC will re-create the dib section and allocated memory */
2947 This->resource.allocatedMemory = NULL;
2948 /* HeapMemory should be NULL already */
2949 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
2950 This->Flags &= ~SFLAG_USERPTR;
2952 if(This->Flags & SFLAG_CLIENT) {
2953 surface_release_client_storage(iface);
2959 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
2961 /* Flip the surface contents */
2966 front->hDC = back->hDC;
2970 /* Flip the DIBsection */
2973 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
2974 tmp = front->dib.DIBsection;
2975 front->dib.DIBsection = back->dib.DIBsection;
2976 back->dib.DIBsection = tmp;
2978 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
2979 else front->Flags &= ~SFLAG_DIBSECTION;
2980 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
2981 else back->Flags &= ~SFLAG_DIBSECTION;
2984 /* Flip the surface data */
2988 tmp = front->dib.bitmap_data;
2989 front->dib.bitmap_data = back->dib.bitmap_data;
2990 back->dib.bitmap_data = tmp;
2992 tmp = front->resource.allocatedMemory;
2993 front->resource.allocatedMemory = back->resource.allocatedMemory;
2994 back->resource.allocatedMemory = tmp;
2996 tmp = front->resource.heapMemory;
2997 front->resource.heapMemory = back->resource.heapMemory;
2998 back->resource.heapMemory = tmp;
3003 GLuint tmp_pbo = front->pbo;
3004 front->pbo = back->pbo;
3005 back->pbo = tmp_pbo;
3008 /* client_memory should not be different, but just in case */
3011 tmp = front->dib.client_memory;
3012 front->dib.client_memory = back->dib.client_memory;
3013 back->dib.client_memory = tmp;
3016 /* Flip the opengl texture */
3020 tmp = back->texture_name;
3021 back->texture_name = front->texture_name;
3022 front->texture_name = tmp;
3024 tmp = back->texture_name_srgb;
3025 back->texture_name_srgb = front->texture_name_srgb;
3026 front->texture_name_srgb = tmp;
3030 DWORD tmp_flags = back->Flags;
3031 back->Flags = front->Flags;
3032 front->Flags = tmp_flags;
3036 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
3037 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3038 IWineD3DSwapChainImpl *swapchain = NULL;
3040 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
3042 /* Flipping is only supported on RenderTargets and overlays*/
3043 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
3044 WARN("Tried to flip a non-render target, non-overlay surface\n");
3045 return WINEDDERR_NOTFLIPPABLE;
3048 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
3049 flip_surface(This, (IWineD3DSurfaceImpl *) override);
3051 /* Update the overlay if it is visible */
3052 if(This->overlay_dest) {
3053 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
3060 /* DDraw sets this for the X11 surfaces, so don't confuse the user
3061 * FIXME("(%p) Target override is not supported by now\n", This);
3062 * Additionally, it isn't really possible to support triple-buffering
3063 * properly on opengl at all
3067 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
3069 ERR("Flipped surface is not on a swapchain\n");
3070 return WINEDDERR_NOTFLIPPABLE;
3073 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
3074 * and only d3d8 and d3d9 apps specify the presentation interval
3076 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
3077 /* Most common case first to avoid wasting time on all the other cases */
3078 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
3079 } else if(Flags & WINEDDFLIP_NOVSYNC) {
3080 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3081 } else if(Flags & WINEDDFLIP_INTERVAL2) {
3082 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
3083 } else if(Flags & WINEDDFLIP_INTERVAL3) {
3084 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
3086 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
3089 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
3090 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *)swapchain,
3091 NULL, NULL, swapchain->win_handle, NULL, 0);
3092 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
3096 /* Does a direct frame buffer -> texture copy. Stretching is done
3097 * with single pixel copy calls
3099 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3100 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
3102 IWineD3DDeviceImpl *myDevice = This->resource.device;
3105 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3106 struct wined3d_context *context;
3107 BOOL upsidedown = FALSE;
3108 RECT dst_rect = *dst_rect_in;
3110 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3111 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3113 if(dst_rect.top > dst_rect.bottom) {
3114 UINT tmp = dst_rect.bottom;
3115 dst_rect.bottom = dst_rect.top;
3120 context = context_acquire(myDevice, SrcSurface, CTXUSAGE_BLIT);
3121 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3124 /* Bind the target texture */
3125 glBindTexture(This->texture_target, This->texture_name);
3126 checkGLcall("glBindTexture");
3127 if(surface_is_offscreen(SrcSurface)) {
3128 TRACE("Reading from an offscreen target\n");
3129 upsidedown = !upsidedown;
3130 glReadBuffer(myDevice->offscreenBuffer);
3134 glReadBuffer(surface_get_gl_buffer(SrcSurface));
3136 checkGLcall("glReadBuffer");
3138 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
3139 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
3141 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3143 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3145 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
3146 ERR("Texture filtering not supported in direct blit\n");
3149 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
3150 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3152 ERR("Texture filtering not supported in direct blit\n");
3156 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3157 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3159 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
3161 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3162 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
3163 src_rect->left, Src->currentDesc.Height - src_rect->bottom,
3164 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3166 UINT yoffset = Src->currentDesc.Height - src_rect->top + dst_rect.top - 1;
3167 /* I have to process this row by row to swap the image,
3168 * otherwise it would be upside down, so stretching in y direction
3169 * doesn't cost extra time
3171 * However, stretching in x direction can be avoided if not necessary
3173 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
3174 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3176 /* Well, that stuff works, but it's very slow.
3177 * find a better way instead
3181 for(col = dst_rect.left; col < dst_rect.right; col++) {
3182 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3183 dst_rect.left + col /* x offset */, row /* y offset */,
3184 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3187 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3188 dst_rect.left /* x offset */, row /* y offset */,
3189 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
3193 checkGLcall("glCopyTexSubImage2D");
3196 context_release(context);
3198 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3199 * path is never entered
3201 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3204 /* Uses the hardware to stretch and flip the image */
3205 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3206 const RECT *src_rect, const RECT *dst_rect_in, WINED3DTEXTUREFILTERTYPE Filter)
3208 IWineD3DDeviceImpl *myDevice = This->resource.device;
3209 GLuint src, backup = 0;
3210 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3211 IWineD3DSwapChainImpl *src_swapchain = NULL;
3212 float left, right, top, bottom; /* Texture coordinates */
3213 UINT fbwidth = Src->currentDesc.Width;
3214 UINT fbheight = Src->currentDesc.Height;
3215 struct wined3d_context *context;
3216 GLenum drawBuffer = GL_BACK;
3217 GLenum texture_target;
3218 BOOL noBackBufferBackup;
3220 BOOL upsidedown = FALSE;
3221 RECT dst_rect = *dst_rect_in;
3223 TRACE("Using hwstretch blit\n");
3224 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3225 context = context_acquire(myDevice, SrcSurface, CTXUSAGE_BLIT);
3226 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3228 src_offscreen = surface_is_offscreen(SrcSurface);
3229 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3230 if (!noBackBufferBackup && !Src->texture_name)
3232 /* Get it a description */
3233 surface_internal_preload(SrcSurface, SRGB_RGB);
3237 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3238 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3240 if (context->aux_buffers >= 2)
3242 /* Got more than one aux buffer? Use the 2nd aux buffer */
3243 drawBuffer = GL_AUX1;
3245 else if ((!src_offscreen || myDevice->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3247 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3248 drawBuffer = GL_AUX0;
3251 if(noBackBufferBackup) {
3252 glGenTextures(1, &backup);
3253 checkGLcall("glGenTextures");
3254 glBindTexture(GL_TEXTURE_2D, backup);
3255 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3256 texture_target = GL_TEXTURE_2D;
3258 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3259 * we are reading from the back buffer, the backup can be used as source texture
3261 texture_target = Src->texture_target;
3262 glBindTexture(texture_target, Src->texture_name);
3263 checkGLcall("glBindTexture(texture_target, Src->texture_name)");
3264 glEnable(texture_target);
3265 checkGLcall("glEnable(texture_target)");
3267 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3268 Src->Flags &= ~SFLAG_INTEXTURE;
3271 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3272 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3274 if(dst_rect.top > dst_rect.bottom) {
3275 UINT tmp = dst_rect.bottom;
3276 dst_rect.bottom = dst_rect.top;
3283 TRACE("Reading from an offscreen target\n");
3284 upsidedown = !upsidedown;
3285 glReadBuffer(myDevice->offscreenBuffer);
3289 glReadBuffer(surface_get_gl_buffer(SrcSurface));
3292 /* TODO: Only back up the part that will be overwritten */
3293 glCopyTexSubImage2D(texture_target, 0,
3294 0, 0 /* read offsets */,
3299 checkGLcall("glCopyTexSubImage2D");
3301 /* No issue with overriding these - the sampler is dirty due to blit usage */
3302 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3303 wined3d_gl_mag_filter(magLookup, Filter));
3304 checkGLcall("glTexParameteri");
3305 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3306 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
3307 checkGLcall("glTexParameteri");
3309 IWineD3DSurface_GetContainer((IWineD3DSurface *)SrcSurface, &IID_IWineD3DSwapChain, (void **)&src_swapchain);
3310 if (src_swapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *)src_swapchain);
3311 if (!src_swapchain || (IWineD3DSurface *) Src == src_swapchain->backBuffer[0]) {
3312 src = backup ? backup : Src->texture_name;
3314 glReadBuffer(GL_FRONT);
3315 checkGLcall("glReadBuffer(GL_FRONT)");
3317 glGenTextures(1, &src);
3318 checkGLcall("glGenTextures(1, &src)");
3319 glBindTexture(GL_TEXTURE_2D, src);
3320 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
3322 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3323 * out for power of 2 sizes
3325 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
3326 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3327 checkGLcall("glTexImage2D");
3328 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
3329 0, 0 /* read offsets */,
3334 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3335 checkGLcall("glTexParameteri");
3336 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3337 checkGLcall("glTexParameteri");
3339 glReadBuffer(GL_BACK);
3340 checkGLcall("glReadBuffer(GL_BACK)");
3342 if(texture_target != GL_TEXTURE_2D) {
3343 glDisable(texture_target);
3344 glEnable(GL_TEXTURE_2D);
3345 texture_target = GL_TEXTURE_2D;
3348 checkGLcall("glEnd and previous");
3350 left = src_rect->left;
3351 right = src_rect->right;
3354 top = Src->currentDesc.Height - src_rect->top;
3355 bottom = Src->currentDesc.Height - src_rect->bottom;
3357 top = Src->currentDesc.Height - src_rect->bottom;
3358 bottom = Src->currentDesc.Height - src_rect->top;
3361 if(Src->Flags & SFLAG_NORMCOORD) {
3362 left /= Src->pow2Width;
3363 right /= Src->pow2Width;
3364 top /= Src->pow2Height;
3365 bottom /= Src->pow2Height;
3368 /* draw the source texture stretched and upside down. The correct surface is bound already */
3369 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3370 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3372 context_set_draw_buffer(context, drawBuffer);
3373 glReadBuffer(drawBuffer);
3377 glTexCoord2f(left, bottom);
3378 glVertex2i(0, fbheight);
3381 glTexCoord2f(left, top);
3382 glVertex2i(0, fbheight - dst_rect.bottom - dst_rect.top);
3385 glTexCoord2f(right, top);
3386 glVertex2i(dst_rect.right - dst_rect.left, fbheight - dst_rect.bottom - dst_rect.top);
3389 glTexCoord2f(right, bottom);
3390 glVertex2i(dst_rect.right - dst_rect.left, fbheight);
3392 checkGLcall("glEnd and previous");
3394 if (texture_target != This->texture_target)
3396 glDisable(texture_target);
3397 glEnable(This->texture_target);
3398 texture_target = This->texture_target;
3401 /* Now read the stretched and upside down image into the destination texture */
3402 glBindTexture(texture_target, This->texture_name);
3403 checkGLcall("glBindTexture");
3404 glCopyTexSubImage2D(texture_target,
3406 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
3407 0, 0, /* We blitted the image to the origin */
3408 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3409 checkGLcall("glCopyTexSubImage2D");
3411 if(drawBuffer == GL_BACK) {
3412 /* Write the back buffer backup back */
3414 if(texture_target != GL_TEXTURE_2D) {
3415 glDisable(texture_target);
3416 glEnable(GL_TEXTURE_2D);
3417 texture_target = GL_TEXTURE_2D;
3419 glBindTexture(GL_TEXTURE_2D, backup);
3420 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3422 if (texture_target != Src->texture_target)
3424 glDisable(texture_target);
3425 glEnable(Src->texture_target);
3426 texture_target = Src->texture_target;
3428 glBindTexture(Src->texture_target, Src->texture_name);
3429 checkGLcall("glBindTexture(Src->texture_target, Src->texture_name)");
3434 glTexCoord2f(0.0f, (float)fbheight / (float)Src->pow2Height);
3438 glTexCoord2f(0.0f, 0.0f);
3439 glVertex2i(0, fbheight);
3442 glTexCoord2f((float)fbwidth / (float)Src->pow2Width, 0.0f);
3443 glVertex2i(fbwidth, Src->currentDesc.Height);
3446 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
3447 glVertex2i(fbwidth, 0);
3450 glDisable(texture_target);
3451 checkGLcall("glDisable(texture_target)");
3454 if (src != Src->texture_name && src != backup)
3456 glDeleteTextures(1, &src);
3457 checkGLcall("glDeleteTextures(1, &src)");
3460 glDeleteTextures(1, &backup);
3461 checkGLcall("glDeleteTextures(1, &backup)");
3466 wglFlush(); /* Flush to ensure ordering across contexts. */
3468 context_release(context);
3470 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3471 * path is never entered
3473 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3476 /* Until the blit_shader is ready, define some prototypes here. */
3477 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum blit_operation blit_op,
3478 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool,
3479 const struct wined3d_format_desc *src_format_desc,
3480 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool,
3481 const struct wined3d_format_desc *dst_format_desc);
3483 /* Not called from the VTable */
3484 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3485 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx,
3486 WINED3DTEXTUREFILTERTYPE Filter)
3488 IWineD3DDeviceImpl *myDevice = This->resource.device;
3489 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3490 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3491 RECT dst_rect, src_rect;
3493 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3495 /* Get the swapchain. One of the surfaces has to be a primary surface */
3496 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3497 WARN("Destination is in sysmem, rejecting gl blt\n");
3498 return WINED3DERR_INVALIDCALL;
3500 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3501 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3503 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3504 WARN("Src is in sysmem, rejecting gl blt\n");
3505 return WINED3DERR_INVALIDCALL;
3507 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3508 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3511 /* Early sort out of cases where no render target is used */
3512 if(!dstSwapchain && !srcSwapchain &&
3513 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3514 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3515 return WINED3DERR_INVALIDCALL;
3518 /* No destination color keying supported */
3519 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3520 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3521 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3522 return WINED3DERR_INVALIDCALL;
3525 surface_get_rect(This, DestRect, &dst_rect);
3526 if(Src) surface_get_rect(Src, SrcRect, &src_rect);
3528 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3529 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3530 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3531 /* Half-life does a Blt from the back buffer to the front buffer,
3532 * Full surface size, no flags... Use present instead
3534 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3537 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3540 TRACE("Looking if a Present can be done...\n");
3541 /* Source Rectangle must be full surface */
3542 if(src_rect.left != 0 || src_rect.top != 0 ||
3543 src_rect.right != Src->currentDesc.Width || src_rect.bottom != Src->currentDesc.Height) {
3544 TRACE("No, Source rectangle doesn't match\n");
3548 /* No stretching may occur */
3549 if(src_rect.right != dst_rect.right - dst_rect.left ||
3550 src_rect.bottom != dst_rect.bottom - dst_rect.top) {
3551 TRACE("No, stretching is done\n");
3555 /* Destination must be full surface or match the clipping rectangle */
3556 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3560 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3561 pos[0].x = dst_rect.left;
3562 pos[0].y = dst_rect.top;
3563 pos[1].x = dst_rect.right;
3564 pos[1].y = dst_rect.bottom;
3565 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3568 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3569 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3571 TRACE("No, dest rectangle doesn't match(clipper)\n");
3572 TRACE("Clip rect at %s\n", wine_dbgstr_rect(&cliprect));
3573 TRACE("Blt dest: %s\n", wine_dbgstr_rect(&dst_rect));
3579 if(dst_rect.left != 0 || dst_rect.top != 0 ||
3580 dst_rect.right != This->currentDesc.Width || dst_rect.bottom != This->currentDesc.Height) {
3581 TRACE("No, dest rectangle doesn't match(surface size)\n");
3588 /* These flags are unimportant for the flag check, remove them */
3589 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3590 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3592 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3593 * take very long, while a flip is fast.
3594 * This applies to Half-Life, which does such Blts every time it finished
3595 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3596 * menu. This is also used by all apps when they do windowed rendering
3598 * The problem is that flipping is not really the same as copying. After a
3599 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3600 * untouched. Therefore it's necessary to override the swap effect
3601 * and to set it back after the flip.
3603 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3607 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3608 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3610 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3611 IWineD3DSwapChain_Present((IWineD3DSwapChain *)dstSwapchain,
3612 NULL, NULL, dstSwapchain->win_handle, NULL, 0);
3614 dstSwapchain->presentParms.SwapEffect = orig_swap;
3621 TRACE("Unsupported blit between buffers on the same swapchain\n");
3622 return WINED3DERR_INVALIDCALL;
3623 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3624 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3625 return WINED3DERR_INVALIDCALL;
3626 } else if(dstSwapchain && srcSwapchain) {
3627 FIXME("Implement hardware blit between two different swapchains\n");
3628 return WINED3DERR_INVALIDCALL;
3629 } else if(dstSwapchain) {
3630 if(SrcSurface == myDevice->render_targets[0]) {
3631 TRACE("Blit from active render target to a swapchain\n");
3632 /* Handled with regular texture -> swapchain blit */
3634 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3635 FIXME("Implement blit from a swapchain to the active render target\n");
3636 return WINED3DERR_INVALIDCALL;
3639 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3640 /* Blit from render target to texture */
3643 /* P8 read back is not implemented */
3644 if (Src->resource.format_desc->format == WINED3DFMT_P8_UINT ||
3645 This->resource.format_desc->format == WINED3DFMT_P8_UINT)
3647 TRACE("P8 read back not supported by frame buffer to texture blit\n");
3648 return WINED3DERR_INVALIDCALL;
3651 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3652 TRACE("Color keying not supported by frame buffer to texture blit\n");
3653 return WINED3DERR_INVALIDCALL;
3654 /* Destination color key is checked above */
3657 if(dst_rect.right - dst_rect.left != src_rect.right - src_rect.left) {
3663 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3664 * flip the image nor scale it.
3666 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3667 * -> If the app wants a image width an unscaled width, copy it line per line
3668 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3669 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3670 * back buffer. This is slower than reading line per line, thus not used for flipping
3671 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3674 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3675 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3678 if (fbo_blit_supported(&myDevice->adapter->gl_info, BLIT_OP_BLIT,
3679 &src_rect, Src->resource.usage, Src->resource.pool, Src->resource.format_desc,
3680 &dst_rect, This->resource.usage, This->resource.pool, This->resource.format_desc))
3682 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &src_rect,
3683 (IWineD3DSurface *)This, &dst_rect, Filter);
3684 } else if((!stretchx) || dst_rect.right - dst_rect.left > Src->currentDesc.Width ||
3685 dst_rect.bottom - dst_rect.top > Src->currentDesc.Height) {
3686 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3687 fb_copy_to_texture_direct(This, SrcSurface, &src_rect, &dst_rect, Filter);
3689 TRACE("Using hardware stretching to flip / stretch the texture\n");
3690 fb_copy_to_texture_hwstretch(This, SrcSurface, &src_rect, &dst_rect, Filter);
3693 if(!(This->Flags & SFLAG_DONOTFREE)) {
3694 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3695 This->resource.allocatedMemory = NULL;
3696 This->resource.heapMemory = NULL;
3698 This->Flags &= ~SFLAG_INSYSMEM;
3703 /* Blit from offscreen surface to render target */
3704 DWORD oldCKeyFlags = Src->CKeyFlags;
3705 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3706 struct wined3d_context *context;
3707 BOOL paletteOverride = FALSE;
3709 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3711 /* When blitting from an offscreen surface to a rendertarget, the source
3712 * surface is not required to have a palette. Our rendering / conversion
3713 * code further down the road retrieves the palette from the surface, so
3714 * it must have a palette set. */
3715 if (Src->resource.format_desc->format == WINED3DFMT_P8_UINT && !Src->palette)
3717 paletteOverride = TRUE;
3718 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3719 Src->palette = This->palette;
3722 if (!(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3723 && fbo_blit_supported(&myDevice->adapter->gl_info, BLIT_OP_BLIT,
3724 &src_rect, Src->resource.usage, Src->resource.pool, Src->resource.format_desc,
3725 &dst_rect, This->resource.usage, This->resource.pool, This->resource.format_desc))
3727 TRACE("Using stretch_rect_fbo\n");
3728 /* The source is always a texture, but never the currently active render target, and the texture
3729 * contents are never upside down
3731 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &src_rect,
3732 (IWineD3DSurface *)This, &dst_rect, Filter);
3734 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3736 Src->palette = NULL;
3740 if (!(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3741 && arbfp_blit.blit_supported(&myDevice->adapter->gl_info, BLIT_OP_BLIT,
3742 &src_rect, Src->resource.usage, Src->resource.pool, Src->resource.format_desc,
3743 &dst_rect, This->resource.usage, This->resource.pool, This->resource.format_desc))
3745 HRESULT hr = arbfp_blit_surface(myDevice, Src, &src_rect, This, &dst_rect, BLIT_OP_BLIT, Filter);
3747 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3749 Src->palette = NULL;
3754 /* Color keying: Check if we have to do a color keyed blt,
3755 * and if not check if a color key is activated.
3757 * Just modify the color keying parameters in the surface and restore them afterwards
3758 * The surface keeps track of the color key last used to load the opengl surface.
3759 * PreLoad will catch the change to the flags and color key and reload if necessary.
3761 if(Flags & WINEDDBLT_KEYSRC) {
3762 /* Use color key from surface */
3763 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3764 /* Use color key from DDBltFx */
3765 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3766 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3768 /* Do not use color key */
3769 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3772 /* Now load the surface */
3773 surface_internal_preload((IWineD3DSurface *) Src, SRGB_RGB);
3775 /* Activate the destination context, set it up for blitting */
3776 context = context_acquire(myDevice, (IWineD3DSurface *)This, CTXUSAGE_BLIT);
3778 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3779 * while OpenGL coordinates are window relative.
3780 * Also beware of the origin difference(top left vs bottom left).
3781 * Also beware that the front buffer's surface size is screen width x screen height,
3782 * whereas the real gl drawable size is the size of the window.
3784 if (dstSwapchain && (IWineD3DSurface *)This == dstSwapchain->frontBuffer) {
3786 POINT offset = {0,0};
3788 ClientToScreen(context->win_handle, &offset);
3789 GetClientRect(context->win_handle, &windowsize);
3790 h = windowsize.bottom - windowsize.top;
3791 dst_rect.left -= offset.x; dst_rect.right -=offset.x;
3792 dst_rect.top -= offset.y; dst_rect.bottom -=offset.y;
3793 dst_rect.top += This->currentDesc.Height - h; dst_rect.bottom += This->currentDesc.Height - h;
3796 if (!myDevice->blitter->blit_supported(&myDevice->adapter->gl_info, BLIT_OP_BLIT,
3797 &src_rect, Src->resource.usage, Src->resource.pool, Src->resource.format_desc,
3798 &dst_rect, This->resource.usage, This->resource.pool, This->resource.format_desc))
3800 FIXME("Unsupported blit operation falling back to software\n");
3802 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3804 Src->palette = NULL;
3806 return WINED3DERR_INVALIDCALL;
3809 myDevice->blitter->set_shader((IWineD3DDevice *) myDevice, Src);
3813 /* This is for color keying */
3814 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3815 glEnable(GL_ALPHA_TEST);
3816 checkGLcall("glEnable(GL_ALPHA_TEST)");
3818 /* When the primary render target uses P8, the alpha component contains the palette index.
3819 * Which means that the colorkey is one of the palette entries. In other cases pixels that
3820 * should be masked away have alpha set to 0. */
3821 if(primary_render_target_is_p8(myDevice))
3822 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
3824 glAlphaFunc(GL_NOTEQUAL, 0.0f);
3825 checkGLcall("glAlphaFunc");
3827 glDisable(GL_ALPHA_TEST);
3828 checkGLcall("glDisable(GL_ALPHA_TEST)");
3831 /* Draw a textured quad
3833 draw_textured_quad(Src, &src_rect, &dst_rect, Filter);
3835 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3836 glDisable(GL_ALPHA_TEST);
3837 checkGLcall("glDisable(GL_ALPHA_TEST)");
3840 /* Restore the color key parameters */
3841 Src->CKeyFlags = oldCKeyFlags;
3842 Src->SrcBltCKey = oldBltCKey;
3844 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3846 Src->palette = NULL;
3850 /* Leave the opengl state valid for blitting */
3851 myDevice->blitter->unset_shader((IWineD3DDevice *) myDevice);
3853 wglFlush(); /* Flush to ensure ordering across contexts. */
3855 context_release(context);
3857 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
3858 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
3861 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
3865 /* Source-Less Blit to render target */
3866 if (Flags & WINEDDBLT_COLORFILL) {
3869 TRACE("Colorfill\n");
3871 /* The color as given in the Blt function is in the format of the frame-buffer...
3872 * 'clear' expect it in ARGB format => we need to do some conversion :-)
3874 if (!surface_convert_color_to_argb(This, DDBltFx->u5.dwFillColor, &color))
3876 /* The color conversion function already prints an error, so need to do it here */
3877 return WINED3DERR_INVALIDCALL;
3880 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
3881 must be true if we are here */
3882 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
3883 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
3884 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
3885 TRACE("Surface is higher back buffer, falling back to software\n");
3886 return cpu_blit.color_fill(myDevice, This, &dst_rect, color);
3889 return ffp_blit.color_fill(myDevice, This, &dst_rect, color);
3893 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3894 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3895 return WINED3DERR_INVALIDCALL;
3898 static HRESULT IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3899 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx)
3901 IWineD3DDeviceImpl *myDevice = This->resource.device;
3904 if (Flags & WINEDDBLT_DEPTHFILL) {
3905 switch(This->resource.format_desc->format)
3907 case WINED3DFMT_D16_UNORM:
3908 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
3910 case WINED3DFMT_S1_UINT_D15_UNORM:
3911 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
3913 case WINED3DFMT_D24_UNORM_S8_UINT:
3914 case WINED3DFMT_X8D24_UNORM:
3915 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
3917 case WINED3DFMT_D32_UNORM:
3918 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
3922 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format_desc->format));
3925 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
3926 DestRect == NULL ? 0 : 1,
3927 (const WINED3DRECT *)DestRect,
3928 WINED3DCLEAR_ZBUFFER,
3934 FIXME("(%p): Unsupp depthstencil blit\n", This);
3935 return WINED3DERR_INVALIDCALL;
3938 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
3939 const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
3940 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3941 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3942 IWineD3DDeviceImpl *myDevice = This->resource.device;
3944 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3945 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
3947 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
3949 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3950 return WINEDDERR_SURFACEBUSY;
3953 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
3954 * except depth blits, which seem to work
3956 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
3957 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
3958 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3959 return WINED3DERR_INVALIDCALL;
3960 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
3961 TRACE("Z Blit override handled the blit\n");
3966 /* Special cases for RenderTargets */
3967 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
3968 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
3969 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
3972 /* For the rest call the X11 surface implementation.
3973 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
3974 * other Blts are rather rare
3976 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
3979 static HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
3980 IWineD3DSurface *Source, const RECT *rsrc, DWORD trans)
3982 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3983 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
3984 IWineD3DDeviceImpl *myDevice = This->resource.device;
3986 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
3988 if ( (This->Flags & SFLAG_LOCKED) || (srcImpl->Flags & SFLAG_LOCKED))
3990 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
3991 return WINEDDERR_SURFACEBUSY;
3994 if(myDevice->inScene &&
3995 (iface == myDevice->stencilBufferTarget ||
3996 (Source == myDevice->stencilBufferTarget))) {
3997 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
3998 return WINED3DERR_INVALIDCALL;
4001 /* Special cases for RenderTargets */
4002 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
4003 (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) ) {
4005 RECT SrcRect, DstRect;
4008 surface_get_rect(srcImpl, rsrc, &SrcRect);
4010 DstRect.left = dstx;
4012 DstRect.right = dstx + SrcRect.right - SrcRect.left;
4013 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
4015 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
4016 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
4017 Flags |= WINEDDBLT_KEYSRC;
4018 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
4019 Flags |= WINEDDBLT_KEYDEST;
4020 if(trans & WINEDDBLTFAST_WAIT)
4021 Flags |= WINEDDBLT_WAIT;
4022 if(trans & WINEDDBLTFAST_DONOTWAIT)
4023 Flags |= WINEDDBLT_DONOTWAIT;
4025 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
4029 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
4032 static HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface)
4034 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4036 IWineD3DPaletteImpl *pal = This->palette;
4038 TRACE("(%p)\n", This);
4040 if (!pal) return WINED3D_OK;
4042 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
4043 || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
4045 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
4047 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
4048 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
4050 /* We want to force a palette refresh, so mark the drawable as not being up to date */
4051 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
4053 if(!(This->Flags & SFLAG_INSYSMEM)) {
4054 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
4055 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
4057 TRACE("Dirtifying surface\n");
4058 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
4062 if(This->Flags & SFLAG_DIBSECTION) {
4063 TRACE("(%p): Updating the hdc's palette\n", This);
4064 for (n=0; n<256; n++) {
4065 col[n].rgbRed = pal->palents[n].peRed;
4066 col[n].rgbGreen = pal->palents[n].peGreen;
4067 col[n].rgbBlue = pal->palents[n].peBlue;
4068 col[n].rgbReserved = 0;
4070 SetDIBColorTable(This->hDC, 0, 256, col);
4073 /* Propagate the changes to the drawable when we have a palette. */
4074 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
4075 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
4080 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
4081 /** Check against the maximum texture sizes supported by the video card **/
4082 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4083 const struct wined3d_gl_info *gl_info = &This->resource.device->adapter->gl_info;
4084 unsigned int pow2Width, pow2Height;
4086 This->texture_name = 0;
4087 This->texture_target = GL_TEXTURE_2D;
4089 /* Non-power2 support */
4090 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINE_NORMALIZED_TEXRECT])
4092 pow2Width = This->currentDesc.Width;
4093 pow2Height = This->currentDesc.Height;
4097 /* Find the nearest pow2 match */
4098 pow2Width = pow2Height = 1;
4099 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
4100 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
4102 This->pow2Width = pow2Width;
4103 This->pow2Height = pow2Height;
4105 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
4106 /** TODO: add support for non power two compressed textures **/
4107 if (This->resource.format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
4109 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
4110 This, This->currentDesc.Width, This->currentDesc.Height);
4111 return WINED3DERR_NOTAVAILABLE;
4115 if(pow2Width != This->currentDesc.Width ||
4116 pow2Height != This->currentDesc.Height) {
4117 This->Flags |= SFLAG_NONPOW2;
4120 TRACE("%p\n", This);
4121 if ((This->pow2Width > gl_info->limits.texture_size || This->pow2Height > gl_info->limits.texture_size)
4122 && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
4124 /* one of three options
4125 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)
4126 2: Set the texture to the maximum size (bad idea)
4127 3: WARN and return WINED3DERR_NOTAVAILABLE;
4128 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.
4130 if(This->resource.pool == WINED3DPOOL_DEFAULT || This->resource.pool == WINED3DPOOL_MANAGED)
4132 WARN("(%p) Unable to allocate a surface which exceeds the maximum OpenGL texture size\n", This);
4133 return WINED3DERR_NOTAVAILABLE;
4136 /* We should never use this surface in combination with OpenGL! */
4137 TRACE("(%p) Creating an oversized surface: %ux%u\n", This, This->pow2Width, This->pow2Height);
4139 /* This will be initialized on the first blt */
4140 This->glRect.left = 0;
4141 This->glRect.top = 0;
4142 This->glRect.right = 0;
4143 This->glRect.bottom = 0;
4145 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
4146 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
4147 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
4149 if (This->Flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
4150 && !(This->resource.format_desc->format == WINED3DFMT_P8_UINT
4151 && gl_info->supported[EXT_PALETTED_TEXTURE]
4152 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
4154 This->texture_target = GL_TEXTURE_RECTANGLE_ARB;
4155 This->pow2Width = This->currentDesc.Width;
4156 This->pow2Height = This->currentDesc.Height;
4157 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
4160 This->glRect.left = 0;
4161 This->glRect.top = 0;
4162 This->glRect.right = This->pow2Width;
4163 This->glRect.bottom = This->pow2Height;
4166 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4167 switch(wined3d_settings.offscreen_rendering_mode) {
4168 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4169 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4173 This->Flags |= SFLAG_INSYSMEM;
4178 /* GL locking is done by the caller */
4179 static void surface_depth_blt(IWineD3DSurfaceImpl *This, const struct wined3d_gl_info *gl_info,
4180 GLuint texture, GLsizei w, GLsizei h, GLenum target)
4182 IWineD3DDeviceImpl *device = This->resource.device;
4183 struct blt_info info;
4184 GLint old_binding = 0;
4186 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4188 glDisable(GL_CULL_FACE);
4189 glDisable(GL_BLEND);
4190 glDisable(GL_ALPHA_TEST);
4191 glDisable(GL_SCISSOR_TEST);
4192 glDisable(GL_STENCIL_TEST);
4193 glEnable(GL_DEPTH_TEST);
4194 glDepthFunc(GL_ALWAYS);
4195 glDepthMask(GL_TRUE);
4196 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4197 glViewport(0, 0, w, h);
4199 surface_get_blt_info(target, NULL, w, h, &info);
4200 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4201 glGetIntegerv(info.binding, &old_binding);
4202 glBindTexture(info.bind_target, texture);
4204 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4206 glBegin(GL_TRIANGLE_STRIP);
4207 glTexCoord3fv(info.coords[0]);
4208 glVertex2f(-1.0f, -1.0f);
4209 glTexCoord3fv(info.coords[1]);
4210 glVertex2f(1.0f, -1.0f);
4211 glTexCoord3fv(info.coords[2]);
4212 glVertex2f(-1.0f, 1.0f);
4213 glTexCoord3fv(info.coords[3]);
4214 glVertex2f(1.0f, 1.0f);
4217 glBindTexture(info.bind_target, old_binding);
4221 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4224 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
4225 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4227 TRACE("(%p) New location %#x\n", This, location);
4229 if (location & ~SFLAG_DS_LOCATIONS) {
4230 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
4233 This->Flags &= ~SFLAG_DS_LOCATIONS;
4234 This->Flags |= location;
4237 /* Context activation is done by the caller. */
4238 void surface_load_ds_location(IWineD3DSurface *iface, struct wined3d_context *context, DWORD location)
4240 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4241 IWineD3DDeviceImpl *device = This->resource.device;
4242 const struct wined3d_gl_info *gl_info = context->gl_info;
4244 TRACE("(%p) New location %#x\n", This, location);
4246 /* TODO: Make this work for modes other than FBO */
4247 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4249 if (This->Flags & location) {
4250 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
4254 if (This->current_renderbuffer) {
4255 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
4259 if (location == SFLAG_DS_OFFSCREEN) {
4260 if (This->Flags & SFLAG_DS_ONSCREEN) {
4261 GLint old_binding = 0;
4264 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
4268 if (!device->depth_blt_texture) {
4269 glGenTextures(1, &device->depth_blt_texture);
4272 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4273 * directly on the FBO texture. That's because we need to flip. */
4274 context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4275 if (This->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4277 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4278 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4280 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4281 bind_target = GL_TEXTURE_2D;
4283 glBindTexture(bind_target, device->depth_blt_texture);
4284 glCopyTexImage2D(bind_target, This->texture_level, This->resource.format_desc->glInternal,
4285 0, 0, This->currentDesc.Width, This->currentDesc.Height, 0);
4286 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4287 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4288 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4289 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4290 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4291 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4292 glBindTexture(bind_target, old_binding);
4294 /* Setup the destination */
4295 if (!device->depth_blt_rb) {
4296 gl_info->fbo_ops.glGenRenderbuffers(1, &device->depth_blt_rb);
4297 checkGLcall("glGenRenderbuffersEXT");
4299 if (device->depth_blt_rb_w != This->currentDesc.Width
4300 || device->depth_blt_rb_h != This->currentDesc.Height) {
4301 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, device->depth_blt_rb);
4302 checkGLcall("glBindRenderbufferEXT");
4303 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8,
4304 This->currentDesc.Width, This->currentDesc.Height);
4305 checkGLcall("glRenderbufferStorageEXT");
4306 device->depth_blt_rb_w = This->currentDesc.Width;
4307 device->depth_blt_rb_h = This->currentDesc.Height;
4310 context_bind_fbo(context, GL_FRAMEBUFFER, &context->dst_fbo);
4311 gl_info->fbo_ops.glFramebufferRenderbuffer(GL_FRAMEBUFFER,
4312 GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, device->depth_blt_rb);
4313 checkGLcall("glFramebufferRenderbufferEXT");
4314 context_attach_depth_stencil_fbo(context, GL_FRAMEBUFFER, iface, FALSE);
4316 /* Do the actual blit */
4317 surface_depth_blt(This, gl_info, device->depth_blt_texture,
4318 This->currentDesc.Width, This->currentDesc.Height, bind_target);
4319 checkGLcall("depth_blt");
4321 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER, &context->current_fbo->id);
4322 else context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4326 wglFlush(); /* Flush to ensure ordering across contexts. */
4330 FIXME("No up to date depth stencil location\n");
4332 } else if (location == SFLAG_DS_ONSCREEN) {
4333 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4334 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4338 context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4339 surface_depth_blt(This, gl_info, This->texture_name,
4340 This->currentDesc.Width, This->currentDesc.Height, This->texture_target);
4341 checkGLcall("depth_blt");
4343 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER, &context->current_fbo->id);
4347 wglFlush(); /* Flush to ensure ordering across contexts. */
4351 FIXME("No up to date depth stencil location\n");
4354 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4357 This->Flags |= location;
4360 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4361 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4362 IWineD3DBaseTexture *texture;
4363 IWineD3DSurfaceImpl *overlay;
4365 TRACE("(%p)->(%s, %s)\n", iface, debug_surflocation(flag),
4366 persistent ? "TRUE" : "FALSE");
4368 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4369 if (surface_is_offscreen(iface))
4371 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4372 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4376 TRACE("Surface %p is an onscreen surface\n", iface);
4381 if(((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) ||
4382 ((This->Flags & SFLAG_INSRGBTEX) && !(flag & SFLAG_INSRGBTEX))) {
4383 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4384 TRACE("Passing to container\n");
4385 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4386 IWineD3DBaseTexture_Release(texture);
4389 This->Flags &= ~SFLAG_LOCATIONS;
4390 This->Flags |= flag;
4392 /* Redraw emulated overlays, if any */
4393 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4394 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4395 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4399 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))) {
4400 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4401 TRACE("Passing to container\n");
4402 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4403 IWineD3DBaseTexture_Release(texture);
4406 This->Flags &= ~flag;
4409 if(!(This->Flags & SFLAG_LOCATIONS)) {
4410 ERR("%p: Surface does not have any up to date location\n", This);
4414 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in)
4416 IWineD3DDeviceImpl *device = This->resource.device;
4417 struct wined3d_context *context;
4418 RECT src_rect, dst_rect;
4420 surface_get_rect(This, rect_in, &src_rect);
4422 context = context_acquire(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4423 if (context->render_offscreen)
4425 dst_rect.left = src_rect.left;
4426 dst_rect.right = src_rect.right;
4427 dst_rect.top = src_rect.bottom;
4428 dst_rect.bottom = src_rect.top;
4432 dst_rect = src_rect;
4435 device->blitter->set_shader((IWineD3DDevice *) device, This);
4438 draw_textured_quad(This, &src_rect, &dst_rect, WINED3DTEXF_POINT);
4441 device->blitter->set_shader((IWineD3DDevice *) device, This);
4443 wglFlush(); /* Flush to ensure ordering across contexts. */
4445 context_release(context);
4448 /*****************************************************************************
4449 * IWineD3DSurface::LoadLocation
4451 * Copies the current surface data from wherever it is to the requested
4452 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4453 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4454 * multiple locations, the gl texture is preferred over the drawable, which is
4455 * preferred over system memory. The PBO counts as system memory. If rect is
4456 * not NULL, only the specified rectangle is copied (only supported for
4457 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4458 * location is marked up to date after the copy.
4461 * flag: Surface location flag to be updated
4462 * rect: rectangle to be copied
4465 * WINED3D_OK on success
4466 * WINED3DERR_DEVICELOST on an internal error
4468 *****************************************************************************/
4469 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4470 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4471 IWineD3DDeviceImpl *device = This->resource.device;
4472 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4473 struct wined3d_format_desc desc;
4474 CONVERT_TYPES convert;
4475 int width, pitch, outpitch;
4477 BOOL drawable_read_ok = TRUE;
4478 BOOL in_fbo = FALSE;
4480 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4481 if (surface_is_offscreen(iface))
4483 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4484 * Prefer SFLAG_INTEXTURE. */
4485 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4486 drawable_read_ok = FALSE;
4491 TRACE("Surface %p is an onscreen surface\n", iface);
4495 TRACE("(%p)->(%s, %p)\n", iface, debug_surflocation(flag), rect);
4497 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4500 if(This->Flags & flag) {
4501 TRACE("Location already up to date\n");
4505 if(!(This->Flags & SFLAG_LOCATIONS)) {
4506 ERR("%p: Surface does not have any up to date location\n", This);
4507 This->Flags |= SFLAG_LOST;
4508 return WINED3DERR_DEVICELOST;
4511 if(flag == SFLAG_INSYSMEM) {
4512 surface_prepare_system_memory(This);
4514 /* Download the surface to system memory */
4515 if (This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
4517 struct wined3d_context *context = NULL;
4519 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4521 surface_bind_and_dirtify(This, !(This->Flags & SFLAG_INTEXTURE));
4522 surface_download_data(This, gl_info);
4524 if (context) context_release(context);
4528 /* Note: It might be faster to download into a texture first. */
4529 read_from_framebuffer(This, rect,
4530 This->resource.allocatedMemory,
4531 IWineD3DSurface_GetPitch(iface));
4533 } else if(flag == SFLAG_INDRAWABLE) {
4534 if(This->Flags & SFLAG_INTEXTURE) {
4535 surface_blt_to_drawable(This, rect);
4538 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4539 /* This needs a shader to convert the srgb data sampled from the GL texture into RGB
4540 * values, otherwise we get incorrect values in the target. For now go the slow way
4541 * via a system memory copy
4543 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4546 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &desc, &convert);
4548 /* The width is in 'length' not in bytes */
4549 width = This->currentDesc.Width;
4550 pitch = IWineD3DSurface_GetPitch(iface);
4552 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4553 * but it isn't set (yet) in all cases it is getting called. */
4554 if ((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO))
4556 struct wined3d_context *context = NULL;
4558 TRACE("Removing the pbo attached to surface %p\n", This);
4560 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4561 surface_remove_pbo(This, gl_info);
4562 if (context) context_release(context);
4565 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4566 int height = This->currentDesc.Height;
4567 byte_count = desc.conv_byte_count;
4569 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4570 outpitch = width * byte_count;
4571 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4573 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4575 ERR("Out of memory %d, %d!\n", outpitch, height);
4576 return WINED3DERR_OUTOFVIDEOMEMORY;
4578 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4580 This->Flags |= SFLAG_CONVERTED;
4582 This->Flags &= ~SFLAG_CONVERTED;
4583 mem = This->resource.allocatedMemory;
4584 byte_count = desc.byte_count;
4587 flush_to_framebuffer_drawpixels(This, desc.glFormat, desc.glType, byte_count, mem);
4589 /* Don't delete PBO memory */
4590 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4591 HeapFree(GetProcessHeap(), 0, mem);
4593 } else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */ {
4594 if (drawable_read_ok && (This->Flags & SFLAG_INDRAWABLE)) {
4595 read_from_framebuffer_texture(This, flag == SFLAG_INSRGBTEX);
4599 /* Upload from system memory */
4600 BOOL srgb = flag == SFLAG_INSRGBTEX;
4601 struct wined3d_context *context = NULL;
4603 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */,
4607 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE) {
4608 /* Performance warning ... */
4609 FIXME("%p: Downloading rgb texture to reload it as srgb\n", This);
4610 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4613 if((This->Flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX) {
4614 /* Performance warning ... */
4615 FIXME("%p: Downloading srgb texture to reload it as rgb\n", This);
4616 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4619 if(!(This->Flags & SFLAG_INSYSMEM)) {
4620 /* Should not happen */
4621 ERR("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set\n");
4622 /* Lets hope we get it from somewhere... */
4623 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4626 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4628 surface_prepare_texture(This, gl_info, srgb);
4629 surface_bind_and_dirtify(This, srgb);
4631 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4632 This->Flags |= SFLAG_GLCKEY;
4633 This->glCKey = This->SrcBltCKey;
4635 else This->Flags &= ~SFLAG_GLCKEY;
4637 /* The width is in 'length' not in bytes */
4638 width = This->currentDesc.Width;
4639 pitch = IWineD3DSurface_GetPitch(iface);
4641 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4642 * but it isn't set (yet) in all cases it is getting called. */
4643 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4644 TRACE("Removing the pbo attached to surface %p\n", This);
4645 surface_remove_pbo(This, gl_info);
4649 /* This code is entered for texture formats which need a fixup. */
4650 int height = This->currentDesc.Height;
4652 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4653 outpitch = width * desc.conv_byte_count;
4654 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4656 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4658 ERR("Out of memory %d, %d!\n", outpitch, height);
4659 if (context) context_release(context);
4660 return WINED3DERR_OUTOFVIDEOMEMORY;
4662 desc.convert(This->resource.allocatedMemory, mem, pitch, width, height);
4663 } else if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4664 /* This code is only entered for color keying fixups */
4665 int height = This->currentDesc.Height;
4667 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4668 outpitch = width * desc.conv_byte_count;
4669 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4671 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4673 ERR("Out of memory %d, %d!\n", outpitch, height);
4674 if (context) context_release(context);
4675 return WINED3DERR_OUTOFVIDEOMEMORY;
4677 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4679 mem = This->resource.allocatedMemory;
4682 /* Make sure the correct pitch is used */
4684 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
4687 if (This->Flags & SFLAG_NONPOW2) {
4688 TRACE("non power of two support\n");
4689 if (mem || (This->Flags & SFLAG_PBO))
4691 surface_upload_data(This, gl_info, &desc, srgb,
4692 This->currentDesc.Width, This->currentDesc.Height, mem);
4695 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
4696 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
4698 if (mem || (This->Flags & SFLAG_PBO))
4700 surface_upload_data(This, gl_info, &desc, srgb, This->glRect.right - This->glRect.left,
4701 This->glRect.bottom - This->glRect.top, mem);
4705 /* Restore the default pitch */
4707 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
4710 if (context) context_release(context);
4712 /* Don't delete PBO memory */
4713 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4714 HeapFree(GetProcessHeap(), 0, mem);
4719 This->Flags |= flag;
4722 if (in_fbo && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
4723 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4724 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4730 static HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container)
4732 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4733 IWineD3DSwapChain *swapchain = NULL;
4735 /* Update the drawable size method */
4737 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
4740 This->get_drawable_size = get_drawable_size_swapchain;
4741 IWineD3DSwapChain_Release(swapchain);
4742 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4743 switch(wined3d_settings.offscreen_rendering_mode) {
4744 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4745 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4749 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
4752 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
4753 return SURFACE_OPENGL;
4756 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
4757 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4760 /* If there's no destination surface there is nothing to do */
4761 if(!This->overlay_dest) return WINED3D_OK;
4763 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
4764 * update the overlay. Prevent an endless recursion
4766 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
4769 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
4770 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
4771 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
4772 NULL, WINED3DTEXF_LINEAR);
4773 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
4778 BOOL surface_is_offscreen(IWineD3DSurface *iface)
4780 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4781 IWineD3DSwapChainImpl *swapchain = (IWineD3DSwapChainImpl *) This->container;
4783 /* Not on a swapchain - must be offscreen */
4784 if (!(This->Flags & SFLAG_SWAPCHAIN)) return TRUE;
4786 /* The front buffer is always onscreen */
4787 if(iface == swapchain->frontBuffer) return FALSE;
4789 /* If the swapchain is rendered to an FBO, the backbuffer is
4790 * offscreen, otherwise onscreen */
4791 return swapchain->render_to_fbo;
4794 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
4797 IWineD3DBaseSurfaceImpl_QueryInterface,
4798 IWineD3DBaseSurfaceImpl_AddRef,
4799 IWineD3DSurfaceImpl_Release,
4800 /* IWineD3DResource */
4801 IWineD3DBaseSurfaceImpl_GetParent,
4802 IWineD3DBaseSurfaceImpl_SetPrivateData,
4803 IWineD3DBaseSurfaceImpl_GetPrivateData,
4804 IWineD3DBaseSurfaceImpl_FreePrivateData,
4805 IWineD3DBaseSurfaceImpl_SetPriority,
4806 IWineD3DBaseSurfaceImpl_GetPriority,
4807 IWineD3DSurfaceImpl_PreLoad,
4808 IWineD3DSurfaceImpl_UnLoad,
4809 IWineD3DBaseSurfaceImpl_GetType,
4810 /* IWineD3DSurface */
4811 IWineD3DBaseSurfaceImpl_GetContainer,
4812 IWineD3DBaseSurfaceImpl_GetDesc,
4813 IWineD3DSurfaceImpl_LockRect,
4814 IWineD3DSurfaceImpl_UnlockRect,
4815 IWineD3DSurfaceImpl_GetDC,
4816 IWineD3DSurfaceImpl_ReleaseDC,
4817 IWineD3DSurfaceImpl_Flip,
4818 IWineD3DSurfaceImpl_Blt,
4819 IWineD3DBaseSurfaceImpl_GetBltStatus,
4820 IWineD3DBaseSurfaceImpl_GetFlipStatus,
4821 IWineD3DBaseSurfaceImpl_IsLost,
4822 IWineD3DBaseSurfaceImpl_Restore,
4823 IWineD3DSurfaceImpl_BltFast,
4824 IWineD3DBaseSurfaceImpl_GetPalette,
4825 IWineD3DBaseSurfaceImpl_SetPalette,
4826 IWineD3DSurfaceImpl_RealizePalette,
4827 IWineD3DBaseSurfaceImpl_SetColorKey,
4828 IWineD3DBaseSurfaceImpl_GetPitch,
4829 IWineD3DSurfaceImpl_SetMem,
4830 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
4831 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
4832 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
4833 IWineD3DBaseSurfaceImpl_UpdateOverlay,
4834 IWineD3DBaseSurfaceImpl_SetClipper,
4835 IWineD3DBaseSurfaceImpl_GetClipper,
4837 IWineD3DSurfaceImpl_LoadTexture,
4838 IWineD3DSurfaceImpl_BindTexture,
4839 IWineD3DSurfaceImpl_SaveSnapshot,
4840 IWineD3DSurfaceImpl_SetContainer,
4841 IWineD3DBaseSurfaceImpl_GetData,
4842 IWineD3DSurfaceImpl_SetFormat,
4843 IWineD3DSurfaceImpl_PrivateSetup,
4844 IWineD3DSurfaceImpl_ModifyLocation,
4845 IWineD3DSurfaceImpl_LoadLocation,
4846 IWineD3DSurfaceImpl_GetImplType,
4847 IWineD3DSurfaceImpl_DrawOverlay
4850 static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
4851 /* Context activation is done by the caller. */
4852 static void ffp_blit_free(IWineD3DDevice *iface) { }
4854 /* This function is used in case of 8bit paletted textures using GL_EXT_paletted_texture */
4855 /* Context activation is done by the caller. */
4856 static void ffp_blit_p8_upload_palette(IWineD3DSurfaceImpl *surface, const struct wined3d_gl_info *gl_info)
4859 BOOL colorkey_active = (surface->CKeyFlags & WINEDDSD_CKSRCBLT) ? TRUE : FALSE;
4861 d3dfmt_p8_init_palette(surface, table, colorkey_active);
4863 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
4865 GL_EXTCALL(glColorTableEXT(surface->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
4869 /* Context activation is done by the caller. */
4870 static HRESULT ffp_blit_set(IWineD3DDevice *iface, IWineD3DSurfaceImpl *surface)
4872 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
4873 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4874 enum complex_fixup fixup = get_complex_fixup(surface->resource.format_desc->color_fixup);
4876 /* When EXT_PALETTED_TEXTURE is around, palette conversion is done by the GPU
4877 * else the surface is converted in software at upload time in LoadLocation.
4879 if(fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
4880 ffp_blit_p8_upload_palette(surface, gl_info);
4883 glEnable(surface->texture_target);
4884 checkGLcall("glEnable(surface->texture_target)");
4889 /* Context activation is done by the caller. */
4890 static void ffp_blit_unset(IWineD3DDevice *iface)
4892 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
4893 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4896 glDisable(GL_TEXTURE_2D);
4897 checkGLcall("glDisable(GL_TEXTURE_2D)");
4898 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
4900 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4901 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4903 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
4905 glDisable(GL_TEXTURE_RECTANGLE_ARB);
4906 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4911 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum blit_operation blit_op,
4912 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool,
4913 const struct wined3d_format_desc *src_format_desc,
4914 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool,
4915 const struct wined3d_format_desc *dst_format_desc)
4917 enum complex_fixup src_fixup = get_complex_fixup(src_format_desc->color_fixup);
4919 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4921 TRACE("Checking support for fixup:\n");
4922 dump_color_fixup_desc(src_format_desc->color_fixup);
4925 if (blit_op != BLIT_OP_BLIT)
4927 TRACE("Unsupported blit_op=%d\n", blit_op);
4931 if (!is_identity_fixup(dst_format_desc->color_fixup))
4933 TRACE("Destination fixups are not supported\n");
4937 if (src_fixup == COMPLEX_FIXUP_P8 && gl_info->supported[EXT_PALETTED_TEXTURE])
4939 TRACE("P8 fixup supported\n");
4943 /* We only support identity conversions. */
4944 if (is_identity_fixup(src_format_desc->color_fixup))
4950 TRACE("[FAILED]\n");
4954 static HRESULT ffp_blit_color_fill(IWineD3DDeviceImpl *device, IWineD3DSurfaceImpl *dst_surface, const RECT *dst_rect, DWORD fill_color)
4956 return IWineD3DDeviceImpl_ClearSurface(device, dst_surface, 1 /* Number of rectangles */,
4957 (const WINED3DRECT*)dst_rect, WINED3DCLEAR_TARGET, fill_color, 0.0f /* Z */, 0 /* Stencil */);
4960 const struct blit_shader ffp_blit = {
4969 static HRESULT cpu_blit_alloc(IWineD3DDevice *iface)
4974 /* Context activation is done by the caller. */
4975 static void cpu_blit_free(IWineD3DDevice *iface)
4979 /* Context activation is done by the caller. */
4980 static HRESULT cpu_blit_set(IWineD3DDevice *iface, IWineD3DSurfaceImpl *surface)
4985 /* Context activation is done by the caller. */
4986 static void cpu_blit_unset(IWineD3DDevice *iface)
4990 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum blit_operation blit_op,
4991 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool,
4992 const struct wined3d_format_desc *src_format_desc,
4993 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool,
4994 const struct wined3d_format_desc *dst_format_desc)
4999 static HRESULT cpu_blit_color_fill(IWineD3DDeviceImpl *device, IWineD3DSurfaceImpl *dst_surface, const RECT *dst_rect, DWORD fill_color)
5002 memset(&BltFx, 0, sizeof(BltFx));
5003 BltFx.dwSize = sizeof(BltFx);
5004 BltFx.u5.dwFillColor = color_convert_argb_to_fmt(fill_color, dst_surface->resource.format_desc->format);
5005 return IWineD3DBaseSurfaceImpl_Blt((IWineD3DSurface*)dst_surface, dst_rect, NULL, NULL, WINEDDBLT_COLORFILL, &BltFx, WINED3DTEXF_POINT);
5008 const struct blit_shader cpu_blit = {
5017 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum blit_operation blit_op,
5018 const RECT *src_rect, DWORD src_usage, WINED3DPOOL src_pool,
5019 const struct wined3d_format_desc *src_format_desc,
5020 const RECT *dst_rect, DWORD dst_usage, WINED3DPOOL dst_pool,
5021 const struct wined3d_format_desc *dst_format_desc)
5023 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
5026 /* We only support blitting. Things like color keying / color fill should
5027 * be handled by other blitters.
5029 if (blit_op != BLIT_OP_BLIT)
5032 /* Source and/or destination need to be on the GL side */
5033 if (src_pool == WINED3DPOOL_SYSTEMMEM || dst_pool == WINED3DPOOL_SYSTEMMEM)
5036 if(!((src_format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET))
5037 && ((dst_format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
5040 if (!is_identity_fixup(src_format_desc->color_fixup) ||
5041 !is_identity_fixup(dst_format_desc->color_fixup))
5044 if (!(src_format_desc->format == dst_format_desc->format
5045 || (is_identity_fixup(src_format_desc->color_fixup)
5046 && is_identity_fixup(dst_format_desc->color_fixup))))