wined3d: Use the context's window handle in IWineD3DSurfaceImpl_BltOverride().
[wine.git] / dlls / wined3d / surface.c
blob74c84bd5af2cb9f57a72cc5a67a03e1cfb8e7cd1
1 /*
2 * IWineD3DSurface Implementation
4 * Copyright 1998 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2002-2005 Jason Edmeades
7 * Copyright 2002-2003 Raphael Junqueira
8 * Copyright 2004 Christian Costa
9 * Copyright 2005 Oliver Stieber
10 * Copyright 2006-2008 Stefan Dösinger for CodeWeavers
11 * Copyright 2007-2008 Henri Verbeet
12 * Copyright 2006-2008 Roderick Colenbrander
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
30 #include "config.h"
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);
57 ENTER_GL();
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)
68 /* Delete the 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);
78 LEAVE_GL();
80 if (This->Flags & SFLAG_DIBSECTION)
82 /* Release the DC. */
83 SelectObject(This->hDC, This->dib.holdbitmap);
84 DeleteDC(This->hDC);
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 GlPixelFormatDesc *format_desc, UINT alignment, UINT width, UINT height)
103 UINT size;
105 if (format_desc->format == WINED3DFMT_UNKNOWN)
107 size = 0;
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;
115 else
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;
123 return size;
126 struct blt_info
128 GLenum binding;
129 GLenum bind_target;
130 enum tex_types tex_type;
131 GLfloat coords[4][3];
134 struct float_rect
136 float l;
137 float t;
138 float r;
139 float b;
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;
153 RECT rect;
154 struct float_rect f;
156 if (rect_in)
157 rect = *rect_in;
158 else
160 rect.left = 0;
161 rect.top = 0;
162 rect.right = w;
163 rect.bottom = h;
166 switch (target)
168 default:
169 FIXME("Unsupported texture target %#x\n", target);
170 /* Fall back to GL_TEXTURE_2D */
171 case 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;
177 coords[0][2] = 0.0f;
179 coords[1][0] = (float)rect.right / w;
180 coords[1][1] = (float)rect.top / h;
181 coords[1][2] = 0.0f;
183 coords[2][0] = (float)rect.left / w;
184 coords[2][1] = (float)rect.bottom / h;
185 coords[2][2] = 0.0f;
187 coords[3][0] = (float)rect.right / w;
188 coords[3][1] = (float)rect.bottom / h;
189 coords[3][2] = 0.0f;
190 break;
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;
200 break;
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;
212 break;
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;
224 break;
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;
236 break;
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;
248 break;
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;
260 break;
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;
272 break;
277 HRESULT surface_init(IWineD3DSurfaceImpl *surface, WINED3DSURFTYPE surface_type, UINT alignment,
278 UINT width, UINT height, UINT level, BOOL lockable, BOOL discard, WINED3DMULTISAMPLE_TYPE multisample_type,
279 UINT multisample_quality, IWineD3DDeviceImpl *device, DWORD usage, WINED3DFORMAT format,
280 WINED3DPOOL pool, IUnknown *parent, const struct wined3d_parent_ops *parent_ops)
282 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
283 const struct GlPixelFormatDesc *format_desc = getFormatDescEntry(format, gl_info);
284 void (*cleanup)(IWineD3DSurfaceImpl *This);
285 unsigned int resource_size;
286 HRESULT hr;
288 if (multisample_quality > 0)
290 FIXME("multisample_quality set to %u, substituting 0\n", multisample_quality);
291 multisample_quality = 0;
294 /* FIXME: Check that the format is supported by the device. */
296 resource_size = surface_calculate_size(format_desc, alignment, width, height);
298 /* Look at the implementation and set the correct Vtable. */
299 switch (surface_type)
301 case SURFACE_OPENGL:
302 surface->lpVtbl = &IWineD3DSurface_Vtbl;
303 cleanup = surface_cleanup;
304 break;
306 case SURFACE_GDI:
307 surface->lpVtbl = &IWineGDISurface_Vtbl;
308 cleanup = surface_gdi_cleanup;
309 break;
311 default:
312 ERR("Requested unknown surface implementation %#x.\n", surface_type);
313 return WINED3DERR_INVALIDCALL;
316 hr = resource_init((IWineD3DResource *)surface, WINED3DRTYPE_SURFACE,
317 device, resource_size, usage, format_desc, pool, parent, parent_ops);
318 if (FAILED(hr))
320 WARN("Failed to initialize resource, returning %#x.\n", hr);
321 return hr;
324 /* "Standalone" surface. */
325 IWineD3DSurface_SetContainer((IWineD3DSurface *)surface, NULL);
327 surface->currentDesc.Width = width;
328 surface->currentDesc.Height = height;
329 surface->currentDesc.MultiSampleType = multisample_type;
330 surface->currentDesc.MultiSampleQuality = multisample_quality;
331 surface->texture_level = level;
332 list_init(&surface->overlays);
334 /* Flags */
335 surface->Flags = SFLAG_NORMCOORD; /* Default to normalized coords. */
336 if (discard) surface->Flags |= SFLAG_DISCARD;
337 if (lockable || format == WINED3DFMT_D16_LOCKABLE) surface->Flags |= SFLAG_LOCKABLE;
339 /* Quick lockable sanity check.
340 * TODO: remove this after surfaces, usage and lockability have been debugged properly
341 * this function is too deep to need to care about things like this.
342 * Levels need to be checked too, since they all affect what can be done. */
343 switch (pool)
345 case WINED3DPOOL_SCRATCH:
346 if(!lockable)
348 FIXME("Called with a pool of SCRATCH and a lockable of FALSE "
349 "which are mutually exclusive, setting lockable to TRUE.\n");
350 lockable = TRUE;
352 break;
354 case WINED3DPOOL_SYSTEMMEM:
355 if (!lockable)
356 FIXME("Called with a pool of SYSTEMMEM and a lockable of FALSE, this is acceptable but unexpected.\n");
357 break;
359 case WINED3DPOOL_MANAGED:
360 if (usage & WINED3DUSAGE_DYNAMIC)
361 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
362 break;
364 case WINED3DPOOL_DEFAULT:
365 if (lockable && !(usage & (WINED3DUSAGE_DYNAMIC | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
366 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
367 break;
369 default:
370 FIXME("Unknown pool %#x.\n", pool);
371 break;
374 if (usage & WINED3DUSAGE_RENDERTARGET && pool != WINED3DPOOL_DEFAULT)
376 FIXME("Trying to create a render target that isn't in the default pool.\n");
379 /* Mark the texture as dirty so that it gets loaded first time around. */
380 surface_add_dirty_rect((IWineD3DSurface *)surface, NULL);
381 list_init(&surface->renderbuffers);
383 TRACE("surface %p, memory %p, size %u\n", surface, surface->resource.allocatedMemory, surface->resource.size);
385 /* Call the private setup routine */
386 hr = IWineD3DSurface_PrivateSetup((IWineD3DSurface *)surface);
387 if (FAILED(hr))
389 ERR("Private setup failed, returning %#x\n", hr);
390 cleanup(surface);
391 return hr;
394 return hr;
397 static void surface_force_reload(IWineD3DSurface *iface)
399 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
401 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
404 void surface_set_texture_name(IWineD3DSurface *iface, GLuint new_name, BOOL srgb)
406 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
407 GLuint *name;
408 DWORD flag;
410 if(srgb)
412 name = &This->texture_name_srgb;
413 flag = SFLAG_INSRGBTEX;
415 else
417 name = &This->texture_name;
418 flag = SFLAG_INTEXTURE;
421 TRACE("(%p) : setting texture name %u\n", This, new_name);
423 if (!*name && new_name)
425 /* FIXME: We shouldn't need to remove SFLAG_INTEXTURE if the
426 * surface has no texture name yet. See if we can get rid of this. */
427 if (This->Flags & flag)
428 ERR("Surface has SFLAG_INTEXTURE set, but no texture name\n");
429 IWineD3DSurface_ModifyLocation(iface, flag, FALSE);
432 *name = new_name;
433 surface_force_reload(iface);
436 void surface_set_texture_target(IWineD3DSurface *iface, GLenum target)
438 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
440 TRACE("(%p) : setting target %#x\n", This, target);
442 if (This->texture_target != target)
444 if (target == GL_TEXTURE_RECTANGLE_ARB)
446 This->Flags &= ~SFLAG_NORMCOORD;
448 else if (This->texture_target == GL_TEXTURE_RECTANGLE_ARB)
450 This->Flags |= SFLAG_NORMCOORD;
453 This->texture_target = target;
454 surface_force_reload(iface);
457 /* Context activation is done by the caller. */
458 static void surface_bind_and_dirtify(IWineD3DSurfaceImpl *This, BOOL srgb) {
459 DWORD active_sampler;
461 /* We don't need a specific texture unit, but after binding the texture the current unit is dirty.
462 * Read the unit back instead of switching to 0, this avoids messing around with the state manager's
463 * gl states. The current texture unit should always be a valid one.
465 * To be more specific, this is tricky because we can implicitly be called
466 * from sampler() in state.c. This means we can't touch anything other than
467 * whatever happens to be the currently active texture, or we would risk
468 * marking already applied sampler states dirty again.
470 * TODO: Track the current active texture per GL context instead of using glGet
472 GLint active_texture;
473 ENTER_GL();
474 glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
475 LEAVE_GL();
476 active_sampler = This->resource.device->rev_tex_unit_map[active_texture - GL_TEXTURE0_ARB];
478 if (active_sampler != WINED3D_UNMAPPED_STAGE)
480 IWineD3DDeviceImpl_MarkStateDirty(This->resource.device, STATE_SAMPLER(active_sampler));
482 IWineD3DSurface_BindTexture((IWineD3DSurface *)This, srgb);
485 /* This function checks if the primary render target uses the 8bit paletted format. */
486 static BOOL primary_render_target_is_p8(IWineD3DDeviceImpl *device)
488 if (device->render_targets && device->render_targets[0]) {
489 IWineD3DSurfaceImpl* render_target = (IWineD3DSurfaceImpl*)device->render_targets[0];
490 if ((render_target->resource.usage & WINED3DUSAGE_RENDERTARGET)
491 && (render_target->resource.format_desc->format == WINED3DFMT_P8_UINT))
492 return TRUE;
494 return FALSE;
497 #undef GLINFO_LOCATION
499 #define GLINFO_LOCATION This->resource.device->adapter->gl_info
501 /* This call just downloads data, the caller is responsible for binding the
502 * correct texture. */
503 /* Context activation is done by the caller. */
504 static void surface_download_data(IWineD3DSurfaceImpl *This) {
505 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
507 /* Only support read back of converted P8 surfaces */
508 if (This->Flags & SFLAG_CONVERTED && format_desc->format != WINED3DFMT_P8_UINT)
510 FIXME("Read back converted textures unsupported, format=%s\n", debug_d3dformat(format_desc->format));
511 return;
514 ENTER_GL();
516 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
518 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
519 This, This->texture_level, format_desc->glFormat, format_desc->glType,
520 This->resource.allocatedMemory);
522 if (This->Flags & SFLAG_PBO)
524 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
525 checkGLcall("glBindBufferARB");
526 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target, This->texture_level, NULL));
527 checkGLcall("glGetCompressedTexImageARB");
528 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
529 checkGLcall("glBindBufferARB");
531 else
533 GL_EXTCALL(glGetCompressedTexImageARB(This->texture_target,
534 This->texture_level, This->resource.allocatedMemory));
535 checkGLcall("glGetCompressedTexImageARB");
538 LEAVE_GL();
539 } else {
540 void *mem;
541 GLenum format = format_desc->glFormat;
542 GLenum type = format_desc->glType;
543 int src_pitch = 0;
544 int dst_pitch = 0;
546 /* In case of P8 the index is stored in the alpha component if the primary render target uses P8 */
547 if (format_desc->format == WINED3DFMT_P8_UINT && primary_render_target_is_p8(This->resource.device))
549 format = GL_ALPHA;
550 type = GL_UNSIGNED_BYTE;
553 if (This->Flags & SFLAG_NONPOW2) {
554 unsigned char alignment = This->resource.device->surface_alignment;
555 src_pitch = format_desc->byte_count * This->pow2Width;
556 dst_pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This);
557 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
558 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * This->pow2Height);
559 } else {
560 mem = This->resource.allocatedMemory;
563 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
564 This, This->texture_level, format, type, mem);
566 if(This->Flags & SFLAG_PBO) {
567 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
568 checkGLcall("glBindBufferARB");
570 glGetTexImage(This->texture_target, This->texture_level, format, type, NULL);
571 checkGLcall("glGetTexImage");
573 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
574 checkGLcall("glBindBufferARB");
575 } else {
576 glGetTexImage(This->texture_target, This->texture_level, format, type, mem);
577 checkGLcall("glGetTexImage");
579 LEAVE_GL();
581 if (This->Flags & SFLAG_NONPOW2) {
582 const BYTE *src_data;
583 BYTE *dst_data;
584 UINT y;
586 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
587 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
588 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
590 * We're doing this...
592 * instead of boxing the texture :
593 * |<-texture width ->| -->pow2width| /\
594 * |111111111111111111| | |
595 * |222 Texture 222222| boxed empty | texture height
596 * |3333 Data 33333333| | |
597 * |444444444444444444| | \/
598 * ----------------------------------- |
599 * | boxed empty | boxed empty | pow2height
600 * | | | \/
601 * -----------------------------------
604 * we're repacking the data to the expected texture width
606 * |<-texture width ->| -->pow2width| /\
607 * |111111111111111111222222222222222| |
608 * |222333333333333333333444444444444| texture height
609 * |444444 | |
610 * | | \/
611 * | | |
612 * | empty | pow2height
613 * | | \/
614 * -----------------------------------
616 * == is the same as
618 * |<-texture width ->| /\
619 * |111111111111111111|
620 * |222222222222222222|texture height
621 * |333333333333333333|
622 * |444444444444444444| \/
623 * --------------------
625 * this also means that any references to allocatedMemory should work with the data as if were a
626 * standard texture with a non-power2 width instead of texture boxed up to be a power2 texture.
628 * internally the texture is still stored in a boxed format so any references to textureName will
629 * get a boxed texture with width pow2width and not a texture of width currentDesc.Width.
631 * Performance should not be an issue, because applications normally do not lock the surfaces when
632 * rendering. If an app does, the SFLAG_DYNLOCK flag will kick in and the memory copy won't be released,
633 * and doesn't have to be re-read.
635 src_data = mem;
636 dst_data = This->resource.allocatedMemory;
637 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", This, src_pitch, dst_pitch);
638 for (y = 1 ; y < This->currentDesc.Height; y++) {
639 /* skip the first row */
640 src_data += src_pitch;
641 dst_data += dst_pitch;
642 memcpy(dst_data, src_data, dst_pitch);
645 HeapFree(GetProcessHeap(), 0, mem);
649 /* Surface has now been downloaded */
650 This->Flags |= SFLAG_INSYSMEM;
653 /* This call just uploads data, the caller is responsible for binding the
654 * correct texture. */
655 /* Context activation is done by the caller. */
656 static void surface_upload_data(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *data) {
657 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
659 TRACE("This %p, internal %#x, width %d, height %d, format %#x, type %#x, data %p.\n",
660 This, internal, width, height, format, type, data);
661 TRACE("target %#x, level %u, resource size %u.\n",
662 This->texture_target, This->texture_level, This->resource.size);
664 if (format_desc->heightscale != 1.0f && format_desc->heightscale != 0.0f) height *= format_desc->heightscale;
666 ENTER_GL();
668 if (This->Flags & SFLAG_PBO)
670 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
671 checkGLcall("glBindBufferARB");
673 TRACE("(%p) pbo: %#x, data: %p.\n", This, This->pbo, data);
674 data = NULL;
677 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
679 TRACE("Calling glCompressedTexSubImage2DARB.\n");
681 GL_EXTCALL(glCompressedTexSubImage2DARB(This->texture_target, This->texture_level,
682 0, 0, width, height, internal, This->resource.size, data));
683 checkGLcall("glCompressedTexSubImage2DARB");
685 else
687 TRACE("Calling glTexSubImage2D.\n");
689 glTexSubImage2D(This->texture_target, This->texture_level,
690 0, 0, width, height, format, type, data);
691 checkGLcall("glTexSubImage2D");
694 if (This->Flags & SFLAG_PBO)
696 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
697 checkGLcall("glBindBufferARB");
700 LEAVE_GL();
703 /* This call just allocates the texture, the caller is responsible for binding
704 * the correct texture. */
705 /* Context activation is done by the caller. */
706 static void surface_allocate_surface(IWineD3DSurfaceImpl *This, GLenum internal, GLsizei width, GLsizei height, GLenum format, GLenum type) {
707 const struct wined3d_gl_info *gl_info = &This->resource.device->adapter->gl_info;
708 const struct GlPixelFormatDesc *format_desc = This->resource.format_desc;
709 BOOL enable_client_storage = FALSE;
710 const BYTE *mem = NULL;
712 if (format_desc->heightscale != 1.0f && format_desc->heightscale != 0.0f) height *= format_desc->heightscale;
714 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",
715 This, This->texture_target, This->texture_level, debug_d3dformat(format_desc->format),
716 internal, width, height, format, type);
718 ENTER_GL();
720 if (gl_info->supported[APPLE_CLIENT_STORAGE])
722 if(This->Flags & (SFLAG_NONPOW2 | SFLAG_DIBSECTION | SFLAG_CONVERTED) || This->resource.allocatedMemory == NULL) {
723 /* In some cases we want to disable client storage.
724 * SFLAG_NONPOW2 has a bigger opengl texture than the client memory, and different pitches
725 * SFLAG_DIBSECTION: Dibsections may have read / write protections on the memory. Avoid issues...
726 * SFLAG_CONVERTED: The conversion destination memory is freed after loading the surface
727 * allocatedMemory == NULL: Not defined in the extension. Seems to disable client storage effectively
729 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
730 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE)");
731 This->Flags &= ~SFLAG_CLIENT;
732 enable_client_storage = TRUE;
733 } else {
734 This->Flags |= SFLAG_CLIENT;
736 /* Point opengl to our allocated texture memory. Do not use resource.allocatedMemory here because
737 * it might point into a pbo. Instead use heapMemory, but get the alignment right.
739 mem = (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
743 if (format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED && mem)
745 GL_EXTCALL(glCompressedTexImage2DARB(This->texture_target, This->texture_level,
746 internal, width, height, 0, This->resource.size, mem));
748 else
750 glTexImage2D(This->texture_target, This->texture_level,
751 internal, width, height, 0, format, type, mem);
752 checkGLcall("glTexImage2D");
755 if(enable_client_storage) {
756 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
757 checkGLcall("glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE)");
759 LEAVE_GL();
762 /* In D3D the depth stencil dimensions have to be greater than or equal to the
763 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
764 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
765 /* GL locking is done by the caller */
766 void surface_set_compatible_renderbuffer(IWineD3DSurface *iface, unsigned int width, unsigned int height) {
767 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
768 const struct wined3d_gl_info *gl_info = &This->resource.device->adapter->gl_info;
769 renderbuffer_entry_t *entry;
770 GLuint renderbuffer = 0;
771 unsigned int src_width, src_height;
773 src_width = This->pow2Width;
774 src_height = This->pow2Height;
776 /* A depth stencil smaller than the render target is not valid */
777 if (width > src_width || height > src_height) return;
779 /* Remove any renderbuffer set if the sizes match */
780 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
781 || (width == src_width && height == src_height))
783 This->current_renderbuffer = NULL;
784 return;
787 /* Look if we've already got a renderbuffer of the correct dimensions */
788 LIST_FOR_EACH_ENTRY(entry, &This->renderbuffers, renderbuffer_entry_t, entry) {
789 if (entry->width == width && entry->height == height) {
790 renderbuffer = entry->id;
791 This->current_renderbuffer = entry;
792 break;
796 if (!renderbuffer) {
797 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
798 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
799 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
800 This->resource.format_desc->glInternal, width, height);
802 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(renderbuffer_entry_t));
803 entry->width = width;
804 entry->height = height;
805 entry->id = renderbuffer;
806 list_add_head(&This->renderbuffers, &entry->entry);
808 This->current_renderbuffer = entry;
811 checkGLcall("set_compatible_renderbuffer");
814 GLenum surface_get_gl_buffer(IWineD3DSurface *iface)
816 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
817 IWineD3DSwapChainImpl *swapchain = (IWineD3DSwapChainImpl *)This->container;
819 TRACE("iface %p.\n", iface);
821 if (!(This->Flags & SFLAG_SWAPCHAIN))
823 ERR("Surface %p is not on a swapchain.\n", iface);
824 return GL_NONE;
827 if (swapchain->backBuffer && swapchain->backBuffer[0] == iface)
829 if (swapchain->render_to_fbo)
831 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
832 return GL_COLOR_ATTACHMENT0;
834 TRACE("Returning GL_BACK\n");
835 return GL_BACK;
837 else if (swapchain->frontBuffer == iface)
839 TRACE("Returning GL_FRONT\n");
840 return GL_FRONT;
843 FIXME("Higher back buffer, returning GL_BACK\n");
844 return GL_BACK;
847 /* Slightly inefficient way to handle multiple dirty rects but it works :) */
848 void surface_add_dirty_rect(IWineD3DSurface *iface, const RECT *dirty_rect)
850 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
851 IWineD3DBaseTexture *baseTexture = NULL;
853 if (!(This->Flags & SFLAG_INSYSMEM) && (This->Flags & SFLAG_INTEXTURE))
854 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL /* no partial locking for textures yet */);
856 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
857 if (dirty_rect)
859 This->dirtyRect.left = min(This->dirtyRect.left, dirty_rect->left);
860 This->dirtyRect.top = min(This->dirtyRect.top, dirty_rect->top);
861 This->dirtyRect.right = max(This->dirtyRect.right, dirty_rect->right);
862 This->dirtyRect.bottom = max(This->dirtyRect.bottom, dirty_rect->bottom);
864 else
866 This->dirtyRect.left = 0;
867 This->dirtyRect.top = 0;
868 This->dirtyRect.right = This->currentDesc.Width;
869 This->dirtyRect.bottom = This->currentDesc.Height;
872 TRACE("(%p) : Dirty: yes, Rect:(%d, %d, %d, %d)\n", This, This->dirtyRect.left,
873 This->dirtyRect.top, This->dirtyRect.right, This->dirtyRect.bottom);
875 /* if the container is a basetexture then mark it dirty. */
876 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture)))
878 TRACE("Passing to container\n");
879 IWineD3DBaseTexture_SetDirty(baseTexture, TRUE);
880 IWineD3DBaseTexture_Release(baseTexture);
884 static inline BOOL surface_can_stretch_rect(IWineD3DSurfaceImpl *src, IWineD3DSurfaceImpl *dst)
886 return ((src->resource.format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE)
887 || (src->resource.usage & WINED3DUSAGE_RENDERTARGET))
888 && ((dst->resource.format_desc->Flags & WINED3DFMT_FLAG_FBO_ATTACHABLE)
889 || (dst->resource.usage & WINED3DUSAGE_RENDERTARGET))
890 && (src->resource.format_desc->format == dst->resource.format_desc->format
891 || (is_identity_fixup(src->resource.format_desc->color_fixup)
892 && is_identity_fixup(dst->resource.format_desc->color_fixup)));
895 static ULONG WINAPI IWineD3DSurfaceImpl_Release(IWineD3DSurface *iface)
897 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
898 ULONG ref = InterlockedDecrement(&This->resource.ref);
899 TRACE("(%p) : Releasing from %d\n", This, ref + 1);
901 if (!ref)
903 surface_cleanup(This);
904 This->resource.parent_ops->wined3d_object_destroyed(This->resource.parent);
906 TRACE("(%p) Released.\n", This);
907 HeapFree(GetProcessHeap(), 0, This);
910 return ref;
913 /* ****************************************************
914 IWineD3DSurface IWineD3DResource parts follow
915 **************************************************** */
917 void surface_internal_preload(IWineD3DSurface *iface, enum WINED3DSRGB srgb)
919 /* TODO: check for locks */
920 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
921 IWineD3DDeviceImpl *device = This->resource.device;
922 IWineD3DBaseTexture *baseTexture = NULL;
924 TRACE("(%p)Checking to see if the container is a base texture\n", This);
925 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
926 IWineD3DBaseTextureImpl *tex_impl = (IWineD3DBaseTextureImpl *) baseTexture;
927 TRACE("Passing to container\n");
928 tex_impl->baseTexture.internal_preload(baseTexture, srgb);
929 IWineD3DBaseTexture_Release(baseTexture);
930 } else {
931 struct wined3d_context *context = NULL;
933 TRACE("(%p) : About to load surface\n", This);
935 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
937 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
938 || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
940 if(palette9_changed(This)) {
941 TRACE("Reloading surface because the d3d8/9 palette was changed\n");
942 /* TODO: This is not necessarily needed with hw palettized texture support */
943 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
944 /* Make sure the texture is reloaded because of the palette change, this kills performance though :( */
945 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
949 IWineD3DSurface_LoadTexture(iface, srgb == SRGB_SRGB ? TRUE : FALSE);
951 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
952 /* Tell opengl to try and keep this texture in video ram (well mostly) */
953 GLclampf tmp;
954 tmp = 0.9f;
955 ENTER_GL();
956 glPrioritizeTextures(1, &This->texture_name, &tmp);
957 LEAVE_GL();
960 if (context) context_release(context);
964 static void WINAPI IWineD3DSurfaceImpl_PreLoad(IWineD3DSurface *iface) {
965 surface_internal_preload(iface, SRGB_ANY);
968 /* Context activation is done by the caller. */
969 static void surface_remove_pbo(IWineD3DSurfaceImpl *This) {
970 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
971 This->resource.allocatedMemory =
972 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
974 ENTER_GL();
975 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
976 checkGLcall("glBindBufferARB(GL_PIXEL_UNPACK_BUFFER, This->pbo)");
977 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, This->resource.size, This->resource.allocatedMemory));
978 checkGLcall("glGetBufferSubDataARB");
979 GL_EXTCALL(glDeleteBuffersARB(1, &This->pbo));
980 checkGLcall("glDeleteBuffersARB");
981 LEAVE_GL();
983 This->pbo = 0;
984 This->Flags &= ~SFLAG_PBO;
987 BOOL surface_init_sysmem(IWineD3DSurface *iface)
989 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
991 if(!This->resource.allocatedMemory)
993 This->resource.heapMemory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->resource.size + RESOURCE_ALIGNMENT);
994 if(!This->resource.heapMemory)
996 ERR("Out of memory\n");
997 return FALSE;
999 This->resource.allocatedMemory =
1000 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1002 else
1004 memset(This->resource.allocatedMemory, 0, This->resource.size);
1007 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
1008 return TRUE;
1011 static void WINAPI IWineD3DSurfaceImpl_UnLoad(IWineD3DSurface *iface) {
1012 IWineD3DBaseTexture *texture = NULL;
1013 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
1014 IWineD3DDeviceImpl *device = This->resource.device;
1015 const struct wined3d_gl_info *gl_info;
1016 renderbuffer_entry_t *entry, *entry2;
1017 struct wined3d_context *context;
1019 TRACE("(%p)\n", iface);
1021 if(This->resource.pool == WINED3DPOOL_DEFAULT) {
1022 /* Default pool resources are supposed to be destroyed before Reset is called.
1023 * Implicit resources stay however. So this means we have an implicit render target
1024 * or depth stencil. The content may be destroyed, but we still have to tear down
1025 * opengl resources, so we cannot leave early.
1027 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1028 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1029 * or the depth stencil into an FBO the texture or render buffer will be removed
1030 * and all flags get lost
1032 surface_init_sysmem(iface);
1033 } else {
1034 /* Load the surface into system memory */
1035 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
1036 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
1038 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
1039 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSRGBTEX, FALSE);
1040 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
1042 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
1043 gl_info = context->gl_info;
1045 /* Destroy PBOs, but load them into real sysmem before */
1046 if(This->Flags & SFLAG_PBO) {
1047 surface_remove_pbo(This);
1050 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1051 * all application-created targets the application has to release the surface
1052 * before calling _Reset
1054 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &This->renderbuffers, renderbuffer_entry_t, entry) {
1055 ENTER_GL();
1056 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1057 LEAVE_GL();
1058 list_remove(&entry->entry);
1059 HeapFree(GetProcessHeap(), 0, entry);
1061 list_init(&This->renderbuffers);
1062 This->current_renderbuffer = NULL;
1064 /* If we're in a texture, the texture name belongs to the texture. Otherwise,
1065 * destroy it
1067 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **) &texture);
1068 if(!texture) {
1069 ENTER_GL();
1070 glDeleteTextures(1, &This->texture_name);
1071 This->texture_name = 0;
1072 glDeleteTextures(1, &This->texture_name_srgb);
1073 This->texture_name_srgb = 0;
1074 LEAVE_GL();
1075 } else {
1076 IWineD3DBaseTexture_Release(texture);
1079 context_release(context);
1082 /* ******************************************************
1083 IWineD3DSurface IWineD3DSurface parts follow
1084 ****************************************************** */
1086 /* Read the framebuffer back into the surface */
1087 static void read_from_framebuffer(IWineD3DSurfaceImpl *This, const RECT *rect, void *dest, UINT pitch)
1089 IWineD3DDeviceImpl *myDevice = This->resource.device;
1090 struct wined3d_context *context;
1091 BYTE *mem;
1092 GLint fmt;
1093 GLint type;
1094 BYTE *row, *top, *bottom;
1095 int i;
1096 BOOL bpp;
1097 RECT local_rect;
1098 BOOL srcIsUpsideDown;
1099 GLint rowLen = 0;
1100 GLint skipPix = 0;
1101 GLint skipRow = 0;
1103 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1104 static BOOL warned = FALSE;
1105 if(!warned) {
1106 ERR("The application tries to lock the render target, but render target locking is disabled\n");
1107 warned = TRUE;
1109 return;
1112 /* Activate the surface. Set it up for blitting now, although not necessarily needed for LockRect.
1113 * Certain graphics drivers seem to dislike some enabled states when reading from opengl, the blitting usage
1114 * should help here. Furthermore unlockrect will need the context set up for blitting. The context manager will find
1115 * context->last_was_blit set on the unlock.
1117 context = context_acquire(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1118 ENTER_GL();
1120 /* Select the correct read buffer, and give some debug output.
1121 * There is no need to keep track of the current read buffer or reset it, every part of the code
1122 * that reads sets the read buffer as desired.
1124 if (surface_is_offscreen((IWineD3DSurface *) This))
1126 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1127 * Read from the back buffer
1129 TRACE("Locking offscreen render target\n");
1130 glReadBuffer(myDevice->offscreenBuffer);
1131 srcIsUpsideDown = TRUE;
1133 else
1135 /* Onscreen surfaces are always part of a swapchain */
1136 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This);
1137 TRACE("Locking %#x buffer\n", buffer);
1138 glReadBuffer(buffer);
1139 checkGLcall("glReadBuffer");
1140 srcIsUpsideDown = FALSE;
1143 /* TODO: Get rid of the extra rectangle comparison and construction of a full surface rectangle */
1144 if(!rect) {
1145 local_rect.left = 0;
1146 local_rect.top = 0;
1147 local_rect.right = This->currentDesc.Width;
1148 local_rect.bottom = This->currentDesc.Height;
1149 } else {
1150 local_rect = *rect;
1152 /* TODO: Get rid of the extra GetPitch call, LockRect does that too. Cache the pitch */
1154 switch(This->resource.format_desc->format)
1156 case WINED3DFMT_P8_UINT:
1158 if(primary_render_target_is_p8(myDevice)) {
1159 /* In case of P8 render targets the index is stored in the alpha component */
1160 fmt = GL_ALPHA;
1161 type = GL_UNSIGNED_BYTE;
1162 mem = dest;
1163 bpp = This->resource.format_desc->byte_count;
1164 } else {
1165 /* GL can't return palettized data, so read ARGB pixels into a
1166 * separate block of memory and convert them into palettized format
1167 * in software. Slow, but if the app means to use palettized render
1168 * targets and locks it...
1170 * Use GL_RGB, GL_UNSIGNED_BYTE to read the surface for performance reasons
1171 * Don't use GL_BGR as in the WINED3DFMT_R8G8B8 case, instead watch out
1172 * for the color channels when palettizing the colors.
1174 fmt = GL_RGB;
1175 type = GL_UNSIGNED_BYTE;
1176 pitch *= 3;
1177 mem = HeapAlloc(GetProcessHeap(), 0, This->resource.size * 3);
1178 if(!mem) {
1179 ERR("Out of memory\n");
1180 LEAVE_GL();
1181 return;
1183 bpp = This->resource.format_desc->byte_count * 3;
1186 break;
1188 default:
1189 mem = dest;
1190 fmt = This->resource.format_desc->glFormat;
1191 type = This->resource.format_desc->glType;
1192 bpp = This->resource.format_desc->byte_count;
1195 if(This->Flags & SFLAG_PBO) {
1196 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, This->pbo));
1197 checkGLcall("glBindBufferARB");
1198 if(mem != NULL) {
1199 ERR("mem not null for pbo -- unexpected\n");
1200 mem = NULL;
1204 /* Save old pixel store pack state */
1205 glGetIntegerv(GL_PACK_ROW_LENGTH, &rowLen);
1206 checkGLcall("glGetIntegerv");
1207 glGetIntegerv(GL_PACK_SKIP_PIXELS, &skipPix);
1208 checkGLcall("glGetIntegerv");
1209 glGetIntegerv(GL_PACK_SKIP_ROWS, &skipRow);
1210 checkGLcall("glGetIntegerv");
1212 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1213 glPixelStorei(GL_PACK_ROW_LENGTH, This->currentDesc.Width);
1214 checkGLcall("glPixelStorei");
1215 glPixelStorei(GL_PACK_SKIP_PIXELS, local_rect.left);
1216 checkGLcall("glPixelStorei");
1217 glPixelStorei(GL_PACK_SKIP_ROWS, local_rect.top);
1218 checkGLcall("glPixelStorei");
1220 glReadPixels(local_rect.left, (!srcIsUpsideDown) ? (This->currentDesc.Height - local_rect.bottom) : local_rect.top ,
1221 local_rect.right - local_rect.left,
1222 local_rect.bottom - local_rect.top,
1223 fmt, type, mem);
1224 checkGLcall("glReadPixels");
1226 /* Reset previous pixel store pack state */
1227 glPixelStorei(GL_PACK_ROW_LENGTH, rowLen);
1228 checkGLcall("glPixelStorei");
1229 glPixelStorei(GL_PACK_SKIP_PIXELS, skipPix);
1230 checkGLcall("glPixelStorei");
1231 glPixelStorei(GL_PACK_SKIP_ROWS, skipRow);
1232 checkGLcall("glPixelStorei");
1234 if(This->Flags & SFLAG_PBO) {
1235 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1236 checkGLcall("glBindBufferARB");
1238 /* Check if we need to flip the image. If we need to flip use glMapBufferARB
1239 * to get a pointer to it and perform the flipping in software. This is a lot
1240 * faster than calling glReadPixels for each line. In case we want more speed
1241 * we should rerender it flipped in a FBO and read the data back from the FBO. */
1242 if(!srcIsUpsideDown) {
1243 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1244 checkGLcall("glBindBufferARB");
1246 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1247 checkGLcall("glMapBufferARB");
1251 /* TODO: Merge this with the palettization loop below for P8 targets */
1252 if(!srcIsUpsideDown) {
1253 UINT len, off;
1254 /* glReadPixels returns the image upside down, and there is no way to prevent this.
1255 Flip the lines in software */
1256 len = (local_rect.right - local_rect.left) * bpp;
1257 off = local_rect.left * bpp;
1259 row = HeapAlloc(GetProcessHeap(), 0, len);
1260 if(!row) {
1261 ERR("Out of memory\n");
1262 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT) HeapFree(GetProcessHeap(), 0, mem);
1263 LEAVE_GL();
1264 return;
1267 top = mem + pitch * local_rect.top;
1268 bottom = mem + pitch * (local_rect.bottom - 1);
1269 for(i = 0; i < (local_rect.bottom - local_rect.top) / 2; i++) {
1270 memcpy(row, top + off, len);
1271 memcpy(top + off, bottom + off, len);
1272 memcpy(bottom + off, row, len);
1273 top += pitch;
1274 bottom -= pitch;
1276 HeapFree(GetProcessHeap(), 0, row);
1278 /* Unmap the temp PBO buffer */
1279 if(This->Flags & SFLAG_PBO) {
1280 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1281 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1285 LEAVE_GL();
1286 context_release(context);
1288 /* For P8 textures we need to perform an inverse palette lookup. This is done by searching for a palette
1289 * index which matches the RGB value. Note this isn't guaranteed to work when there are multiple entries for
1290 * the same color but we have no choice.
1291 * In case of P8 render targets, the index is stored in the alpha component so no conversion is needed.
1293 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT && !primary_render_target_is_p8(myDevice))
1295 const PALETTEENTRY *pal = NULL;
1296 DWORD width = pitch / 3;
1297 int x, y, c;
1299 if(This->palette) {
1300 pal = This->palette->palents;
1301 } else {
1302 ERR("Palette is missing, cannot perform inverse palette lookup\n");
1303 HeapFree(GetProcessHeap(), 0, mem);
1304 return ;
1307 for(y = local_rect.top; y < local_rect.bottom; y++) {
1308 for(x = local_rect.left; x < local_rect.right; x++) {
1309 /* start lines pixels */
1310 const BYTE *blue = mem + y * pitch + x * (sizeof(BYTE) * 3);
1311 const BYTE *green = blue + 1;
1312 const BYTE *red = green + 1;
1314 for(c = 0; c < 256; c++) {
1315 if(*red == pal[c].peRed &&
1316 *green == pal[c].peGreen &&
1317 *blue == pal[c].peBlue)
1319 *((BYTE *) dest + y * width + x) = c;
1320 break;
1325 HeapFree(GetProcessHeap(), 0, mem);
1329 /* Read the framebuffer contents into a texture */
1330 static void read_from_framebuffer_texture(IWineD3DSurfaceImpl *This, BOOL srgb)
1332 IWineD3DDeviceImpl *device = This->resource.device;
1333 struct wined3d_context *context;
1334 int bpp;
1335 GLenum format, internal, type;
1336 CONVERT_TYPES convert;
1337 GLint prevRead;
1338 BOOL alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
1340 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */, &format, &internal, &type, &convert, &bpp, srgb);
1342 /* Activate the surface to read from. In some situations it isn't the currently active target(e.g. backbuffer
1343 * locking during offscreen rendering). RESOURCELOAD is ok because glCopyTexSubImage2D isn't affected by any
1344 * states in the stateblock, and no driver was found yet that had bugs in that regard.
1346 context = context_acquire(device, (IWineD3DSurface *) This, CTXUSAGE_RESOURCELOAD);
1347 surface_bind_and_dirtify(This, srgb);
1349 ENTER_GL();
1350 glGetIntegerv(GL_READ_BUFFER, &prevRead);
1351 LEAVE_GL();
1353 /* Select the correct read buffer, and give some debug output.
1354 * There is no need to keep track of the current read buffer or reset it, every part of the code
1355 * that reads sets the read buffer as desired.
1357 if (!surface_is_offscreen((IWineD3DSurface *)This))
1359 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This);
1360 TRACE("Locking %#x buffer\n", buffer);
1362 ENTER_GL();
1363 glReadBuffer(buffer);
1364 checkGLcall("glReadBuffer");
1365 LEAVE_GL();
1367 else
1369 /* Locking the primary render target which is not on a swapchain(=offscreen render target).
1370 * Read from the back buffer
1372 TRACE("Locking offscreen render target\n");
1373 ENTER_GL();
1374 glReadBuffer(device->offscreenBuffer);
1375 checkGLcall("glReadBuffer");
1376 LEAVE_GL();
1379 if(!(This->Flags & alloc_flag)) {
1380 surface_allocate_surface(This, internal, This->pow2Width,
1381 This->pow2Height, format, type);
1382 This->Flags |= alloc_flag;
1385 ENTER_GL();
1386 /* If !SrcIsUpsideDown we should flip the surface.
1387 * This can be done using glCopyTexSubImage2D but this
1388 * is VERY slow, so don't do that. We should prevent
1389 * this code from getting called in such cases or perhaps
1390 * we can use FBOs */
1392 glCopyTexSubImage2D(This->texture_target, This->texture_level,
1393 0, 0, 0, 0, This->currentDesc.Width, This->currentDesc.Height);
1394 checkGLcall("glCopyTexSubImage2D");
1396 glReadBuffer(prevRead);
1397 checkGLcall("glReadBuffer");
1399 LEAVE_GL();
1401 context_release(context);
1403 TRACE("Updated target %d\n", This->texture_target);
1406 /* Context activation is done by the caller. */
1407 void surface_prepare_texture(IWineD3DSurfaceImpl *surface, BOOL srgb)
1409 DWORD alloc_flag = srgb ? SFLAG_SRGBALLOCATED : SFLAG_ALLOCATED;
1410 GLenum format, internal, type;
1411 GLsizei width, height;
1412 CONVERT_TYPES convert;
1413 int bpp;
1415 if (surface->Flags & alloc_flag) return;
1417 d3dfmt_get_conv(surface, TRUE, TRUE, &format, &internal, &type, &convert, &bpp, srgb);
1418 if(convert != NO_CONVERSION) surface->Flags |= SFLAG_CONVERTED;
1419 else surface->Flags &= ~SFLAG_CONVERTED;
1421 if (surface->Flags & SFLAG_NONPOW2)
1423 width = surface->pow2Width;
1424 height = surface->pow2Height;
1426 else
1428 width = surface->glRect.right - surface->glRect.left;
1429 height = surface->glRect.bottom - surface->glRect.top;
1432 surface_bind_and_dirtify(surface, srgb);
1433 surface_allocate_surface(surface, internal, width, height, format, type);
1434 surface->Flags |= alloc_flag;
1437 static void surface_prepare_system_memory(IWineD3DSurfaceImpl *This)
1439 IWineD3DDeviceImpl *device = This->resource.device;
1440 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1442 /* Performance optimization: Count how often a surface is locked, if it is locked regularly do not throw away the system memory copy.
1443 * This avoids the need to download the surface from opengl all the time. The surface is still downloaded if the opengl texture is
1444 * changed
1446 if(!(This->Flags & SFLAG_DYNLOCK)) {
1447 This->lockCount++;
1448 /* MAXLOCKCOUNT is defined in wined3d_private.h */
1449 if(This->lockCount > MAXLOCKCOUNT) {
1450 TRACE("Surface is locked regularly, not freeing the system memory copy any more\n");
1451 This->Flags |= SFLAG_DYNLOCK;
1455 /* Create a PBO for dynamically locked surfaces but don't do it for converted or non-pow2 surfaces.
1456 * Also don't create a PBO for systemmem surfaces.
1458 if (gl_info->supported[ARB_PIXEL_BUFFER_OBJECT] && (This->Flags & SFLAG_DYNLOCK)
1459 && !(This->Flags & (SFLAG_PBO | SFLAG_CONVERTED | SFLAG_NONPOW2))
1460 && (This->resource.pool != WINED3DPOOL_SYSTEMMEM))
1462 GLenum error;
1463 struct wined3d_context *context;
1465 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
1466 ENTER_GL();
1468 GL_EXTCALL(glGenBuffersARB(1, &This->pbo));
1469 error = glGetError();
1470 if(This->pbo == 0 || error != GL_NO_ERROR) {
1471 ERR("Failed to bind the PBO with error %s (%#x)\n", debug_glerror(error), error);
1474 TRACE("Attaching pbo=%#x to (%p)\n", This->pbo, This);
1476 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1477 checkGLcall("glBindBufferARB");
1479 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->resource.size + 4, This->resource.allocatedMemory, GL_STREAM_DRAW_ARB));
1480 checkGLcall("glBufferDataARB");
1482 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1483 checkGLcall("glBindBufferARB");
1485 /* We don't need the system memory anymore and we can't even use it for PBOs */
1486 if(!(This->Flags & SFLAG_CLIENT)) {
1487 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
1488 This->resource.heapMemory = NULL;
1490 This->resource.allocatedMemory = NULL;
1491 This->Flags |= SFLAG_PBO;
1492 LEAVE_GL();
1493 context_release(context);
1495 else if (!(This->resource.allocatedMemory || This->Flags & SFLAG_PBO))
1497 /* Whatever surface we have, make sure that there is memory allocated for the downloaded copy,
1498 * or a pbo to map
1500 if(!This->resource.heapMemory) {
1501 This->resource.heapMemory = HeapAlloc(GetProcessHeap() ,0 , This->resource.size + RESOURCE_ALIGNMENT);
1503 This->resource.allocatedMemory =
1504 (BYTE *)(((ULONG_PTR) This->resource.heapMemory + (RESOURCE_ALIGNMENT - 1)) & ~(RESOURCE_ALIGNMENT - 1));
1505 if(This->Flags & SFLAG_INSYSMEM) {
1506 ERR("Surface without memory or pbo has SFLAG_INSYSMEM set!\n");
1511 static HRESULT WINAPI IWineD3DSurfaceImpl_LockRect(IWineD3DSurface *iface, WINED3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
1512 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1513 IWineD3DDeviceImpl *myDevice = This->resource.device;
1514 const RECT *pass_rect = pRect;
1516 TRACE("(%p) : rect@%p flags(%08x), output lockedRect@%p, memory@%p\n", This, pRect, Flags, pLockedRect, This->resource.allocatedMemory);
1518 /* This is also done in the base class, but we have to verify this before loading any data from
1519 * gl into the sysmem copy. The PBO may be mapped, a different rectangle locked, the discard flag
1520 * may interfere, and all other bad things may happen
1522 if (This->Flags & SFLAG_LOCKED) {
1523 WARN("Surface is already locked, returning D3DERR_INVALIDCALL\n");
1524 return WINED3DERR_INVALIDCALL;
1526 This->Flags |= SFLAG_LOCKED;
1528 if (!(This->Flags & SFLAG_LOCKABLE))
1530 TRACE("Warning: trying to lock unlockable surf@%p\n", This);
1533 if (Flags & WINED3DLOCK_DISCARD) {
1534 /* Set SFLAG_INSYSMEM, so we'll never try to download the data from the texture. */
1535 TRACE("WINED3DLOCK_DISCARD flag passed, marking local copy as up to date\n");
1536 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1537 This->Flags |= SFLAG_INSYSMEM;
1538 goto lock_end;
1541 if (This->Flags & SFLAG_INSYSMEM) {
1542 TRACE("Local copy is up to date, not downloading data\n");
1543 surface_prepare_system_memory(This); /* Makes sure memory is allocated */
1544 goto lock_end;
1547 /* IWineD3DSurface_LoadLocation() does not check if the rectangle specifies
1548 * the full surface. Most callers don't need that, so do it here. */
1549 if (pRect && pRect->top == 0 && pRect->left == 0
1550 && pRect->right == This->currentDesc.Width
1551 && pRect->bottom == This->currentDesc.Height)
1553 pass_rect = NULL;
1556 if (!(wined3d_settings.rendertargetlock_mode == RTL_DISABLE
1557 && ((This->Flags & SFLAG_SWAPCHAIN) || iface == myDevice->render_targets[0])))
1559 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, pass_rect);
1562 lock_end:
1563 if (This->Flags & SFLAG_PBO)
1565 struct wined3d_context *context;
1567 context = context_acquire(myDevice, NULL, CTXUSAGE_RESOURCELOAD);
1568 ENTER_GL();
1569 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1570 checkGLcall("glBindBufferARB");
1572 /* This shouldn't happen but could occur if some other function didn't handle the PBO properly */
1573 if(This->resource.allocatedMemory) {
1574 ERR("The surface already has PBO memory allocated!\n");
1577 This->resource.allocatedMemory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
1578 checkGLcall("glMapBufferARB");
1580 /* Make sure the pbo isn't set anymore in order not to break non-pbo calls */
1581 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1582 checkGLcall("glBindBufferARB");
1584 LEAVE_GL();
1585 context_release(context);
1588 if (Flags & (WINED3DLOCK_NO_DIRTY_UPDATE | WINED3DLOCK_READONLY)) {
1589 /* Don't dirtify */
1590 } else {
1591 IWineD3DBaseTexture *pBaseTexture;
1593 * Dirtify on lock
1594 * as seen in msdn docs
1596 surface_add_dirty_rect(iface, pRect);
1598 /** Dirtify Container if needed */
1599 if (SUCCEEDED(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&pBaseTexture))) {
1600 TRACE("Making container dirty\n");
1601 IWineD3DBaseTexture_SetDirty(pBaseTexture, TRUE);
1602 IWineD3DBaseTexture_Release(pBaseTexture);
1603 } else {
1604 TRACE("Surface is standalone, no need to dirty the container\n");
1608 return IWineD3DBaseSurfaceImpl_LockRect(iface, pLockedRect, pRect, Flags);
1611 static void flush_to_framebuffer_drawpixels(IWineD3DSurfaceImpl *This, GLenum fmt, GLenum type, UINT bpp, const BYTE *mem) {
1612 GLint prev_store;
1613 GLint prev_rasterpos[4];
1614 GLint skipBytes = 0;
1615 UINT pitch = IWineD3DSurface_GetPitch((IWineD3DSurface *) This); /* target is argb, 4 byte */
1616 IWineD3DDeviceImpl *myDevice = This->resource.device;
1617 struct wined3d_context *context;
1619 /* Activate the correct context for the render target */
1620 context = context_acquire(myDevice, (IWineD3DSurface *) This, CTXUSAGE_BLIT);
1621 ENTER_GL();
1623 if (!surface_is_offscreen((IWineD3DSurface *)This))
1625 GLenum buffer = surface_get_gl_buffer((IWineD3DSurface *)This);
1626 TRACE("Unlocking %#x buffer.\n", buffer);
1627 context_set_draw_buffer(context, buffer);
1629 else
1631 /* Primary offscreen render target */
1632 TRACE("Offscreen render target.\n");
1633 context_set_draw_buffer(context, myDevice->offscreenBuffer);
1636 glGetIntegerv(GL_PACK_SWAP_BYTES, &prev_store);
1637 checkGLcall("glGetIntegerv");
1638 glGetIntegerv(GL_CURRENT_RASTER_POSITION, &prev_rasterpos[0]);
1639 checkGLcall("glGetIntegerv");
1640 glPixelZoom(1.0f, -1.0f);
1641 checkGLcall("glPixelZoom");
1643 /* If not fullscreen, we need to skip a number of bytes to find the next row of data */
1644 glGetIntegerv(GL_UNPACK_ROW_LENGTH, &skipBytes);
1645 glPixelStorei(GL_UNPACK_ROW_LENGTH, This->currentDesc.Width);
1647 glRasterPos3i(This->lockedRect.left, This->lockedRect.top, 1);
1648 checkGLcall("glRasterPos3i");
1650 /* Some drivers(radeon dri, others?) don't like exceptions during
1651 * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
1652 * after ReleaseDC. Reading it will cause an exception, which x11drv will
1653 * catch to put the dib section in InSync mode, which leads to a crash
1654 * and a blocked x server on my radeon card.
1656 * The following lines read the dib section so it is put in InSync mode
1657 * before glDrawPixels is called and the crash is prevented. There won't
1658 * be any interfering gdi accesses, because UnlockRect is called from
1659 * ReleaseDC, and the app won't use the dc any more afterwards.
1661 if((This->Flags & SFLAG_DIBSECTION) && !(This->Flags & SFLAG_PBO)) {
1662 volatile BYTE read;
1663 read = This->resource.allocatedMemory[0];
1666 if(This->Flags & SFLAG_PBO) {
1667 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1668 checkGLcall("glBindBufferARB");
1671 /* When the surface is locked we only have to refresh the locked part else we need to update the whole image */
1672 if(This->Flags & SFLAG_LOCKED) {
1673 glDrawPixels(This->lockedRect.right - This->lockedRect.left,
1674 (This->lockedRect.bottom - This->lockedRect.top)-1,
1675 fmt, type,
1676 mem + bpp * This->lockedRect.left + pitch * This->lockedRect.top);
1677 checkGLcall("glDrawPixels");
1678 } else {
1679 glDrawPixels(This->currentDesc.Width,
1680 This->currentDesc.Height,
1681 fmt, type, mem);
1682 checkGLcall("glDrawPixels");
1685 if(This->Flags & SFLAG_PBO) {
1686 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1687 checkGLcall("glBindBufferARB");
1690 glPixelZoom(1.0f, 1.0f);
1691 checkGLcall("glPixelZoom");
1693 glRasterPos3iv(&prev_rasterpos[0]);
1694 checkGLcall("glRasterPos3iv");
1696 /* Reset to previous pack row length */
1697 glPixelStorei(GL_UNPACK_ROW_LENGTH, skipBytes);
1698 checkGLcall("glPixelStorei(GL_UNPACK_ROW_LENGTH)");
1700 LEAVE_GL();
1701 context_release(context);
1704 static HRESULT WINAPI IWineD3DSurfaceImpl_UnlockRect(IWineD3DSurface *iface) {
1705 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1706 IWineD3DDeviceImpl *myDevice = This->resource.device;
1707 BOOL fullsurface;
1709 if (!(This->Flags & SFLAG_LOCKED)) {
1710 WARN("trying to Unlock an unlocked surf@%p\n", This);
1711 return WINEDDERR_NOTLOCKED;
1714 if (This->Flags & SFLAG_PBO)
1716 struct wined3d_context *context;
1718 TRACE("Freeing PBO memory\n");
1720 context = context_acquire(myDevice, NULL, CTXUSAGE_RESOURCELOAD);
1721 ENTER_GL();
1722 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, This->pbo));
1723 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
1724 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1725 checkGLcall("glUnmapBufferARB");
1726 LEAVE_GL();
1727 context_release(context);
1729 This->resource.allocatedMemory = NULL;
1732 TRACE("(%p) : dirtyfied(%d)\n", This, This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE) ? 0 : 1);
1734 if (This->Flags & (SFLAG_INDRAWABLE | SFLAG_INTEXTURE)) {
1735 TRACE("(%p) : Not Dirtified so nothing to do, return now\n", This);
1736 goto unlock_end;
1739 if ((This->Flags & SFLAG_SWAPCHAIN) || (myDevice->render_targets && iface == myDevice->render_targets[0]))
1741 if(wined3d_settings.rendertargetlock_mode == RTL_DISABLE) {
1742 static BOOL warned = FALSE;
1743 if(!warned) {
1744 ERR("The application tries to write to the render target, but render target locking is disabled\n");
1745 warned = TRUE;
1747 goto unlock_end;
1750 if(This->dirtyRect.left == 0 &&
1751 This->dirtyRect.top == 0 &&
1752 This->dirtyRect.right == This->currentDesc.Width &&
1753 This->dirtyRect.bottom == This->currentDesc.Height) {
1754 fullsurface = TRUE;
1755 } else {
1756 /* TODO: Proper partial rectangle tracking */
1757 fullsurface = FALSE;
1758 This->Flags |= SFLAG_INSYSMEM;
1761 switch(wined3d_settings.rendertargetlock_mode) {
1762 case RTL_READTEX:
1763 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL /* partial texture loading not supported yet */);
1764 /* drop through */
1766 case RTL_READDRAW:
1767 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, fullsurface ? NULL : &This->dirtyRect);
1768 break;
1771 if(!fullsurface) {
1772 /* Partial rectangle tracking is not commonly implemented, it is only done for render targets. Overwrite
1773 * the flags to bring them back into a sane state. INSYSMEM was set before to tell LoadLocation where
1774 * to read the rectangle from. Indrawable is set because all modifications from the partial sysmem copy
1775 * are written back to the drawable, thus the surface is merged again in the drawable. The sysmem copy is
1776 * not fully up to date because only a subrectangle was read in LockRect.
1778 This->Flags &= ~SFLAG_INSYSMEM;
1779 This->Flags |= SFLAG_INDRAWABLE;
1782 This->dirtyRect.left = This->currentDesc.Width;
1783 This->dirtyRect.top = This->currentDesc.Height;
1784 This->dirtyRect.right = 0;
1785 This->dirtyRect.bottom = 0;
1786 } else if(iface == myDevice->stencilBufferTarget) {
1787 FIXME("Depth Stencil buffer locking is not implemented\n");
1788 } else {
1789 /* The rest should be a normal texture */
1790 IWineD3DBaseTextureImpl *impl;
1791 /* Check if the texture is bound, if yes dirtify the sampler to force a re-upload of the texture
1792 * Can't load the texture here because PreLoad may destroy and recreate the gl texture, so sampler
1793 * states need resetting
1795 if(IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&impl) == WINED3D_OK) {
1796 if(impl->baseTexture.bindCount) {
1797 IWineD3DDeviceImpl_MarkStateDirty(myDevice, STATE_SAMPLER(impl->baseTexture.sampler));
1799 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *) impl);
1803 unlock_end:
1804 This->Flags &= ~SFLAG_LOCKED;
1805 memset(&This->lockedRect, 0, sizeof(RECT));
1807 /* Overlays have to be redrawn manually after changes with the GL implementation */
1808 if(This->overlay_dest) {
1809 IWineD3DSurface_DrawOverlay(iface);
1811 return WINED3D_OK;
1814 static void surface_release_client_storage(IWineD3DSurface *iface)
1816 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
1817 struct wined3d_context *context;
1819 context = context_acquire(This->resource.device, NULL, CTXUSAGE_RESOURCELOAD);
1821 ENTER_GL();
1822 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_FALSE);
1823 if(This->texture_name)
1825 surface_bind_and_dirtify(This, FALSE);
1826 glTexImage2D(This->texture_target, This->texture_level,
1827 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
1829 if(This->texture_name_srgb)
1831 surface_bind_and_dirtify(This, TRUE);
1832 glTexImage2D(This->texture_target, This->texture_level,
1833 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
1835 glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE);
1837 LEAVE_GL();
1838 context_release(context);
1840 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSRGBTEX, FALSE);
1841 IWineD3DSurface_ModifyLocation(iface, SFLAG_INTEXTURE, FALSE);
1842 surface_force_reload(iface);
1845 static HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC)
1847 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1848 WINED3DLOCKED_RECT lock;
1849 HRESULT hr;
1850 RGBQUAD col[256];
1852 TRACE("(%p)->(%p)\n",This,pHDC);
1854 if(This->Flags & SFLAG_USERPTR) {
1855 ERR("Not supported on surfaces with an application-provided surfaces\n");
1856 return WINEDDERR_NODC;
1859 /* Give more detailed info for ddraw */
1860 if (This->Flags & SFLAG_DCINUSE)
1861 return WINEDDERR_DCALREADYCREATED;
1863 /* Can't GetDC if the surface is locked */
1864 if (This->Flags & SFLAG_LOCKED)
1865 return WINED3DERR_INVALIDCALL;
1867 memset(&lock, 0, sizeof(lock)); /* To be sure */
1869 /* Create a DIB section if there isn't a hdc yet */
1870 if(!This->hDC) {
1871 if(This->Flags & SFLAG_CLIENT) {
1872 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
1873 surface_release_client_storage(iface);
1875 hr = IWineD3DBaseSurfaceImpl_CreateDIBSection(iface);
1876 if(FAILED(hr)) return WINED3DERR_INVALIDCALL;
1878 /* Use the dib section from now on if we are not using a PBO */
1879 if(!(This->Flags & SFLAG_PBO))
1880 This->resource.allocatedMemory = This->dib.bitmap_data;
1883 /* Lock the surface */
1884 hr = IWineD3DSurface_LockRect(iface,
1885 &lock,
1886 NULL,
1889 if(This->Flags & SFLAG_PBO) {
1890 /* Sync the DIB with the PBO. This can't be done earlier because LockRect activates the allocatedMemory */
1891 memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->dib.bitmap_size);
1894 if(FAILED(hr)) {
1895 ERR("IWineD3DSurface_LockRect failed with hr = %08x\n", hr);
1896 /* keep the dib section */
1897 return hr;
1900 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
1901 || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
1903 /* GetDC on palettized formats is unsupported in D3D9, and the method is missing in
1904 D3D8, so this should only be used for DX <=7 surfaces (with non-device palettes) */
1905 unsigned int n;
1906 const PALETTEENTRY *pal = NULL;
1908 if(This->palette) {
1909 pal = This->palette->palents;
1910 } else {
1911 IWineD3DSurfaceImpl *dds_primary;
1912 IWineD3DSwapChainImpl *swapchain;
1913 swapchain = (IWineD3DSwapChainImpl *)This->resource.device->swapchains[0];
1914 dds_primary = (IWineD3DSurfaceImpl *)swapchain->frontBuffer;
1915 if (dds_primary && dds_primary->palette)
1916 pal = dds_primary->palette->palents;
1919 if (pal) {
1920 for (n=0; n<256; n++) {
1921 col[n].rgbRed = pal[n].peRed;
1922 col[n].rgbGreen = pal[n].peGreen;
1923 col[n].rgbBlue = pal[n].peBlue;
1924 col[n].rgbReserved = 0;
1926 SetDIBColorTable(This->hDC, 0, 256, col);
1930 *pHDC = This->hDC;
1931 TRACE("returning %p\n",*pHDC);
1932 This->Flags |= SFLAG_DCINUSE;
1934 return WINED3D_OK;
1937 static HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC)
1939 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
1941 TRACE("(%p)->(%p)\n",This,hDC);
1943 if (!(This->Flags & SFLAG_DCINUSE))
1944 return WINEDDERR_NODC;
1946 if (This->hDC !=hDC) {
1947 WARN("Application tries to release an invalid DC(%p), surface dc is %p\n", hDC, This->hDC);
1948 return WINEDDERR_NODC;
1951 if((This->Flags & SFLAG_PBO) && This->resource.allocatedMemory) {
1952 /* Copy the contents of the DIB over to the PBO */
1953 memcpy(This->resource.allocatedMemory, This->dib.bitmap_data, This->dib.bitmap_size);
1956 /* we locked first, so unlock now */
1957 IWineD3DSurface_UnlockRect(iface);
1959 This->Flags &= ~SFLAG_DCINUSE;
1961 return WINED3D_OK;
1964 /* ******************************************************
1965 IWineD3DSurface Internal (No mapping to directx api) parts follow
1966 ****************************************************** */
1968 HRESULT d3dfmt_get_conv(IWineD3DSurfaceImpl *This, BOOL need_alpha_ck, BOOL use_texturing, GLenum *format, GLenum *internal, GLenum *type, CONVERT_TYPES *convert, int *target_bpp, BOOL srgb_mode) {
1969 BOOL colorkey_active = need_alpha_ck && (This->CKeyFlags & WINEDDSD_CKSRCBLT);
1970 const struct GlPixelFormatDesc *glDesc = This->resource.format_desc;
1971 IWineD3DDeviceImpl *device = This->resource.device;
1972 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
1974 /* Default values: From the surface */
1975 *format = glDesc->glFormat;
1976 *type = glDesc->glType;
1977 *convert = NO_CONVERSION;
1978 *target_bpp = glDesc->byte_count;
1980 if(srgb_mode) {
1981 *internal = glDesc->glGammaInternal;
1983 else if (This->resource.usage & WINED3DUSAGE_RENDERTARGET
1984 && surface_is_offscreen((IWineD3DSurface *) This))
1986 *internal = glDesc->rtInternal;
1987 } else {
1988 *internal = glDesc->glInternal;
1991 /* Ok, now look if we have to do any conversion */
1992 switch(This->resource.format_desc->format)
1994 case WINED3DFMT_P8_UINT:
1995 /* ****************
1996 Paletted Texture
1997 **************** */
1999 /* Use conversion when the paletted texture extension OR fragment shaders are available. When either
2000 * of the two is available make sure texturing is requested as neither of the two works in
2001 * conjunction with calls like glDraw-/glReadPixels. Further also use conversion in case of color keying.
2002 * Paletted textures can be emulated using shaders but only do that for 2D purposes e.g. situations
2003 * in which the main render target uses p8. Some games like GTA Vice City use P8 for texturing which
2004 * conflicts with this.
2006 if (!(gl_info->supported[EXT_PALETTED_TEXTURE] || (device->blitter->color_fixup_supported(This->resource.format_desc->color_fixup)
2007 && device->render_targets && This == (IWineD3DSurfaceImpl*)device->render_targets[0]))
2008 || colorkey_active || !use_texturing)
2010 *format = GL_RGBA;
2011 *internal = GL_RGBA;
2012 *type = GL_UNSIGNED_BYTE;
2013 *target_bpp = 4;
2014 if(colorkey_active) {
2015 *convert = CONVERT_PALETTED_CK;
2016 } else {
2017 *convert = CONVERT_PALETTED;
2020 else if (!gl_info->supported[EXT_PALETTED_TEXTURE] && device->blitter->color_fixup_supported(This->resource.format_desc->color_fixup))
2022 *format = GL_ALPHA;
2023 *type = GL_UNSIGNED_BYTE;
2024 *target_bpp = 1;
2027 break;
2029 case WINED3DFMT_B2G3R3_UNORM:
2030 /* **********************
2031 GL_UNSIGNED_BYTE_3_3_2
2032 ********************** */
2033 if (colorkey_active) {
2034 /* This texture format will never be used.. So do not care about color keying
2035 up until the point in time it will be needed :-) */
2036 FIXME(" ColorKeying not supported in the RGB 332 format !\n");
2038 break;
2040 case WINED3DFMT_B5G6R5_UNORM:
2041 if (colorkey_active) {
2042 *convert = CONVERT_CK_565;
2043 *format = GL_RGBA;
2044 *internal = GL_RGB5_A1;
2045 *type = GL_UNSIGNED_SHORT_5_5_5_1;
2047 break;
2049 case WINED3DFMT_B5G5R5X1_UNORM:
2050 if (colorkey_active) {
2051 *convert = CONVERT_CK_5551;
2052 *format = GL_BGRA;
2053 *internal = GL_RGB5_A1;
2054 *type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
2056 break;
2058 case WINED3DFMT_B8G8R8_UNORM:
2059 if (colorkey_active) {
2060 *convert = CONVERT_CK_RGB24;
2061 *format = GL_RGBA;
2062 *internal = GL_RGBA8;
2063 *type = GL_UNSIGNED_INT_8_8_8_8;
2064 *target_bpp = 4;
2066 break;
2068 case WINED3DFMT_B8G8R8X8_UNORM:
2069 if (colorkey_active) {
2070 *convert = CONVERT_RGB32_888;
2071 *format = GL_RGBA;
2072 *internal = GL_RGBA8;
2073 *type = GL_UNSIGNED_INT_8_8_8_8;
2075 break;
2077 case WINED3DFMT_R8G8_SNORM:
2078 if (gl_info->supported[NV_TEXTURE_SHADER]) break;
2079 *convert = CONVERT_V8U8;
2080 *format = GL_BGR;
2081 *type = GL_UNSIGNED_BYTE;
2082 *target_bpp = 3;
2083 break;
2085 case WINED3DFMT_R5G5_SNORM_L6_UNORM:
2086 *convert = CONVERT_L6V5U5;
2087 if (gl_info->supported[NV_TEXTURE_SHADER])
2089 *target_bpp = 3;
2090 /* Use format and types from table */
2091 } else {
2092 /* Load it into unsigned R5G6B5, swap L and V channels, and revert that in the shader */
2093 *target_bpp = 2;
2094 *format = GL_RGB;
2095 *type = GL_UNSIGNED_SHORT_5_6_5;
2097 break;
2099 case WINED3DFMT_R8G8_SNORM_L8X8_UNORM:
2100 *convert = CONVERT_X8L8V8U8;
2101 *target_bpp = 4;
2102 if (gl_info->supported[NV_TEXTURE_SHADER])
2104 /* Use formats from gl table. It is a bit unfortunate, but the conversion
2105 * is needed to set the X format to 255 to get 1.0 for alpha when sampling
2106 * the texture. OpenGL can't use GL_DSDT8_MAG8_NV as internal format with
2107 * the needed type and format parameter, so the internal format contains a
2108 * 4th component, which is returned as alpha
2110 } else {
2111 *format = GL_BGRA;
2112 *type = GL_UNSIGNED_INT_8_8_8_8_REV;
2114 break;
2116 case WINED3DFMT_R8G8B8A8_SNORM:
2117 if (gl_info->supported[NV_TEXTURE_SHADER]) break;
2118 *convert = CONVERT_Q8W8V8U8;
2119 *format = GL_BGRA;
2120 *type = GL_UNSIGNED_BYTE;
2121 *target_bpp = 4;
2122 break;
2124 case WINED3DFMT_R16G16_SNORM:
2125 if (gl_info->supported[NV_TEXTURE_SHADER]) break;
2126 *convert = CONVERT_V16U16;
2127 *format = GL_BGR;
2128 *type = GL_UNSIGNED_SHORT;
2129 *target_bpp = 6;
2130 break;
2132 case WINED3DFMT_L4A4_UNORM:
2133 /* WINED3DFMT_L4A4_UNORM exists as an internal gl format, but for some reason there is not
2134 * format+type combination to load it. Thus convert it to A8L8, then load it
2135 * with A4L4 internal, but A8L8 format+type
2137 *convert = CONVERT_A4L4;
2138 *format = GL_LUMINANCE_ALPHA;
2139 *type = GL_UNSIGNED_BYTE;
2140 *target_bpp = 2;
2141 break;
2143 case WINED3DFMT_R16G16_UNORM:
2144 *convert = CONVERT_G16R16;
2145 *format = GL_RGB;
2146 *type = GL_UNSIGNED_SHORT;
2147 *target_bpp = 6;
2148 break;
2150 case WINED3DFMT_R16G16_FLOAT:
2151 *convert = CONVERT_R16G16F;
2152 *format = GL_RGB;
2153 *type = GL_HALF_FLOAT_ARB;
2154 *target_bpp = 6;
2155 break;
2157 case WINED3DFMT_R32G32_FLOAT:
2158 *convert = CONVERT_R32G32F;
2159 *format = GL_RGB;
2160 *type = GL_FLOAT;
2161 *target_bpp = 12;
2162 break;
2164 case WINED3DFMT_S1_UINT_D15_UNORM:
2165 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2166 || gl_info->supported[EXT_PACKED_DEPTH_STENCIL])
2168 *convert = CONVERT_D15S1;
2169 *target_bpp = 4;
2171 break;
2173 case WINED3DFMT_S4X4_UINT_D24_UNORM:
2174 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
2175 || gl_info->supported[EXT_PACKED_DEPTH_STENCIL])
2177 *convert = CONVERT_D24X4S4;
2179 break;
2181 case WINED3DFMT_S8_UINT_D24_FLOAT:
2182 if (gl_info->supported[ARB_DEPTH_BUFFER_FLOAT])
2184 *convert = CONVERT_D24FS8;
2185 *target_bpp = 8;
2187 break;
2189 default:
2190 break;
2193 return WINED3D_OK;
2196 static void d3dfmt_p8_init_palette(IWineD3DSurfaceImpl *This, BYTE table[256][4], BOOL colorkey)
2198 IWineD3DDeviceImpl *device = This->resource.device;
2199 IWineD3DPaletteImpl *pal = This->palette;
2200 BOOL index_in_alpha = FALSE;
2201 unsigned int i;
2203 /* Old games like StarCraft, C&C, Red Alert and others use P8 render targets.
2204 * Reading back the RGB output each lockrect (each frame as they lock the whole screen)
2205 * is slow. Further RGB->P8 conversion is not possible because palettes can have
2206 * duplicate entries. Store the color key in the unused alpha component to speed the
2207 * download up and to make conversion unneeded. */
2208 index_in_alpha = primary_render_target_is_p8(device);
2210 if (!pal)
2212 UINT dxVersion = ((IWineD3DImpl *)device->wined3d)->dxVersion;
2214 /* In DirectDraw the palette is a property of the surface, there are no such things as device palettes. */
2215 if (dxVersion <= 7)
2217 ERR("This code should never get entered for DirectDraw!, expect problems\n");
2218 if (index_in_alpha)
2220 /* Guarantees that memory representation remains correct after sysmem<->texture transfers even if
2221 * there's no palette at this time. */
2222 for (i = 0; i < 256; i++) table[i][3] = i;
2225 else
2227 /* Direct3D >= 8 palette usage style: P8 textures use device palettes, palette entry format is A8R8G8B8,
2228 * alpha is stored in peFlags and may be used by the app if D3DPTEXTURECAPS_ALPHAPALETTE device
2229 * capability flag is present (wine does advertise this capability) */
2230 for (i = 0; i < 256; ++i)
2232 table[i][0] = device->palettes[device->currentPalette][i].peRed;
2233 table[i][1] = device->palettes[device->currentPalette][i].peGreen;
2234 table[i][2] = device->palettes[device->currentPalette][i].peBlue;
2235 table[i][3] = device->palettes[device->currentPalette][i].peFlags;
2239 else
2241 TRACE("Using surface palette %p\n", pal);
2242 /* Get the surface's palette */
2243 for (i = 0; i < 256; ++i)
2245 table[i][0] = pal->palents[i].peRed;
2246 table[i][1] = pal->palents[i].peGreen;
2247 table[i][2] = pal->palents[i].peBlue;
2249 /* When index_in_alpha is set the palette index is stored in the
2250 * alpha component. In case of a readback we can then read
2251 * GL_ALPHA. Color keying is handled in BltOverride using a
2252 * GL_ALPHA_TEST using GL_NOT_EQUAL. In case of index_in_alpha the
2253 * color key itself is passed to glAlphaFunc in other cases the
2254 * alpha component of pixels that should be masked away is set to 0. */
2255 if (index_in_alpha)
2257 table[i][3] = i;
2259 else if (colorkey && (i >= This->SrcBltCKey.dwColorSpaceLowValue)
2260 && (i <= This->SrcBltCKey.dwColorSpaceHighValue))
2262 table[i][3] = 0x00;
2264 else if(pal->Flags & WINEDDPCAPS_ALPHA)
2266 table[i][3] = pal->palents[i].peFlags;
2268 else
2270 table[i][3] = 0xFF;
2276 static HRESULT d3dfmt_convert_surface(const BYTE *src, BYTE *dst, UINT pitch, UINT width,
2277 UINT height, UINT outpitch, CONVERT_TYPES convert, IWineD3DSurfaceImpl *This)
2279 IWineD3DDeviceImpl *device = This->resource.device;
2280 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
2281 const BYTE *source;
2282 BYTE *dest;
2283 TRACE("(%p)->(%p),(%d,%d,%d,%d,%p)\n", src, dst, pitch, height, outpitch, convert,This);
2285 switch (convert) {
2286 case NO_CONVERSION:
2288 memcpy(dst, src, pitch * height);
2289 break;
2291 case CONVERT_PALETTED:
2292 case CONVERT_PALETTED_CK:
2294 IWineD3DPaletteImpl* pal = This->palette;
2295 BYTE table[256][4];
2296 unsigned int x, y;
2298 if( pal == NULL) {
2299 /* TODO: If we are a sublevel, try to get the palette from level 0 */
2302 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2304 for (y = 0; y < height; y++)
2306 source = src + pitch * y;
2307 dest = dst + outpitch * y;
2308 /* This is an 1 bpp format, using the width here is fine */
2309 for (x = 0; x < width; x++) {
2310 BYTE color = *source++;
2311 *dest++ = table[color][0];
2312 *dest++ = table[color][1];
2313 *dest++ = table[color][2];
2314 *dest++ = table[color][3];
2318 break;
2320 case CONVERT_CK_565:
2322 /* Converting the 565 format in 5551 packed to emulate color-keying.
2324 Note : in all these conversion, it would be best to average the averaging
2325 pixels to get the color of the pixel that will be color-keyed to
2326 prevent 'color bleeding'. This will be done later on if ever it is
2327 too visible.
2329 Note2: Nvidia documents say that their driver does not support alpha + color keying
2330 on the same surface and disables color keying in such a case
2332 unsigned int x, y;
2333 const WORD *Source;
2334 WORD *Dest;
2336 TRACE("Color keyed 565\n");
2338 for (y = 0; y < height; y++) {
2339 Source = (const WORD *)(src + y * pitch);
2340 Dest = (WORD *) (dst + y * outpitch);
2341 for (x = 0; x < width; x++ ) {
2342 WORD color = *Source++;
2343 *Dest = ((color & 0xFFC0) | ((color & 0x1F) << 1));
2344 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2345 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2346 *Dest |= 0x0001;
2348 Dest++;
2352 break;
2354 case CONVERT_CK_5551:
2356 /* Converting X1R5G5B5 format to R5G5B5A1 to emulate color-keying. */
2357 unsigned int x, y;
2358 const WORD *Source;
2359 WORD *Dest;
2360 TRACE("Color keyed 5551\n");
2361 for (y = 0; y < height; y++) {
2362 Source = (const WORD *)(src + y * pitch);
2363 Dest = (WORD *) (dst + y * outpitch);
2364 for (x = 0; x < width; x++ ) {
2365 WORD color = *Source++;
2366 *Dest = color;
2367 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2368 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2369 *Dest |= (1 << 15);
2371 else {
2372 *Dest &= ~(1 << 15);
2374 Dest++;
2378 break;
2380 case CONVERT_CK_RGB24:
2382 /* Converting R8G8B8 format to R8G8B8A8 with color-keying. */
2383 unsigned int x, y;
2384 for (y = 0; y < height; y++)
2386 source = src + pitch * y;
2387 dest = dst + outpitch * y;
2388 for (x = 0; x < width; x++) {
2389 DWORD color = ((DWORD)source[0] << 16) + ((DWORD)source[1] << 8) + (DWORD)source[2] ;
2390 DWORD dstcolor = color << 8;
2391 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2392 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2393 dstcolor |= 0xff;
2395 *(DWORD*)dest = dstcolor;
2396 source += 3;
2397 dest += 4;
2401 break;
2403 case CONVERT_RGB32_888:
2405 /* Converting X8R8G8B8 format to R8G8B8A8 with color-keying. */
2406 unsigned int x, y;
2407 for (y = 0; y < height; y++)
2409 source = src + pitch * y;
2410 dest = dst + outpitch * y;
2411 for (x = 0; x < width; x++) {
2412 DWORD color = 0xffffff & *(const DWORD*)source;
2413 DWORD dstcolor = color << 8;
2414 if ((color < This->SrcBltCKey.dwColorSpaceLowValue) ||
2415 (color > This->SrcBltCKey.dwColorSpaceHighValue)) {
2416 dstcolor |= 0xff;
2418 *(DWORD*)dest = dstcolor;
2419 source += 4;
2420 dest += 4;
2424 break;
2426 case CONVERT_V8U8:
2428 unsigned int x, y;
2429 const short *Source;
2430 unsigned char *Dest;
2431 for(y = 0; y < height; y++) {
2432 Source = (const short *)(src + y * pitch);
2433 Dest = dst + y * outpitch;
2434 for (x = 0; x < width; x++ ) {
2435 long color = (*Source++);
2436 /* B */ Dest[0] = 0xff;
2437 /* G */ Dest[1] = (color >> 8) + 128; /* V */
2438 /* R */ Dest[2] = (color) + 128; /* U */
2439 Dest += 3;
2442 break;
2445 case CONVERT_V16U16:
2447 unsigned int x, y;
2448 const DWORD *Source;
2449 unsigned short *Dest;
2450 for(y = 0; y < height; y++) {
2451 Source = (const DWORD *)(src + y * pitch);
2452 Dest = (unsigned short *) (dst + y * outpitch);
2453 for (x = 0; x < width; x++ ) {
2454 DWORD color = (*Source++);
2455 /* B */ Dest[0] = 0xffff;
2456 /* G */ Dest[1] = (color >> 16) + 32768; /* V */
2457 /* R */ Dest[2] = (color ) + 32768; /* U */
2458 Dest += 3;
2461 break;
2464 case CONVERT_Q8W8V8U8:
2466 unsigned int x, y;
2467 const DWORD *Source;
2468 unsigned char *Dest;
2469 for(y = 0; y < height; y++) {
2470 Source = (const DWORD *)(src + y * pitch);
2471 Dest = dst + y * outpitch;
2472 for (x = 0; x < width; x++ ) {
2473 long color = (*Source++);
2474 /* B */ Dest[0] = ((color >> 16) & 0xff) + 128; /* W */
2475 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2476 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2477 /* A */ Dest[3] = ((color >> 24) & 0xff) + 128; /* Q */
2478 Dest += 4;
2481 break;
2484 case CONVERT_L6V5U5:
2486 unsigned int x, y;
2487 const WORD *Source;
2488 unsigned char *Dest;
2490 if (gl_info->supported[NV_TEXTURE_SHADER])
2492 /* This makes the gl surface bigger(24 bit instead of 16), but it works with
2493 * fixed function and shaders without further conversion once the surface is
2494 * loaded
2496 for(y = 0; y < height; y++) {
2497 Source = (const WORD *)(src + y * pitch);
2498 Dest = dst + y * outpitch;
2499 for (x = 0; x < width; x++ ) {
2500 short color = (*Source++);
2501 unsigned char l = ((color >> 10) & 0xfc);
2502 char v = ((color >> 5) & 0x3e);
2503 char u = ((color ) & 0x1f);
2505 /* 8 bits destination, 6 bits source, 8th bit is the sign. gl ignores the sign
2506 * and doubles the positive range. Thus shift left only once, gl does the 2nd
2507 * shift. GL reads a signed value and converts it into an unsigned value.
2509 /* M */ Dest[2] = l << 1;
2511 /* Those are read as signed, but kept signed. Just left-shift 3 times to scale
2512 * from 5 bit values to 8 bit values.
2514 /* V */ Dest[1] = v << 3;
2515 /* U */ Dest[0] = u << 3;
2516 Dest += 3;
2519 } else {
2520 for(y = 0; y < height; y++) {
2521 unsigned short *Dest_s = (unsigned short *) (dst + y * outpitch);
2522 Source = (const WORD *)(src + y * pitch);
2523 for (x = 0; x < width; x++ ) {
2524 short color = (*Source++);
2525 unsigned char l = ((color >> 10) & 0xfc);
2526 short v = ((color >> 5) & 0x3e);
2527 short u = ((color ) & 0x1f);
2528 short v_conv = v + 16;
2529 short u_conv = u + 16;
2531 *Dest_s = ((v_conv << 11) & 0xf800) | ((l << 5) & 0x7e0) | (u_conv & 0x1f);
2532 Dest_s += 1;
2536 break;
2539 case CONVERT_X8L8V8U8:
2541 unsigned int x, y;
2542 const DWORD *Source;
2543 unsigned char *Dest;
2545 if (gl_info->supported[NV_TEXTURE_SHADER])
2547 /* This implementation works with the fixed function pipeline and shaders
2548 * without further modification after converting the surface.
2550 for(y = 0; y < height; y++) {
2551 Source = (const DWORD *)(src + y * pitch);
2552 Dest = dst + y * outpitch;
2553 for (x = 0; x < width; x++ ) {
2554 long color = (*Source++);
2555 /* L */ Dest[2] = ((color >> 16) & 0xff); /* L */
2556 /* V */ Dest[1] = ((color >> 8 ) & 0xff); /* V */
2557 /* U */ Dest[0] = (color & 0xff); /* U */
2558 /* I */ Dest[3] = 255; /* X */
2559 Dest += 4;
2562 } else {
2563 /* Doesn't work correctly with the fixed function pipeline, but can work in
2564 * shaders if the shader is adjusted. (There's no use for this format in gl's
2565 * standard fixed function pipeline anyway).
2567 for(y = 0; y < height; y++) {
2568 Source = (const DWORD *)(src + y * pitch);
2569 Dest = dst + y * outpitch;
2570 for (x = 0; x < width; x++ ) {
2571 long color = (*Source++);
2572 /* B */ Dest[0] = ((color >> 16) & 0xff); /* L */
2573 /* G */ Dest[1] = ((color >> 8 ) & 0xff) + 128; /* V */
2574 /* R */ Dest[2] = (color & 0xff) + 128; /* U */
2575 Dest += 4;
2579 break;
2582 case CONVERT_A4L4:
2584 unsigned int x, y;
2585 const unsigned char *Source;
2586 unsigned char *Dest;
2587 for(y = 0; y < height; y++) {
2588 Source = src + y * pitch;
2589 Dest = dst + y * outpitch;
2590 for (x = 0; x < width; x++ ) {
2591 unsigned char color = (*Source++);
2592 /* A */ Dest[1] = (color & 0xf0) << 0;
2593 /* L */ Dest[0] = (color & 0x0f) << 4;
2594 Dest += 2;
2597 break;
2600 case CONVERT_G16R16:
2601 case CONVERT_R16G16F:
2603 unsigned int x, y;
2604 const WORD *Source;
2605 WORD *Dest;
2607 for(y = 0; y < height; y++) {
2608 Source = (const WORD *)(src + y * pitch);
2609 Dest = (WORD *) (dst + y * outpitch);
2610 for (x = 0; x < width; x++ ) {
2611 WORD green = (*Source++);
2612 WORD red = (*Source++);
2613 Dest[0] = green;
2614 Dest[1] = red;
2615 /* Strictly speaking not correct for R16G16F, but it doesn't matter because the
2616 * shader overwrites it anyway
2618 Dest[2] = 0xffff;
2619 Dest += 3;
2622 break;
2625 case CONVERT_R32G32F:
2627 unsigned int x, y;
2628 const float *Source;
2629 float *Dest;
2630 for(y = 0; y < height; y++) {
2631 Source = (const float *)(src + y * pitch);
2632 Dest = (float *) (dst + y * outpitch);
2633 for (x = 0; x < width; x++ ) {
2634 float green = (*Source++);
2635 float red = (*Source++);
2636 Dest[0] = green;
2637 Dest[1] = red;
2638 Dest[2] = 1.0f;
2639 Dest += 3;
2642 break;
2645 case CONVERT_D15S1:
2647 unsigned int x, y;
2649 for (y = 0; y < height; ++y)
2651 const WORD *source = (const WORD *)(src + y * pitch);
2652 DWORD *dest = (DWORD *)(dst + y * outpitch);
2654 for (x = 0; x < width; ++x)
2656 /* The depth data is normalized, so needs to be scaled,
2657 * the stencil data isn't. Scale depth data by
2658 * (2^24-1)/(2^15-1) ~~ (2^9 + 2^-6). */
2659 WORD d15 = source[x] >> 1;
2660 DWORD d24 = (d15 << 9) + (d15 >> 6);
2661 dest[x] = (d24 << 8) | (source[x] & 0x1);
2664 break;
2667 case CONVERT_D24X4S4:
2669 unsigned int x, y;
2671 for (y = 0; y < height; ++y)
2673 const DWORD *source = (const DWORD *)(src + y * pitch);
2674 DWORD *dest = (DWORD *)(dst + y * outpitch);
2676 for (x = 0; x < width; ++x)
2678 /* Just need to clear out the X4 part. */
2679 dest[x] = source[x] & ~0xf0;
2682 break;
2685 case CONVERT_D24FS8:
2687 unsigned int x, y;
2689 for (y = 0; y < height; ++y)
2691 const DWORD *source = (const DWORD *)(src + y * pitch);
2692 float *dest_f = (float *)(dst + y * outpitch);
2693 DWORD *dest_s = (DWORD *)(dst + y * outpitch);
2695 for (x = 0; x < width; ++x)
2697 dest_f[x * 2] = float_24_to_32((source[x] & 0xffffff00) >> 8);
2698 dest_s[x * 2 + 1] = source[x] & 0xff;
2701 break;
2704 default:
2705 ERR("Unsupported conversion type %#x.\n", convert);
2707 return WINED3D_OK;
2710 /* This function is used in case of 8bit paletted textures to upload the palette.
2711 It supports GL_EXT_paletted_texture and GL_ARB_fragment_program, support for other
2712 extensions like ATI_fragment_shaders is possible.
2714 /* Context activation is done by the caller. */
2715 static void d3dfmt_p8_upload_palette(IWineD3DSurface *iface, CONVERT_TYPES convert) {
2716 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2717 BYTE table[256][4];
2718 IWineD3DDeviceImpl *device = This->resource.device;
2719 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
2721 d3dfmt_p8_init_palette(This, table, (convert == CONVERT_PALETTED_CK));
2723 /* Try to use the paletted texture extension */
2724 if (gl_info->supported[EXT_PALETTED_TEXTURE])
2726 TRACE("Using GL_EXT_PALETTED_TEXTURE for 8-bit paletted texture support\n");
2727 ENTER_GL();
2728 GL_EXTCALL(glColorTableEXT(This->texture_target, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, table));
2729 LEAVE_GL();
2731 else
2733 /* Let a fragment shader do the color conversion by uploading the palette to a 1D texture.
2734 * The 8bit pixel data will be used as an index in this palette texture to retrieve the final color. */
2735 TRACE("Using fragment shaders for emulating 8-bit paletted texture support\n");
2737 device->blitter->set_shader((IWineD3DDevice *) device, This->resource.format_desc,
2738 This->texture_target, This->pow2Width, This->pow2Height);
2740 ENTER_GL();
2741 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE1));
2742 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2744 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2745 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); /* Make sure we have discrete color levels. */
2746 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2747 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, table); /* Upload the palette */
2749 /* Switch back to unit 0 in which the 2D texture will be stored. */
2750 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0));
2752 /* Rebind the texture because it isn't bound anymore */
2753 glBindTexture(This->texture_target, This->texture_name);
2754 LEAVE_GL();
2758 BOOL palette9_changed(IWineD3DSurfaceImpl *This)
2760 IWineD3DDeviceImpl *device = This->resource.device;
2762 if (This->palette || (This->resource.format_desc->format != WINED3DFMT_P8_UINT
2763 && This->resource.format_desc->format != WINED3DFMT_P8_UINT_A8_UNORM))
2765 /* If a ddraw-style palette is attached assume no d3d9 palette change.
2766 * Also the palette isn't interesting if the surface format isn't P8 or A8P8
2768 return FALSE;
2771 if (This->palette9)
2773 if (!memcmp(This->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256))
2775 return FALSE;
2777 } else {
2778 This->palette9 = HeapAlloc(GetProcessHeap(), 0, sizeof(PALETTEENTRY) * 256);
2780 memcpy(This->palette9, device->palettes[device->currentPalette], sizeof(PALETTEENTRY) * 256);
2781 return TRUE;
2784 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadTexture(IWineD3DSurface *iface, BOOL srgb_mode) {
2785 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2786 DWORD flag = srgb_mode ? SFLAG_INSRGBTEX : SFLAG_INTEXTURE;
2788 if (!(This->Flags & flag)) {
2789 TRACE("Reloading because surface is dirty\n");
2790 } else if(/* Reload: gl texture has ck, now no ckey is set OR */
2791 ((This->Flags & SFLAG_GLCKEY) && (!(This->CKeyFlags & WINEDDSD_CKSRCBLT))) ||
2792 /* Reload: vice versa OR */
2793 ((!(This->Flags & SFLAG_GLCKEY)) && (This->CKeyFlags & WINEDDSD_CKSRCBLT)) ||
2794 /* Also reload: Color key is active AND the color key has changed */
2795 ((This->CKeyFlags & WINEDDSD_CKSRCBLT) && (
2796 (This->glCKey.dwColorSpaceLowValue != This->SrcBltCKey.dwColorSpaceLowValue) ||
2797 (This->glCKey.dwColorSpaceHighValue != This->SrcBltCKey.dwColorSpaceHighValue)))) {
2798 TRACE("Reloading because of color keying\n");
2799 /* To perform the color key conversion we need a sysmem copy of
2800 * the surface. Make sure we have it
2803 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
2804 /* Make sure the texture is reloaded because of the color key change, this kills performance though :( */
2805 /* TODO: This is not necessarily needed with hw palettized texture support */
2806 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
2807 } else {
2808 TRACE("surface is already in texture\n");
2809 return WINED3D_OK;
2812 /* Resources are placed in system RAM and do not need to be recreated when a device is lost.
2813 * These resources are not bound by device size or format restrictions. Because of this,
2814 * these resources cannot be accessed by the Direct3D device nor set as textures or render targets.
2815 * However, these resources can always be created, locked, and copied.
2817 if (This->resource.pool == WINED3DPOOL_SCRATCH )
2819 FIXME("(%p) Operation not supported for scratch textures\n",This);
2820 return WINED3DERR_INVALIDCALL;
2823 IWineD3DSurface_LoadLocation(iface, flag, NULL /* no partial locking for textures yet */);
2825 #if 0
2827 static unsigned int gen = 0;
2828 char buffer[4096];
2829 ++gen;
2830 if ((gen % 10) == 0) {
2831 snprintf(buffer, sizeof(buffer), "/tmp/surface%p_type%u_level%u_%u.ppm",
2832 This, This->texture_target, This->texture_level, gen);
2833 IWineD3DSurfaceImpl_SaveSnapshot(iface, buffer);
2836 * debugging crash code
2837 if (gen == 250) {
2838 void** test = NULL;
2839 *test = 0;
2843 #endif
2845 if (!(This->Flags & SFLAG_DONOTFREE)) {
2846 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
2847 This->resource.allocatedMemory = NULL;
2848 This->resource.heapMemory = NULL;
2849 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, FALSE);
2852 return WINED3D_OK;
2855 /* Context activation is done by the caller. */
2856 static void WINAPI IWineD3DSurfaceImpl_BindTexture(IWineD3DSurface *iface, BOOL srgb) {
2857 /* TODO: check for locks */
2858 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2859 IWineD3DBaseTexture *baseTexture = NULL;
2861 TRACE("(%p)Checking to see if the container is a base texture\n", This);
2862 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&baseTexture) == WINED3D_OK) {
2863 TRACE("Passing to container\n");
2864 IWineD3DBaseTexture_BindTexture(baseTexture, srgb);
2865 IWineD3DBaseTexture_Release(baseTexture);
2867 else
2869 GLuint *name;
2871 TRACE("(%p) : Binding surface\n", This);
2873 name = srgb ? &This->texture_name_srgb : &This->texture_name;
2875 ENTER_GL();
2877 if (!This->texture_level)
2879 if (!*name) {
2880 glGenTextures(1, name);
2881 checkGLcall("glGenTextures");
2882 TRACE("Surface %p given name %d\n", This, *name);
2884 glBindTexture(This->texture_target, *name);
2885 checkGLcall("glBindTexture");
2886 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2887 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)");
2888 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2889 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)");
2890 glTexParameteri(This->texture_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2891 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)");
2892 glTexParameteri(This->texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2893 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MIN_FILTER, GL_NEAREST)");
2894 glTexParameteri(This->texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2895 checkGLcall("glTexParameteri(dimension, GL_TEXTURE_MAG_FILTER, GL_NEAREST)");
2897 /* This is where we should be reducing the amount of GLMemoryUsed */
2898 } else if (*name) {
2899 /* Mipmap surfaces should have a base texture container */
2900 ERR("Mipmap surface has a glTexture bound to it!\n");
2903 glBindTexture(This->texture_target, *name);
2904 checkGLcall("glBindTexture");
2906 LEAVE_GL();
2910 #include <errno.h>
2911 #include <stdio.h>
2912 static HRESULT WINAPI IWineD3DSurfaceImpl_SaveSnapshot(IWineD3DSurface *iface, const char* filename)
2914 FILE* f = NULL;
2915 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
2916 char *allocatedMemory;
2917 const char *textureRow;
2918 IWineD3DSwapChain *swapChain = NULL;
2919 int width, height, i, y;
2920 GLuint tmpTexture = 0;
2921 DWORD color;
2922 /*FIXME:
2923 Textures may not be stored in ->allocatedgMemory and a GlTexture
2924 so we should lock the surface before saving a snapshot, or at least check that
2926 /* TODO: Compressed texture images can be obtained from the GL in uncompressed form
2927 by calling GetTexImage and in compressed form by calling
2928 GetCompressedTexImageARB. Queried compressed images can be saved and
2929 later reused by calling CompressedTexImage[123]DARB. Pre-compressed
2930 texture images do not need to be processed by the GL and should
2931 significantly improve texture loading performance relative to uncompressed
2932 images. */
2934 /* Setup the width and height to be the internal texture width and height. */
2935 width = This->pow2Width;
2936 height = This->pow2Height;
2937 /* check to see if we're a 'virtual' texture, e.g. we're not a pbuffer of texture, we're a back buffer*/
2938 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **)&swapChain);
2940 if (This->Flags & SFLAG_INDRAWABLE && !(This->Flags & SFLAG_INTEXTURE)) {
2941 /* if were not a real texture then read the back buffer into a real texture */
2942 /* we don't want to interfere with the back buffer so read the data into a temporary
2943 * texture and then save the data out of the temporary texture
2945 GLint prevRead;
2946 ENTER_GL();
2947 TRACE("(%p) Reading render target into texture\n", This);
2949 glGenTextures(1, &tmpTexture);
2950 glBindTexture(GL_TEXTURE_2D, tmpTexture);
2952 glTexImage2D(GL_TEXTURE_2D,
2954 GL_RGBA,
2955 width,
2956 height,
2957 0/*border*/,
2958 GL_RGBA,
2959 GL_UNSIGNED_INT_8_8_8_8_REV,
2960 NULL);
2962 glGetIntegerv(GL_READ_BUFFER, &prevRead);
2963 checkGLcall("glGetIntegerv");
2964 glReadBuffer(swapChain ? GL_BACK : This->resource.device->offscreenBuffer);
2965 checkGLcall("glReadBuffer");
2966 glCopyTexImage2D(GL_TEXTURE_2D,
2968 GL_RGBA,
2971 width,
2972 height,
2975 checkGLcall("glCopyTexImage2D");
2976 glReadBuffer(prevRead);
2977 LEAVE_GL();
2979 } else { /* bind the real texture, and make sure it up to date */
2980 surface_internal_preload(iface, SRGB_RGB);
2981 surface_bind_and_dirtify(This, FALSE);
2983 allocatedMemory = HeapAlloc(GetProcessHeap(), 0, width * height * 4);
2984 ENTER_GL();
2985 FIXME("Saving texture level %d width %d height %d\n", This->texture_level, width, height);
2986 glGetTexImage(GL_TEXTURE_2D, This->texture_level, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, allocatedMemory);
2987 checkGLcall("glGetTexImage");
2988 if (tmpTexture) {
2989 glBindTexture(GL_TEXTURE_2D, 0);
2990 glDeleteTextures(1, &tmpTexture);
2992 LEAVE_GL();
2994 f = fopen(filename, "w+");
2995 if (NULL == f) {
2996 ERR("opening of %s failed with: %s\n", filename, strerror(errno));
2997 return WINED3DERR_INVALIDCALL;
2999 /* Save the data out to a TGA file because 1: it's an easy raw format, 2: it supports an alpha channel */
3000 TRACE("(%p) opened %s with format %s\n", This, filename, debug_d3dformat(This->resource.format_desc->format));
3001 /* TGA header */
3002 fputc(0,f);
3003 fputc(0,f);
3004 fputc(2,f);
3005 fputc(0,f);
3006 fputc(0,f);
3007 fputc(0,f);
3008 fputc(0,f);
3009 fputc(0,f);
3010 fputc(0,f);
3011 fputc(0,f);
3012 fputc(0,f);
3013 fputc(0,f);
3014 /* short width*/
3015 fwrite(&width,2,1,f);
3016 /* short height */
3017 fwrite(&height,2,1,f);
3018 /* format rgba */
3019 fputc(0x20,f);
3020 fputc(0x28,f);
3021 /* raw data */
3022 /* 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 */
3023 if(swapChain)
3024 textureRow = allocatedMemory + (width * (height - 1) *4);
3025 else
3026 textureRow = allocatedMemory;
3027 for (y = 0 ; y < height; y++) {
3028 for (i = 0; i < width; i++) {
3029 color = *((const DWORD*)textureRow);
3030 fputc((color >> 16) & 0xFF, f); /* B */
3031 fputc((color >> 8) & 0xFF, f); /* G */
3032 fputc((color >> 0) & 0xFF, f); /* R */
3033 fputc((color >> 24) & 0xFF, f); /* A */
3034 textureRow += 4;
3036 /* take two rows of the pointer to the texture memory */
3037 if(swapChain)
3038 (textureRow-= width << 3);
3041 TRACE("Closing file\n");
3042 fclose(f);
3044 if(swapChain) {
3045 IWineD3DSwapChain_Release(swapChain);
3047 HeapFree(GetProcessHeap(), 0, allocatedMemory);
3048 return WINED3D_OK;
3051 static HRESULT WINAPI IWineD3DSurfaceImpl_SetFormat(IWineD3DSurface *iface, WINED3DFORMAT format) {
3052 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3053 HRESULT hr;
3055 TRACE("(%p) : Calling base function first\n", This);
3056 hr = IWineD3DBaseSurfaceImpl_SetFormat(iface, format);
3057 if(SUCCEEDED(hr)) {
3058 This->Flags &= ~(SFLAG_ALLOCATED | SFLAG_SRGBALLOCATED);
3059 TRACE("(%p) : glFormat %d, glFormatInternal %d, glType %d\n", This, This->resource.format_desc->glFormat,
3060 This->resource.format_desc->glInternal, This->resource.format_desc->glType);
3062 return hr;
3065 static HRESULT WINAPI IWineD3DSurfaceImpl_SetMem(IWineD3DSurface *iface, void *Mem) {
3066 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
3068 if(This->Flags & (SFLAG_LOCKED | SFLAG_DCINUSE)) {
3069 WARN("Surface is locked or the HDC is in use\n");
3070 return WINED3DERR_INVALIDCALL;
3073 if(Mem && Mem != This->resource.allocatedMemory) {
3074 void *release = NULL;
3076 /* Do I have to copy the old surface content? */
3077 if(This->Flags & SFLAG_DIBSECTION) {
3078 /* Release the DC. No need to hold the critical section for the update
3079 * Thread because this thread runs only on front buffers, but this method
3080 * fails for render targets in the check above.
3082 SelectObject(This->hDC, This->dib.holdbitmap);
3083 DeleteDC(This->hDC);
3084 /* Release the DIB section */
3085 DeleteObject(This->dib.DIBsection);
3086 This->dib.bitmap_data = NULL;
3087 This->resource.allocatedMemory = NULL;
3088 This->hDC = NULL;
3089 This->Flags &= ~SFLAG_DIBSECTION;
3090 } else if(!(This->Flags & SFLAG_USERPTR)) {
3091 release = This->resource.heapMemory;
3092 This->resource.heapMemory = NULL;
3094 This->resource.allocatedMemory = Mem;
3095 This->Flags |= SFLAG_USERPTR | SFLAG_INSYSMEM;
3097 /* Now the surface memory is most up do date. Invalidate drawable and texture */
3098 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
3100 /* For client textures opengl has to be notified */
3101 if(This->Flags & SFLAG_CLIENT) {
3102 surface_release_client_storage(iface);
3105 /* Now free the old memory if any */
3106 HeapFree(GetProcessHeap(), 0, release);
3107 } else if(This->Flags & SFLAG_USERPTR) {
3108 /* LockRect and GetDC will re-create the dib section and allocated memory */
3109 This->resource.allocatedMemory = NULL;
3110 /* HeapMemory should be NULL already */
3111 if(This->resource.heapMemory != NULL) ERR("User pointer surface has heap memory allocated\n");
3112 This->Flags &= ~SFLAG_USERPTR;
3114 if(This->Flags & SFLAG_CLIENT) {
3115 surface_release_client_storage(iface);
3118 return WINED3D_OK;
3121 void flip_surface(IWineD3DSurfaceImpl *front, IWineD3DSurfaceImpl *back) {
3123 /* Flip the surface contents */
3124 /* Flip the DC */
3126 HDC tmp;
3127 tmp = front->hDC;
3128 front->hDC = back->hDC;
3129 back->hDC = tmp;
3132 /* Flip the DIBsection */
3134 HBITMAP tmp;
3135 BOOL hasDib = front->Flags & SFLAG_DIBSECTION;
3136 tmp = front->dib.DIBsection;
3137 front->dib.DIBsection = back->dib.DIBsection;
3138 back->dib.DIBsection = tmp;
3140 if(back->Flags & SFLAG_DIBSECTION) front->Flags |= SFLAG_DIBSECTION;
3141 else front->Flags &= ~SFLAG_DIBSECTION;
3142 if(hasDib) back->Flags |= SFLAG_DIBSECTION;
3143 else back->Flags &= ~SFLAG_DIBSECTION;
3146 /* Flip the surface data */
3148 void* tmp;
3150 tmp = front->dib.bitmap_data;
3151 front->dib.bitmap_data = back->dib.bitmap_data;
3152 back->dib.bitmap_data = tmp;
3154 tmp = front->resource.allocatedMemory;
3155 front->resource.allocatedMemory = back->resource.allocatedMemory;
3156 back->resource.allocatedMemory = tmp;
3158 tmp = front->resource.heapMemory;
3159 front->resource.heapMemory = back->resource.heapMemory;
3160 back->resource.heapMemory = tmp;
3163 /* Flip the PBO */
3165 GLuint tmp_pbo = front->pbo;
3166 front->pbo = back->pbo;
3167 back->pbo = tmp_pbo;
3170 /* client_memory should not be different, but just in case */
3172 BOOL tmp;
3173 tmp = front->dib.client_memory;
3174 front->dib.client_memory = back->dib.client_memory;
3175 back->dib.client_memory = tmp;
3178 /* Flip the opengl texture */
3180 GLuint tmp;
3182 tmp = back->texture_name;
3183 back->texture_name = front->texture_name;
3184 front->texture_name = tmp;
3186 tmp = back->texture_name_srgb;
3187 back->texture_name_srgb = front->texture_name_srgb;
3188 front->texture_name_srgb = tmp;
3192 DWORD tmp_flags = back->Flags;
3193 back->Flags = front->Flags;
3194 front->Flags = tmp_flags;
3198 static HRESULT WINAPI IWineD3DSurfaceImpl_Flip(IWineD3DSurface *iface, IWineD3DSurface *override, DWORD Flags) {
3199 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
3200 IWineD3DSwapChainImpl *swapchain = NULL;
3201 HRESULT hr;
3202 TRACE("(%p)->(%p,%x)\n", This, override, Flags);
3204 /* Flipping is only supported on RenderTargets and overlays*/
3205 if( !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_OVERLAY)) ) {
3206 WARN("Tried to flip a non-render target, non-overlay surface\n");
3207 return WINEDDERR_NOTFLIPPABLE;
3210 if(This->resource.usage & WINED3DUSAGE_OVERLAY) {
3211 flip_surface(This, (IWineD3DSurfaceImpl *) override);
3213 /* Update the overlay if it is visible */
3214 if(This->overlay_dest) {
3215 return IWineD3DSurface_DrawOverlay((IWineD3DSurface *) This);
3216 } else {
3217 return WINED3D_OK;
3221 if(override) {
3222 /* DDraw sets this for the X11 surfaces, so don't confuse the user
3223 * FIXME("(%p) Target override is not supported by now\n", This);
3224 * Additionally, it isn't really possible to support triple-buffering
3225 * properly on opengl at all
3229 IWineD3DSurface_GetContainer(iface, &IID_IWineD3DSwapChain, (void **) &swapchain);
3230 if(!swapchain) {
3231 ERR("Flipped surface is not on a swapchain\n");
3232 return WINEDDERR_NOTFLIPPABLE;
3235 /* Just overwrite the swapchain presentation interval. This is ok because only ddraw apps can call Flip,
3236 * and only d3d8 and d3d9 apps specify the presentation interval
3238 if((Flags & (WINEDDFLIP_NOVSYNC | WINEDDFLIP_INTERVAL2 | WINEDDFLIP_INTERVAL3 | WINEDDFLIP_INTERVAL4)) == 0) {
3239 /* Most common case first to avoid wasting time on all the other cases */
3240 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_ONE;
3241 } else if(Flags & WINEDDFLIP_NOVSYNC) {
3242 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3243 } else if(Flags & WINEDDFLIP_INTERVAL2) {
3244 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_TWO;
3245 } else if(Flags & WINEDDFLIP_INTERVAL3) {
3246 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_THREE;
3247 } else {
3248 swapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_FOUR;
3251 /* Flipping a OpenGL surface -> Use WineD3DDevice::Present */
3252 hr = IWineD3DSwapChain_Present((IWineD3DSwapChain *) swapchain, NULL, NULL, 0, NULL, 0);
3253 IWineD3DSwapChain_Release((IWineD3DSwapChain *) swapchain);
3254 return hr;
3257 /* Does a direct frame buffer -> texture copy. Stretching is done
3258 * with single pixel copy calls
3260 static inline void fb_copy_to_texture_direct(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3261 const WINED3DRECT *srect, const WINED3DRECT *drect, BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
3263 IWineD3DDeviceImpl *myDevice = This->resource.device;
3264 float xrel, yrel;
3265 UINT row;
3266 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3267 struct wined3d_context *context;
3270 context = context_acquire(myDevice, SrcSurface, CTXUSAGE_BLIT);
3271 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3272 ENTER_GL();
3274 /* Bind the target texture */
3275 glBindTexture(This->texture_target, This->texture_name);
3276 checkGLcall("glBindTexture");
3277 if(surface_is_offscreen(SrcSurface)) {
3278 TRACE("Reading from an offscreen target\n");
3279 upsidedown = !upsidedown;
3280 glReadBuffer(myDevice->offscreenBuffer);
3282 else
3284 glReadBuffer(surface_get_gl_buffer(SrcSurface));
3286 checkGLcall("glReadBuffer");
3288 xrel = (float) (srect->x2 - srect->x1) / (float) (drect->x2 - drect->x1);
3289 yrel = (float) (srect->y2 - srect->y1) / (float) (drect->y2 - drect->y1);
3291 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3293 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3295 if(Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT) {
3296 ERR("Texture filtering not supported in direct blit\n");
3299 else if ((Filter != WINED3DTEXF_NONE && Filter != WINED3DTEXF_POINT)
3300 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3302 ERR("Texture filtering not supported in direct blit\n");
3305 if (upsidedown
3306 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3307 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3309 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do */
3311 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3312 drect->x1 /*xoffset */, drect->y1 /* y offset */,
3313 srect->x1, Src->currentDesc.Height - srect->y2,
3314 drect->x2 - drect->x1, drect->y2 - drect->y1);
3315 } else {
3316 UINT yoffset = Src->currentDesc.Height - srect->y1 + drect->y1 - 1;
3317 /* I have to process this row by row to swap the image,
3318 * otherwise it would be upside down, so stretching in y direction
3319 * doesn't cost extra time
3321 * However, stretching in x direction can be avoided if not necessary
3323 for(row = drect->y1; row < drect->y2; row++) {
3324 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3326 /* Well, that stuff works, but it's very slow.
3327 * find a better way instead
3329 UINT col;
3331 for(col = drect->x1; col < drect->x2; col++) {
3332 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3333 drect->x1 + col /* x offset */, row /* y offset */,
3334 srect->x1 + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3336 } else {
3337 glCopyTexSubImage2D(This->texture_target, This->texture_level,
3338 drect->x1 /* x offset */, row /* y offset */,
3339 srect->x1, yoffset - (int) (row * yrel), drect->x2-drect->x1, 1);
3343 checkGLcall("glCopyTexSubImage2D");
3345 LEAVE_GL();
3346 context_release(context);
3348 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3349 * path is never entered
3351 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3354 /* Uses the hardware to stretch and flip the image */
3355 static inline void fb_copy_to_texture_hwstretch(IWineD3DSurfaceImpl *This, IWineD3DSurface *SrcSurface,
3356 IWineD3DSwapChainImpl *swapchain, const WINED3DRECT *srect, const WINED3DRECT *drect,
3357 BOOL upsidedown, WINED3DTEXTUREFILTERTYPE Filter)
3359 IWineD3DDeviceImpl *myDevice = This->resource.device;
3360 GLuint src, backup = 0;
3361 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3362 float left, right, top, bottom; /* Texture coordinates */
3363 UINT fbwidth = Src->currentDesc.Width;
3364 UINT fbheight = Src->currentDesc.Height;
3365 struct wined3d_context *context;
3366 GLenum drawBuffer = GL_BACK;
3367 GLenum texture_target;
3368 BOOL noBackBufferBackup;
3369 BOOL src_offscreen;
3371 TRACE("Using hwstretch blit\n");
3372 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3373 context = context_acquire(myDevice, SrcSurface, CTXUSAGE_BLIT);
3374 surface_internal_preload((IWineD3DSurface *) This, SRGB_RGB);
3376 src_offscreen = surface_is_offscreen(SrcSurface);
3377 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3378 if (!noBackBufferBackup && !Src->texture_name)
3380 /* Get it a description */
3381 surface_internal_preload(SrcSurface, SRGB_RGB);
3383 ENTER_GL();
3385 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3386 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3388 if (context->aux_buffers >= 2)
3390 /* Got more than one aux buffer? Use the 2nd aux buffer */
3391 drawBuffer = GL_AUX1;
3393 else if ((!src_offscreen || myDevice->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3395 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3396 drawBuffer = GL_AUX0;
3399 if(noBackBufferBackup) {
3400 glGenTextures(1, &backup);
3401 checkGLcall("glGenTextures");
3402 glBindTexture(GL_TEXTURE_2D, backup);
3403 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3404 texture_target = GL_TEXTURE_2D;
3405 } else {
3406 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3407 * we are reading from the back buffer, the backup can be used as source texture
3409 texture_target = Src->texture_target;
3410 glBindTexture(texture_target, Src->texture_name);
3411 checkGLcall("glBindTexture(texture_target, Src->texture_name)");
3412 glEnable(texture_target);
3413 checkGLcall("glEnable(texture_target)");
3415 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3416 Src->Flags &= ~SFLAG_INTEXTURE;
3419 if (src_offscreen)
3421 TRACE("Reading from an offscreen target\n");
3422 upsidedown = !upsidedown;
3423 glReadBuffer(myDevice->offscreenBuffer);
3425 else
3427 glReadBuffer(surface_get_gl_buffer(SrcSurface));
3430 /* TODO: Only back up the part that will be overwritten */
3431 glCopyTexSubImage2D(texture_target, 0,
3432 0, 0 /* read offsets */,
3433 0, 0,
3434 fbwidth,
3435 fbheight);
3437 checkGLcall("glCopyTexSubImage2D");
3439 /* No issue with overriding these - the sampler is dirty due to blit usage */
3440 glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3441 wined3d_gl_mag_filter(magLookup, Filter));
3442 checkGLcall("glTexParameteri");
3443 glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3444 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
3445 checkGLcall("glTexParameteri");
3447 if(!swapchain || (IWineD3DSurface *) Src == swapchain->backBuffer[0]) {
3448 src = backup ? backup : Src->texture_name;
3449 } else {
3450 glReadBuffer(GL_FRONT);
3451 checkGLcall("glReadBuffer(GL_FRONT)");
3453 glGenTextures(1, &src);
3454 checkGLcall("glGenTextures(1, &src)");
3455 glBindTexture(GL_TEXTURE_2D, src);
3456 checkGLcall("glBindTexture(GL_TEXTURE_2D, src)");
3458 /* TODO: Only copy the part that will be read. Use srect->x1, srect->y2 as origin, but with the width watch
3459 * out for power of 2 sizes
3461 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Src->pow2Width, Src->pow2Height, 0,
3462 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3463 checkGLcall("glTexImage2D");
3464 glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
3465 0, 0 /* read offsets */,
3466 0, 0,
3467 fbwidth,
3468 fbheight);
3470 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3471 checkGLcall("glTexParameteri");
3472 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3473 checkGLcall("glTexParameteri");
3475 glReadBuffer(GL_BACK);
3476 checkGLcall("glReadBuffer(GL_BACK)");
3478 if(texture_target != GL_TEXTURE_2D) {
3479 glDisable(texture_target);
3480 glEnable(GL_TEXTURE_2D);
3481 texture_target = GL_TEXTURE_2D;
3484 checkGLcall("glEnd and previous");
3486 left = srect->x1;
3487 right = srect->x2;
3489 if(upsidedown) {
3490 top = Src->currentDesc.Height - srect->y1;
3491 bottom = Src->currentDesc.Height - srect->y2;
3492 } else {
3493 top = Src->currentDesc.Height - srect->y2;
3494 bottom = Src->currentDesc.Height - srect->y1;
3497 if(Src->Flags & SFLAG_NORMCOORD) {
3498 left /= Src->pow2Width;
3499 right /= Src->pow2Width;
3500 top /= Src->pow2Height;
3501 bottom /= Src->pow2Height;
3504 /* draw the source texture stretched and upside down. The correct surface is bound already */
3505 glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3506 glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3508 context_set_draw_buffer(context, drawBuffer);
3509 glReadBuffer(drawBuffer);
3511 glBegin(GL_QUADS);
3512 /* bottom left */
3513 glTexCoord2f(left, bottom);
3514 glVertex2i(0, fbheight);
3516 /* top left */
3517 glTexCoord2f(left, top);
3518 glVertex2i(0, fbheight - drect->y2 - drect->y1);
3520 /* top right */
3521 glTexCoord2f(right, top);
3522 glVertex2i(drect->x2 - drect->x1, fbheight - drect->y2 - drect->y1);
3524 /* bottom right */
3525 glTexCoord2f(right, bottom);
3526 glVertex2i(drect->x2 - drect->x1, fbheight);
3527 glEnd();
3528 checkGLcall("glEnd and previous");
3530 if (texture_target != This->texture_target)
3532 glDisable(texture_target);
3533 glEnable(This->texture_target);
3534 texture_target = This->texture_target;
3537 /* Now read the stretched and upside down image into the destination texture */
3538 glBindTexture(texture_target, This->texture_name);
3539 checkGLcall("glBindTexture");
3540 glCopyTexSubImage2D(texture_target,
3542 drect->x1, drect->y1, /* xoffset, yoffset */
3543 0, 0, /* We blitted the image to the origin */
3544 drect->x2 - drect->x1, drect->y2 - drect->y1);
3545 checkGLcall("glCopyTexSubImage2D");
3547 if(drawBuffer == GL_BACK) {
3548 /* Write the back buffer backup back */
3549 if(backup) {
3550 if(texture_target != GL_TEXTURE_2D) {
3551 glDisable(texture_target);
3552 glEnable(GL_TEXTURE_2D);
3553 texture_target = GL_TEXTURE_2D;
3555 glBindTexture(GL_TEXTURE_2D, backup);
3556 checkGLcall("glBindTexture(GL_TEXTURE_2D, backup)");
3557 } else {
3558 if (texture_target != Src->texture_target)
3560 glDisable(texture_target);
3561 glEnable(Src->texture_target);
3562 texture_target = Src->texture_target;
3564 glBindTexture(Src->texture_target, Src->texture_name);
3565 checkGLcall("glBindTexture(Src->texture_target, Src->texture_name)");
3568 glBegin(GL_QUADS);
3569 /* top left */
3570 glTexCoord2f(0.0f, (float)fbheight / (float)Src->pow2Height);
3571 glVertex2i(0, 0);
3573 /* bottom left */
3574 glTexCoord2f(0.0f, 0.0f);
3575 glVertex2i(0, fbheight);
3577 /* bottom right */
3578 glTexCoord2f((float)fbwidth / (float)Src->pow2Width, 0.0f);
3579 glVertex2i(fbwidth, Src->currentDesc.Height);
3581 /* top right */
3582 glTexCoord2f((float) fbwidth / (float) Src->pow2Width, (float) fbheight / (float) Src->pow2Height);
3583 glVertex2i(fbwidth, 0);
3584 glEnd();
3586 glDisable(texture_target);
3587 checkGLcall("glDisable(texture_target)");
3589 /* Cleanup */
3590 if (src != Src->texture_name && src != backup)
3592 glDeleteTextures(1, &src);
3593 checkGLcall("glDeleteTextures(1, &src)");
3595 if(backup) {
3596 glDeleteTextures(1, &backup);
3597 checkGLcall("glDeleteTextures(1, &backup)");
3600 LEAVE_GL();
3602 wglFlush(); /* Flush to ensure ordering across contexts. */
3604 context_release(context);
3606 /* The texture is now most up to date - If the surface is a render target and has a drawable, this
3607 * path is never entered
3609 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INTEXTURE, TRUE);
3612 /* Not called from the VTable */
3613 static HRESULT IWineD3DSurfaceImpl_BltOverride(IWineD3DSurfaceImpl *This, const RECT *DestRect,
3614 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx,
3615 WINED3DTEXTUREFILTERTYPE Filter)
3617 IWineD3DDeviceImpl *myDevice = This->resource.device;
3618 WINED3DRECT rect;
3619 IWineD3DSwapChainImpl *srcSwapchain = NULL, *dstSwapchain = NULL;
3620 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
3622 TRACE("(%p)->(%p,%p,%p,%08x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
3624 /* Get the swapchain. One of the surfaces has to be a primary surface */
3625 if(This->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3626 WARN("Destination is in sysmem, rejecting gl blt\n");
3627 return WINED3DERR_INVALIDCALL;
3629 IWineD3DSurface_GetContainer( (IWineD3DSurface *) This, &IID_IWineD3DSwapChain, (void **)&dstSwapchain);
3630 if(dstSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) dstSwapchain);
3631 if(Src) {
3632 if(Src->resource.pool == WINED3DPOOL_SYSTEMMEM) {
3633 WARN("Src is in sysmem, rejecting gl blt\n");
3634 return WINED3DERR_INVALIDCALL;
3636 IWineD3DSurface_GetContainer( (IWineD3DSurface *) Src, &IID_IWineD3DSwapChain, (void **)&srcSwapchain);
3637 if(srcSwapchain) IWineD3DSwapChain_Release((IWineD3DSwapChain *) srcSwapchain);
3640 /* Early sort out of cases where no render target is used */
3641 if(!dstSwapchain && !srcSwapchain &&
3642 SrcSurface != myDevice->render_targets[0] && This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3643 TRACE("No surface is render target, not using hardware blit. Src = %p, dst = %p\n", Src, This);
3644 return WINED3DERR_INVALIDCALL;
3647 /* No destination color keying supported */
3648 if(Flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE)) {
3649 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3650 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3651 return WINED3DERR_INVALIDCALL;
3654 if (DestRect) {
3655 rect.x1 = DestRect->left;
3656 rect.y1 = DestRect->top;
3657 rect.x2 = DestRect->right;
3658 rect.y2 = DestRect->bottom;
3659 } else {
3660 rect.x1 = 0;
3661 rect.y1 = 0;
3662 rect.x2 = This->currentDesc.Width;
3663 rect.y2 = This->currentDesc.Height;
3666 /* The only case where both surfaces on a swapchain are supported is a back buffer -> front buffer blit on the same swapchain */
3667 if(dstSwapchain && dstSwapchain == srcSwapchain && dstSwapchain->backBuffer &&
3668 ((IWineD3DSurface *) This == dstSwapchain->frontBuffer) && SrcSurface == dstSwapchain->backBuffer[0]) {
3669 /* Half-life does a Blt from the back buffer to the front buffer,
3670 * Full surface size, no flags... Use present instead
3672 * This path will only be entered for d3d7 and ddraw apps, because d3d8/9 offer no way to blit TO the front buffer
3675 /* Check rects - IWineD3DDevice_Present doesn't handle them */
3676 while(1)
3678 RECT mySrcRect;
3679 TRACE("Looking if a Present can be done...\n");
3680 /* Source Rectangle must be full surface */
3681 if( SrcRect ) {
3682 if(SrcRect->left != 0 || SrcRect->top != 0 ||
3683 SrcRect->right != Src->currentDesc.Width || SrcRect->bottom != Src->currentDesc.Height) {
3684 TRACE("No, Source rectangle doesn't match\n");
3685 break;
3688 mySrcRect.left = 0;
3689 mySrcRect.top = 0;
3690 mySrcRect.right = Src->currentDesc.Width;
3691 mySrcRect.bottom = Src->currentDesc.Height;
3693 /* No stretching may occur */
3694 if(mySrcRect.right != rect.x2 - rect.x1 ||
3695 mySrcRect.bottom != rect.y2 - rect.y1) {
3696 TRACE("No, stretching is done\n");
3697 break;
3700 /* Destination must be full surface or match the clipping rectangle */
3701 if(This->clipper && ((IWineD3DClipperImpl *) This->clipper)->hWnd)
3703 RECT cliprect;
3704 POINT pos[2];
3705 GetClientRect(((IWineD3DClipperImpl *) This->clipper)->hWnd, &cliprect);
3706 pos[0].x = rect.x1;
3707 pos[0].y = rect.y1;
3708 pos[1].x = rect.x2;
3709 pos[1].y = rect.y2;
3710 MapWindowPoints(GetDesktopWindow(), ((IWineD3DClipperImpl *) This->clipper)->hWnd,
3711 pos, 2);
3713 if(pos[0].x != cliprect.left || pos[0].y != cliprect.top ||
3714 pos[1].x != cliprect.right || pos[1].y != cliprect.bottom)
3716 TRACE("No, dest rectangle doesn't match(clipper)\n");
3717 TRACE("Clip rect at (%d,%d)-(%d,%d)\n", cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
3718 TRACE("Blt dest: (%d,%d)-(%d,%d)\n", rect.x1, rect.y1, rect.x2, rect.y2);
3719 break;
3722 else
3724 if(rect.x1 != 0 || rect.y1 != 0 ||
3725 rect.x2 != This->currentDesc.Width || rect.y2 != This->currentDesc.Height) {
3726 TRACE("No, dest rectangle doesn't match(surface size)\n");
3727 break;
3731 TRACE("Yes\n");
3733 /* These flags are unimportant for the flag check, remove them */
3734 if((Flags & ~(WINEDDBLT_DONOTWAIT | WINEDDBLT_WAIT)) == 0) {
3735 WINED3DSWAPEFFECT orig_swap = dstSwapchain->presentParms.SwapEffect;
3737 /* The idea behind this is that a glReadPixels and a glDrawPixels call
3738 * take very long, while a flip is fast.
3739 * This applies to Half-Life, which does such Blts every time it finished
3740 * a frame, and to Prince of Persia 3D, which uses this to draw at least the main
3741 * menu. This is also used by all apps when they do windowed rendering
3743 * The problem is that flipping is not really the same as copying. After a
3744 * Blt the front buffer is a copy of the back buffer, and the back buffer is
3745 * untouched. Therefore it's necessary to override the swap effect
3746 * and to set it back after the flip.
3748 * Windowed Direct3D < 7 apps do the same. The D3D7 sdk demos are nice
3749 * testcases.
3752 dstSwapchain->presentParms.SwapEffect = WINED3DSWAPEFFECT_COPY;
3753 dstSwapchain->presentParms.PresentationInterval = WINED3DPRESENT_INTERVAL_IMMEDIATE;
3755 TRACE("Full screen back buffer -> front buffer blt, performing a flip instead\n");
3756 IWineD3DSwapChain_Present((IWineD3DSwapChain *) dstSwapchain, NULL, NULL, 0, NULL, 0);
3758 dstSwapchain->presentParms.SwapEffect = orig_swap;
3760 return WINED3D_OK;
3762 break;
3765 TRACE("Unsupported blit between buffers on the same swapchain\n");
3766 return WINED3DERR_INVALIDCALL;
3767 } else if(dstSwapchain && dstSwapchain == srcSwapchain) {
3768 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3769 return WINED3DERR_INVALIDCALL;
3770 } else if(dstSwapchain && srcSwapchain) {
3771 FIXME("Implement hardware blit between two different swapchains\n");
3772 return WINED3DERR_INVALIDCALL;
3773 } else if(dstSwapchain) {
3774 if(SrcSurface == myDevice->render_targets[0]) {
3775 TRACE("Blit from active render target to a swapchain\n");
3776 /* Handled with regular texture -> swapchain blit */
3778 } else if(srcSwapchain && This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0]) {
3779 FIXME("Implement blit from a swapchain to the active render target\n");
3780 return WINED3DERR_INVALIDCALL;
3783 if((srcSwapchain || SrcSurface == myDevice->render_targets[0]) && !dstSwapchain) {
3784 /* Blit from render target to texture */
3785 WINED3DRECT srect;
3786 BOOL upsideDown, stretchx;
3787 BOOL paletteOverride = FALSE;
3789 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
3790 TRACE("Color keying not supported by frame buffer to texture blit\n");
3791 return WINED3DERR_INVALIDCALL;
3792 /* Destination color key is checked above */
3795 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3796 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3798 if(SrcRect) {
3799 if(SrcRect->top < SrcRect->bottom) {
3800 srect.y1 = SrcRect->top;
3801 srect.y2 = SrcRect->bottom;
3802 upsideDown = FALSE;
3803 } else {
3804 srect.y1 = SrcRect->bottom;
3805 srect.y2 = SrcRect->top;
3806 upsideDown = TRUE;
3808 srect.x1 = SrcRect->left;
3809 srect.x2 = SrcRect->right;
3810 } else {
3811 srect.x1 = 0;
3812 srect.y1 = 0;
3813 srect.x2 = Src->currentDesc.Width;
3814 srect.y2 = Src->currentDesc.Height;
3815 upsideDown = FALSE;
3817 if(rect.x1 > rect.x2) {
3818 UINT tmp = rect.x2;
3819 rect.x2 = rect.x1;
3820 rect.x1 = tmp;
3821 upsideDown = !upsideDown;
3824 if(rect.x2 - rect.x1 != srect.x2 - srect.x1) {
3825 stretchx = TRUE;
3826 } else {
3827 stretchx = FALSE;
3830 /* When blitting from a render target a texture, the texture isn't required to have a palette.
3831 * In this case grab the palette from the render target. */
3832 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT && !This->palette)
3834 paletteOverride = TRUE;
3835 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3836 This->palette = Src->palette;
3839 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3840 * flip the image nor scale it.
3842 * -> If the app asks for a unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3843 * -> If the app wants a image width an unscaled width, copy it line per line
3844 * -> If the app wants a image that is scaled on the x axis, and the destination rectangle is smaller
3845 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3846 * back buffer. This is slower than reading line per line, thus not used for flipping
3847 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3848 * pixel by pixel
3850 * If EXT_framebuffer_blit is supported that can be used instead. Note that EXT_framebuffer_blit implies
3851 * FBO support, so it doesn't really make sense to try and make it work with different offscreen rendering
3852 * backends.
3854 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
3855 && myDevice->adapter->gl_info.fbo_ops.glBlitFramebuffer
3856 && surface_can_stretch_rect(Src, This))
3858 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, &srect,
3859 (IWineD3DSurface *)This, &rect, Filter, upsideDown);
3860 } else if((!stretchx) || rect.x2 - rect.x1 > Src->currentDesc.Width ||
3861 rect.y2 - rect.y1 > Src->currentDesc.Height) {
3862 TRACE("No stretching in x direction, using direct framebuffer -> texture copy\n");
3863 fb_copy_to_texture_direct(This, SrcSurface, &srect, &rect, upsideDown, Filter);
3864 } else {
3865 TRACE("Using hardware stretching to flip / stretch the texture\n");
3866 fb_copy_to_texture_hwstretch(This, SrcSurface, srcSwapchain, &srect, &rect, upsideDown, Filter);
3869 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3870 if(paletteOverride)
3871 This->palette = NULL;
3873 if(!(This->Flags & SFLAG_DONOTFREE)) {
3874 HeapFree(GetProcessHeap(), 0, This->resource.heapMemory);
3875 This->resource.allocatedMemory = NULL;
3876 This->resource.heapMemory = NULL;
3877 } else {
3878 This->Flags &= ~SFLAG_INSYSMEM;
3881 return WINED3D_OK;
3882 } else if(Src) {
3883 /* Blit from offscreen surface to render target */
3884 float glTexCoord[4];
3885 DWORD oldCKeyFlags = Src->CKeyFlags;
3886 WINEDDCOLORKEY oldBltCKey = Src->SrcBltCKey;
3887 struct wined3d_context *context;
3888 RECT SourceRectangle;
3889 BOOL paletteOverride = FALSE;
3891 TRACE("Blt from surface %p to rendertarget %p\n", Src, This);
3893 if(SrcRect) {
3894 SourceRectangle.left = SrcRect->left;
3895 SourceRectangle.right = SrcRect->right;
3896 SourceRectangle.top = SrcRect->top;
3897 SourceRectangle.bottom = SrcRect->bottom;
3898 } else {
3899 SourceRectangle.left = 0;
3900 SourceRectangle.right = Src->currentDesc.Width;
3901 SourceRectangle.top = 0;
3902 SourceRectangle.bottom = Src->currentDesc.Height;
3905 /* When blitting from an offscreen surface to a rendertarget, the source
3906 * surface is not required to have a palette. Our rendering / conversion
3907 * code further down the road retrieves the palette from the surface, so
3908 * it must have a palette set. */
3909 if (Src->resource.format_desc->format == WINED3DFMT_P8_UINT && !Src->palette)
3911 paletteOverride = TRUE;
3912 TRACE("Source surface (%p) lacks palette, overriding palette with palette %p of destination surface (%p)\n", Src, This->palette, This);
3913 Src->palette = This->palette;
3916 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
3917 && myDevice->adapter->gl_info.fbo_ops.glBlitFramebuffer
3918 && !(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3919 && surface_can_stretch_rect(Src, This))
3921 TRACE("Using stretch_rect_fbo\n");
3922 /* The source is always a texture, but never the currently active render target, and the texture
3923 * contents are never upside down
3925 stretch_rect_fbo((IWineD3DDevice *)myDevice, SrcSurface, (WINED3DRECT *) &SourceRectangle,
3926 (IWineD3DSurface *)This, &rect, Filter, FALSE);
3928 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
3929 if(paletteOverride)
3930 Src->palette = NULL;
3931 return WINED3D_OK;
3934 if(!CalculateTexRect(Src, &SourceRectangle, glTexCoord)) {
3935 /* Fall back to software */
3936 WARN("(%p) Source texture area (%d,%d)-(%d,%d) is too big\n", Src,
3937 SourceRectangle.left, SourceRectangle.top,
3938 SourceRectangle.right, SourceRectangle.bottom);
3939 return WINED3DERR_INVALIDCALL;
3942 /* Color keying: Check if we have to do a color keyed blt,
3943 * and if not check if a color key is activated.
3945 * Just modify the color keying parameters in the surface and restore them afterwards
3946 * The surface keeps track of the color key last used to load the opengl surface.
3947 * PreLoad will catch the change to the flags and color key and reload if necessary.
3949 if(Flags & WINEDDBLT_KEYSRC) {
3950 /* Use color key from surface */
3951 } else if(Flags & WINEDDBLT_KEYSRCOVERRIDE) {
3952 /* Use color key from DDBltFx */
3953 Src->CKeyFlags |= WINEDDSD_CKSRCBLT;
3954 Src->SrcBltCKey = DDBltFx->ddckSrcColorkey;
3955 } else {
3956 /* Do not use color key */
3957 Src->CKeyFlags &= ~WINEDDSD_CKSRCBLT;
3960 /* Now load the surface */
3961 surface_internal_preload((IWineD3DSurface *) Src, SRGB_RGB);
3963 /* Activate the destination context, set it up for blitting */
3964 context = context_acquire(myDevice, (IWineD3DSurface *)This, CTXUSAGE_BLIT);
3966 /* The coordinates of the ddraw front buffer are always fullscreen ('screen coordinates',
3967 * while OpenGL coordinates are window relative.
3968 * Also beware of the origin difference(top left vs bottom left).
3969 * Also beware that the front buffer's surface size is screen width x screen height,
3970 * whereas the real gl drawable size is the size of the window.
3972 if (dstSwapchain && (IWineD3DSurface *)This == dstSwapchain->frontBuffer) {
3973 RECT windowsize;
3974 POINT offset = {0,0};
3975 UINT h;
3976 ClientToScreen(context->win_handle, &offset);
3977 GetClientRect(context->win_handle, &windowsize);
3978 h = windowsize.bottom - windowsize.top;
3979 rect.x1 -= offset.x; rect.x2 -=offset.x;
3980 rect.y1 -= offset.y; rect.y2 -=offset.y;
3981 rect.y1 += This->currentDesc.Height - h; rect.y2 += This->currentDesc.Height - h;
3984 if (!is_identity_fixup(This->resource.format_desc->color_fixup))
3986 FIXME("Destination format %s has a fixup, this is not supported.\n",
3987 debug_d3dformat(This->resource.format_desc->format));
3988 dump_color_fixup_desc(This->resource.format_desc->color_fixup);
3991 if (!myDevice->blitter->color_fixup_supported(Src->resource.format_desc->color_fixup))
3993 FIXME("Source format %s has an unsupported fixup:\n",
3994 debug_d3dformat(Src->resource.format_desc->format));
3995 dump_color_fixup_desc(Src->resource.format_desc->color_fixup);
3998 myDevice->blitter->set_shader((IWineD3DDevice *) myDevice, Src->resource.format_desc,
3999 Src->texture_target, Src->pow2Width, Src->pow2Height);
4001 ENTER_GL();
4003 /* Bind the texture */
4004 glBindTexture(Src->texture_target, Src->texture_name);
4005 checkGLcall("glBindTexture");
4007 /* Filtering for StretchRect */
4008 glTexParameteri(Src->texture_target, GL_TEXTURE_MAG_FILTER,
4009 wined3d_gl_mag_filter(magLookup, Filter));
4010 checkGLcall("glTexParameteri");
4011 glTexParameteri(Src->texture_target, GL_TEXTURE_MIN_FILTER,
4012 wined3d_gl_min_mip_filter(minMipLookup, Filter, WINED3DTEXF_NONE));
4013 checkGLcall("glTexParameteri");
4014 glTexParameteri(Src->texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
4015 glTexParameteri(Src->texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
4016 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
4017 checkGLcall("glTexEnvi");
4019 /* This is for color keying */
4020 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
4021 glEnable(GL_ALPHA_TEST);
4022 checkGLcall("glEnable(GL_ALPHA_TEST)");
4024 /* When the primary render target uses P8, the alpha component contains the palette index.
4025 * Which means that the colorkey is one of the palette entries. In other cases pixels that
4026 * should be masked away have alpha set to 0. */
4027 if(primary_render_target_is_p8(myDevice))
4028 glAlphaFunc(GL_NOTEQUAL, (float)Src->SrcBltCKey.dwColorSpaceLowValue / 256.0f);
4029 else
4030 glAlphaFunc(GL_NOTEQUAL, 0.0f);
4031 checkGLcall("glAlphaFunc");
4032 } else {
4033 glDisable(GL_ALPHA_TEST);
4034 checkGLcall("glDisable(GL_ALPHA_TEST)");
4037 /* Draw a textured quad
4039 glBegin(GL_QUADS);
4041 glColor3f(1.0f, 1.0f, 1.0f);
4042 glTexCoord2f(glTexCoord[0], glTexCoord[2]);
4043 glVertex3f(rect.x1, rect.y1, 0.0f);
4045 glTexCoord2f(glTexCoord[0], glTexCoord[3]);
4046 glVertex3f(rect.x1, rect.y2, 0.0f);
4048 glTexCoord2f(glTexCoord[1], glTexCoord[3]);
4049 glVertex3f(rect.x2, rect.y2, 0.0f);
4051 glTexCoord2f(glTexCoord[1], glTexCoord[2]);
4052 glVertex3f(rect.x2, rect.y1, 0.0f);
4054 glEnd();
4055 checkGLcall("glEnd");
4057 if(Flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE)) {
4058 glDisable(GL_ALPHA_TEST);
4059 checkGLcall("glDisable(GL_ALPHA_TEST)");
4062 glBindTexture(Src->texture_target, 0);
4063 checkGLcall("glBindTexture(Src->texture_target, 0)");
4065 /* Restore the color key parameters */
4066 Src->CKeyFlags = oldCKeyFlags;
4067 Src->SrcBltCKey = oldBltCKey;
4069 /* Clear the palette as the surface didn't have a palette attached, it would confuse GetPalette and other calls */
4070 if(paletteOverride)
4071 Src->palette = NULL;
4073 LEAVE_GL();
4075 /* Leave the opengl state valid for blitting */
4076 myDevice->blitter->unset_shader((IWineD3DDevice *) myDevice);
4078 wglFlush(); /* Flush to ensure ordering across contexts. */
4080 context_release(context);
4082 /* TODO: If the surface is locked often, perform the Blt in software on the memory instead */
4083 /* The surface is now in the drawable. On onscreen surfaces or without fbos the texture
4084 * is outdated now
4086 IWineD3DSurface_ModifyLocation((IWineD3DSurface *) This, SFLAG_INDRAWABLE, TRUE);
4088 return WINED3D_OK;
4089 } else {
4090 /* Source-Less Blit to render target */
4091 if (Flags & WINEDDBLT_COLORFILL) {
4092 /* This is easy to handle for the D3D Device... */
4093 DWORD color;
4095 TRACE("Colorfill\n");
4097 /* This == (IWineD3DSurfaceImpl *) myDevice->render_targets[0] || dstSwapchain
4098 must be true if we are here */
4099 if (This != (IWineD3DSurfaceImpl *) myDevice->render_targets[0] &&
4100 !(This == (IWineD3DSurfaceImpl*) dstSwapchain->frontBuffer ||
4101 (dstSwapchain->backBuffer && This == (IWineD3DSurfaceImpl*) dstSwapchain->backBuffer[0]))) {
4102 TRACE("Surface is higher back buffer, falling back to software\n");
4103 return WINED3DERR_INVALIDCALL;
4106 /* The color as given in the Blt function is in the format of the frame-buffer...
4107 * 'clear' expect it in ARGB format => we need to do some conversion :-)
4109 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT)
4111 DWORD alpha;
4113 if (primary_render_target_is_p8(myDevice)) alpha = DDBltFx->u5.dwFillColor << 24;
4114 else alpha = 0xFF000000;
4116 if (This->palette) {
4117 color = (alpha |
4118 (This->palette->palents[DDBltFx->u5.dwFillColor].peRed << 16) |
4119 (This->palette->palents[DDBltFx->u5.dwFillColor].peGreen << 8) |
4120 (This->palette->palents[DDBltFx->u5.dwFillColor].peBlue));
4121 } else {
4122 color = alpha;
4125 else if (This->resource.format_desc->format == WINED3DFMT_B5G6R5_UNORM)
4127 if (DDBltFx->u5.dwFillColor == 0xFFFF) {
4128 color = 0xFFFFFFFF;
4129 } else {
4130 color = ((0xFF000000) |
4131 ((DDBltFx->u5.dwFillColor & 0xF800) << 8) |
4132 ((DDBltFx->u5.dwFillColor & 0x07E0) << 5) |
4133 ((DDBltFx->u5.dwFillColor & 0x001F) << 3));
4136 else if (This->resource.format_desc->format == WINED3DFMT_B8G8R8_UNORM
4137 || This->resource.format_desc->format == WINED3DFMT_B8G8R8X8_UNORM)
4139 color = 0xFF000000 | DDBltFx->u5.dwFillColor;
4141 else if (This->resource.format_desc->format == WINED3DFMT_B8G8R8A8_UNORM)
4143 color = DDBltFx->u5.dwFillColor;
4145 else {
4146 ERR("Wrong surface type for BLT override(Format doesn't match) !\n");
4147 return WINED3DERR_INVALIDCALL;
4150 TRACE("(%p) executing Render Target override, color = %x\n", This, color);
4151 IWineD3DDeviceImpl_ClearSurface(myDevice, This, 1 /* Number of rectangles */,
4152 &rect, WINED3DCLEAR_TARGET, color, 0.0f /* Z */, 0 /* Stencil */);
4153 return WINED3D_OK;
4157 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
4158 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
4159 return WINED3DERR_INVALIDCALL;
4162 static HRESULT IWineD3DSurfaceImpl_BltZ(IWineD3DSurfaceImpl *This, const RECT *DestRect,
4163 IWineD3DSurface *SrcSurface, const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx)
4165 IWineD3DDeviceImpl *myDevice = This->resource.device;
4166 float depth;
4168 if (Flags & WINEDDBLT_DEPTHFILL) {
4169 switch(This->resource.format_desc->format)
4171 case WINED3DFMT_D16_UNORM:
4172 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000ffff;
4173 break;
4174 case WINED3DFMT_S1_UINT_D15_UNORM:
4175 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x0000fffe;
4176 break;
4177 case WINED3DFMT_D24_UNORM_S8_UINT:
4178 case WINED3DFMT_X8D24_UNORM:
4179 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0x00ffffff;
4180 break;
4181 case WINED3DFMT_D32_UNORM:
4182 depth = (float) DDBltFx->u5.dwFillDepth / (float) 0xffffffff;
4183 break;
4184 default:
4185 depth = 0.0f;
4186 ERR("Unexpected format for depth fill: %s\n", debug_d3dformat(This->resource.format_desc->format));
4189 return IWineD3DDevice_Clear((IWineD3DDevice *) myDevice,
4190 DestRect == NULL ? 0 : 1,
4191 (const WINED3DRECT *)DestRect,
4192 WINED3DCLEAR_ZBUFFER,
4193 0x00000000,
4194 depth,
4195 0x00000000);
4198 FIXME("(%p): Unsupp depthstencil blit\n", This);
4199 return WINED3DERR_INVALIDCALL;
4202 static HRESULT WINAPI IWineD3DSurfaceImpl_Blt(IWineD3DSurface *iface, const RECT *DestRect, IWineD3DSurface *SrcSurface,
4203 const RECT *SrcRect, DWORD Flags, const WINEDDBLTFX *DDBltFx, WINED3DTEXTUREFILTERTYPE Filter) {
4204 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4205 IWineD3DSurfaceImpl *Src = (IWineD3DSurfaceImpl *) SrcSurface;
4206 IWineD3DDeviceImpl *myDevice = This->resource.device;
4208 TRACE("(%p)->(%p,%p,%p,%x,%p)\n", This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx);
4209 TRACE("(%p): Usage is %s\n", This, debug_d3dusage(This->resource.usage));
4211 if ( (This->Flags & SFLAG_LOCKED) || ((Src != NULL) && (Src->Flags & SFLAG_LOCKED)))
4213 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
4214 return WINEDDERR_SURFACEBUSY;
4217 /* Accessing the depth stencil is supposed to fail between a BeginScene and EndScene pair,
4218 * except depth blits, which seem to work
4220 if(iface == myDevice->stencilBufferTarget || (SrcSurface && SrcSurface == myDevice->stencilBufferTarget)) {
4221 if(myDevice->inScene && !(Flags & WINEDDBLT_DEPTHFILL)) {
4222 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
4223 return WINED3DERR_INVALIDCALL;
4224 } else if(IWineD3DSurfaceImpl_BltZ(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx) == WINED3D_OK) {
4225 TRACE("Z Blit override handled the blit\n");
4226 return WINED3D_OK;
4230 /* Special cases for RenderTargets */
4231 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
4232 ( Src && (Src->resource.usage & WINED3DUSAGE_RENDERTARGET) )) {
4233 if(IWineD3DSurfaceImpl_BltOverride(This, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter) == WINED3D_OK) return WINED3D_OK;
4236 /* For the rest call the X11 surface implementation.
4237 * For RenderTargets this should be implemented OpenGL accelerated in BltOverride,
4238 * other Blts are rather rare
4240 return IWineD3DBaseSurfaceImpl_Blt(iface, DestRect, SrcSurface, SrcRect, Flags, DDBltFx, Filter);
4243 static HRESULT WINAPI IWineD3DSurfaceImpl_BltFast(IWineD3DSurface *iface, DWORD dstx, DWORD dsty,
4244 IWineD3DSurface *Source, const RECT *rsrc, DWORD trans)
4246 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4247 IWineD3DSurfaceImpl *srcImpl = (IWineD3DSurfaceImpl *) Source;
4248 IWineD3DDeviceImpl *myDevice = This->resource.device;
4250 TRACE("(%p)->(%d, %d, %p, %p, %08x\n", iface, dstx, dsty, Source, rsrc, trans);
4252 if ( (This->Flags & SFLAG_LOCKED) || (srcImpl->Flags & SFLAG_LOCKED))
4254 WARN(" Surface is busy, returning DDERR_SURFACEBUSY\n");
4255 return WINEDDERR_SURFACEBUSY;
4258 if(myDevice->inScene &&
4259 (iface == myDevice->stencilBufferTarget ||
4260 (Source == myDevice->stencilBufferTarget))) {
4261 TRACE("Attempt to access the depth stencil surface in a BeginScene / EndScene pair, returning WINED3DERR_INVALIDCALL\n");
4262 return WINED3DERR_INVALIDCALL;
4265 /* Special cases for RenderTargets */
4266 if( (This->resource.usage & WINED3DUSAGE_RENDERTARGET) ||
4267 (srcImpl->resource.usage & WINED3DUSAGE_RENDERTARGET) ) {
4269 RECT SrcRect, DstRect;
4270 DWORD Flags=0;
4272 if(rsrc) {
4273 SrcRect.left = rsrc->left;
4274 SrcRect.top= rsrc->top;
4275 SrcRect.bottom = rsrc->bottom;
4276 SrcRect.right = rsrc->right;
4277 } else {
4278 SrcRect.left = 0;
4279 SrcRect.top = 0;
4280 SrcRect.right = srcImpl->currentDesc.Width;
4281 SrcRect.bottom = srcImpl->currentDesc.Height;
4284 DstRect.left = dstx;
4285 DstRect.top=dsty;
4286 DstRect.right = dstx + SrcRect.right - SrcRect.left;
4287 DstRect.bottom = dsty + SrcRect.bottom - SrcRect.top;
4289 /* Convert BltFast flags into Btl ones because it is called from SurfaceImpl_Blt as well */
4290 if(trans & WINEDDBLTFAST_SRCCOLORKEY)
4291 Flags |= WINEDDBLT_KEYSRC;
4292 if(trans & WINEDDBLTFAST_DESTCOLORKEY)
4293 Flags |= WINEDDBLT_KEYDEST;
4294 if(trans & WINEDDBLTFAST_WAIT)
4295 Flags |= WINEDDBLT_WAIT;
4296 if(trans & WINEDDBLTFAST_DONOTWAIT)
4297 Flags |= WINEDDBLT_DONOTWAIT;
4299 if(IWineD3DSurfaceImpl_BltOverride(This, &DstRect, Source, &SrcRect, Flags, NULL, WINED3DTEXF_POINT) == WINED3D_OK) return WINED3D_OK;
4303 return IWineD3DBaseSurfaceImpl_BltFast(iface, dstx, dsty, Source, rsrc, trans);
4306 static HRESULT WINAPI IWineD3DSurfaceImpl_RealizePalette(IWineD3DSurface *iface)
4308 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4309 RGBQUAD col[256];
4310 IWineD3DPaletteImpl *pal = This->palette;
4311 unsigned int n;
4312 TRACE("(%p)\n", This);
4314 if (!pal) return WINED3D_OK;
4316 if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
4317 || This->resource.format_desc->format == WINED3DFMT_P8_UINT_A8_UNORM)
4319 int bpp;
4320 GLenum format, internal, type;
4321 CONVERT_TYPES convert;
4323 /* Check if we are using a RTL mode which uses texturing for uploads */
4324 BOOL use_texture = (wined3d_settings.rendertargetlock_mode == RTL_READTEX);
4326 /* Check if we have hardware palette conversion if we have convert is set to NO_CONVERSION */
4327 d3dfmt_get_conv(This, TRUE, use_texture, &format, &internal, &type, &convert, &bpp, FALSE);
4329 if((This->resource.usage & WINED3DUSAGE_RENDERTARGET) && (convert == NO_CONVERSION))
4331 IWineD3DDeviceImpl *device = This->resource.device;
4332 struct wined3d_context *context;
4334 /* Make sure the texture is up to date. This call doesn't do anything if the texture is already up to date. */
4335 IWineD3DSurface_LoadLocation(iface, SFLAG_INTEXTURE, NULL);
4337 /* We want to force a palette refresh, so mark the drawable as not being up to date */
4338 IWineD3DSurface_ModifyLocation(iface, SFLAG_INDRAWABLE, FALSE);
4340 /* Re-upload the palette */
4341 context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4342 d3dfmt_p8_upload_palette(iface, convert);
4343 context_release(context);
4344 } else {
4345 if(!(This->Flags & SFLAG_INSYSMEM)) {
4346 TRACE("Palette changed with surface that does not have an up to date system memory copy\n");
4347 IWineD3DSurface_LoadLocation(iface, SFLAG_INSYSMEM, NULL);
4349 TRACE("Dirtifying surface\n");
4350 IWineD3DSurface_ModifyLocation(iface, SFLAG_INSYSMEM, TRUE);
4354 if(This->Flags & SFLAG_DIBSECTION) {
4355 TRACE("(%p): Updating the hdc's palette\n", This);
4356 for (n=0; n<256; n++) {
4357 col[n].rgbRed = pal->palents[n].peRed;
4358 col[n].rgbGreen = pal->palents[n].peGreen;
4359 col[n].rgbBlue = pal->palents[n].peBlue;
4360 col[n].rgbReserved = 0;
4362 SetDIBColorTable(This->hDC, 0, 256, col);
4365 /* Propagate the changes to the drawable when we have a palette. */
4366 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET)
4367 IWineD3DSurface_LoadLocation(iface, SFLAG_INDRAWABLE, NULL);
4369 return WINED3D_OK;
4372 static HRESULT WINAPI IWineD3DSurfaceImpl_PrivateSetup(IWineD3DSurface *iface) {
4373 /** Check against the maximum texture sizes supported by the video card **/
4374 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4375 const struct wined3d_gl_info *gl_info = &This->resource.device->adapter->gl_info;
4376 unsigned int pow2Width, pow2Height;
4378 This->texture_name = 0;
4379 This->texture_target = GL_TEXTURE_2D;
4381 /* Non-power2 support */
4382 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINE_NORMALIZED_TEXRECT])
4384 pow2Width = This->currentDesc.Width;
4385 pow2Height = This->currentDesc.Height;
4387 else
4389 /* Find the nearest pow2 match */
4390 pow2Width = pow2Height = 1;
4391 while (pow2Width < This->currentDesc.Width) pow2Width <<= 1;
4392 while (pow2Height < This->currentDesc.Height) pow2Height <<= 1;
4394 This->pow2Width = pow2Width;
4395 This->pow2Height = pow2Height;
4397 if (pow2Width > This->currentDesc.Width || pow2Height > This->currentDesc.Height) {
4398 /** TODO: add support for non power two compressed textures **/
4399 if (This->resource.format_desc->Flags & WINED3DFMT_FLAG_COMPRESSED)
4401 FIXME("(%p) Compressed non-power-two textures are not supported w(%d) h(%d)\n",
4402 This, This->currentDesc.Width, This->currentDesc.Height);
4403 return WINED3DERR_NOTAVAILABLE;
4407 if(pow2Width != This->currentDesc.Width ||
4408 pow2Height != This->currentDesc.Height) {
4409 This->Flags |= SFLAG_NONPOW2;
4412 TRACE("%p\n", This);
4413 if ((This->pow2Width > gl_info->limits.texture_size || This->pow2Height > gl_info->limits.texture_size)
4414 && !(This->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
4416 /* one of three options
4417 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)
4418 2: Set the texture to the maximum size (bad idea)
4419 3: WARN and return WINED3DERR_NOTAVAILABLE;
4420 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.
4422 if(This->resource.pool == WINED3DPOOL_DEFAULT || This->resource.pool == WINED3DPOOL_MANAGED)
4424 WARN("(%p) Unable to allocate a surface which exceeds the maximum OpenGL texture size\n", This);
4425 return WINED3DERR_NOTAVAILABLE;
4428 /* We should never use this surface in combination with OpenGL! */
4429 TRACE("(%p) Creating an oversized surface: %ux%u\n", This, This->pow2Width, This->pow2Height);
4431 /* This will be initialized on the first blt */
4432 This->glRect.left = 0;
4433 This->glRect.top = 0;
4434 This->glRect.right = 0;
4435 This->glRect.bottom = 0;
4436 } else {
4437 /* Don't use ARB_TEXTURE_RECTANGLE in case the surface format is P8 and EXT_PALETTED_TEXTURE
4438 is used in combination with texture uploads (RTL_READTEX/RTL_TEXTEX). The reason is that EXT_PALETTED_TEXTURE
4439 doesn't work in combination with ARB_TEXTURE_RECTANGLE.
4441 if (This->Flags & SFLAG_NONPOW2 && gl_info->supported[ARB_TEXTURE_RECTANGLE]
4442 && !(This->resource.format_desc->format == WINED3DFMT_P8_UINT
4443 && gl_info->supported[EXT_PALETTED_TEXTURE]
4444 && wined3d_settings.rendertargetlock_mode == RTL_READTEX))
4446 This->texture_target = GL_TEXTURE_RECTANGLE_ARB;
4447 This->pow2Width = This->currentDesc.Width;
4448 This->pow2Height = This->currentDesc.Height;
4449 This->Flags &= ~(SFLAG_NONPOW2 | SFLAG_NORMCOORD);
4452 This->glRect.left = 0;
4453 This->glRect.top = 0;
4454 This->glRect.right = This->pow2Width;
4455 This->glRect.bottom = This->pow2Height;
4458 if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
4459 switch(wined3d_settings.offscreen_rendering_mode) {
4460 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
4461 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
4465 This->Flags |= SFLAG_INSYSMEM;
4467 return WINED3D_OK;
4470 /* GL locking is done by the caller */
4471 static void surface_depth_blt(IWineD3DSurfaceImpl *This, GLuint texture, GLsizei w, GLsizei h, GLenum target)
4473 IWineD3DDeviceImpl *device = This->resource.device;
4474 struct blt_info info;
4475 GLint old_binding = 0;
4477 glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
4479 glDisable(GL_CULL_FACE);
4480 glDisable(GL_BLEND);
4481 glDisable(GL_ALPHA_TEST);
4482 glDisable(GL_SCISSOR_TEST);
4483 glDisable(GL_STENCIL_TEST);
4484 glEnable(GL_DEPTH_TEST);
4485 glDepthFunc(GL_ALWAYS);
4486 glDepthMask(GL_TRUE);
4487 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
4488 glViewport(0, 0, w, h);
4490 surface_get_blt_info(target, NULL, w, h, &info);
4491 GL_EXTCALL(glActiveTextureARB(GL_TEXTURE0_ARB));
4492 glGetIntegerv(info.binding, &old_binding);
4493 glBindTexture(info.bind_target, texture);
4495 device->shader_backend->shader_select_depth_blt((IWineD3DDevice *)device, info.tex_type);
4497 glBegin(GL_TRIANGLE_STRIP);
4498 glTexCoord3fv(info.coords[0]);
4499 glVertex2f(-1.0f, -1.0f);
4500 glTexCoord3fv(info.coords[1]);
4501 glVertex2f(1.0f, -1.0f);
4502 glTexCoord3fv(info.coords[2]);
4503 glVertex2f(-1.0f, 1.0f);
4504 glTexCoord3fv(info.coords[3]);
4505 glVertex2f(1.0f, 1.0f);
4506 glEnd();
4508 glBindTexture(info.bind_target, old_binding);
4510 glPopAttrib();
4512 device->shader_backend->shader_deselect_depth_blt((IWineD3DDevice *)device);
4515 void surface_modify_ds_location(IWineD3DSurface *iface, DWORD location) {
4516 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4518 TRACE("(%p) New location %#x\n", This, location);
4520 if (location & ~SFLAG_DS_LOCATIONS) {
4521 FIXME("(%p) Invalid location (%#x) specified\n", This, location);
4524 This->Flags &= ~SFLAG_DS_LOCATIONS;
4525 This->Flags |= location;
4528 /* Context activation is done by the caller. */
4529 void surface_load_ds_location(IWineD3DSurface *iface, struct wined3d_context *context, DWORD location)
4531 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
4532 IWineD3DDeviceImpl *device = This->resource.device;
4533 const struct wined3d_gl_info *gl_info = context->gl_info;
4535 TRACE("(%p) New location %#x\n", This, location);
4537 /* TODO: Make this work for modes other than FBO */
4538 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
4540 if (This->Flags & location) {
4541 TRACE("(%p) Location (%#x) is already up to date\n", This, location);
4542 return;
4545 if (This->current_renderbuffer) {
4546 FIXME("(%p) Not supported with fixed up depth stencil\n", This);
4547 return;
4550 if (location == SFLAG_DS_OFFSCREEN) {
4551 if (This->Flags & SFLAG_DS_ONSCREEN) {
4552 GLint old_binding = 0;
4553 GLenum bind_target;
4555 TRACE("(%p) Copying onscreen depth buffer to depth texture\n", This);
4557 ENTER_GL();
4559 if (!device->depth_blt_texture) {
4560 glGenTextures(1, &device->depth_blt_texture);
4563 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
4564 * directly on the FBO texture. That's because we need to flip. */
4565 context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4566 if (This->texture_target == GL_TEXTURE_RECTANGLE_ARB)
4568 glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
4569 bind_target = GL_TEXTURE_RECTANGLE_ARB;
4570 } else {
4571 glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
4572 bind_target = GL_TEXTURE_2D;
4574 glBindTexture(bind_target, device->depth_blt_texture);
4575 glCopyTexImage2D(bind_target, This->texture_level, This->resource.format_desc->glInternal,
4576 0, 0, This->currentDesc.Width, This->currentDesc.Height, 0);
4577 glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4578 glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4579 glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
4580 glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4581 glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
4582 glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
4583 glBindTexture(bind_target, old_binding);
4585 /* Setup the destination */
4586 if (!device->depth_blt_rb) {
4587 gl_info->fbo_ops.glGenRenderbuffers(1, &device->depth_blt_rb);
4588 checkGLcall("glGenRenderbuffersEXT");
4590 if (device->depth_blt_rb_w != This->currentDesc.Width
4591 || device->depth_blt_rb_h != This->currentDesc.Height) {
4592 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, device->depth_blt_rb);
4593 checkGLcall("glBindRenderbufferEXT");
4594 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8,
4595 This->currentDesc.Width, This->currentDesc.Height);
4596 checkGLcall("glRenderbufferStorageEXT");
4597 device->depth_blt_rb_w = This->currentDesc.Width;
4598 device->depth_blt_rb_h = This->currentDesc.Height;
4601 context_bind_fbo(context, GL_FRAMEBUFFER, &context->dst_fbo);
4602 gl_info->fbo_ops.glFramebufferRenderbuffer(GL_FRAMEBUFFER,
4603 GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, device->depth_blt_rb);
4604 checkGLcall("glFramebufferRenderbufferEXT");
4605 context_attach_depth_stencil_fbo(context, GL_FRAMEBUFFER, iface, FALSE);
4607 /* Do the actual blit */
4608 surface_depth_blt(This, device->depth_blt_texture, This->currentDesc.Width, This->currentDesc.Height, bind_target);
4609 checkGLcall("depth_blt");
4611 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER, &context->current_fbo->id);
4612 else context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4614 LEAVE_GL();
4616 wglFlush(); /* Flush to ensure ordering across contexts. */
4618 else
4620 FIXME("No up to date depth stencil location\n");
4622 } else if (location == SFLAG_DS_ONSCREEN) {
4623 if (This->Flags & SFLAG_DS_OFFSCREEN) {
4624 TRACE("(%p) Copying depth texture to onscreen depth buffer\n", This);
4626 ENTER_GL();
4628 context_bind_fbo(context, GL_FRAMEBUFFER, NULL);
4629 surface_depth_blt(This, This->texture_name, This->currentDesc.Width,
4630 This->currentDesc.Height, This->texture_target);
4631 checkGLcall("depth_blt");
4633 if (context->current_fbo) context_bind_fbo(context, GL_FRAMEBUFFER, &context->current_fbo->id);
4635 LEAVE_GL();
4637 wglFlush(); /* Flush to ensure ordering across contexts. */
4639 else
4641 FIXME("No up to date depth stencil location\n");
4643 } else {
4644 ERR("(%p) Invalid location (%#x) specified\n", This, location);
4647 This->Flags |= location;
4650 static void WINAPI IWineD3DSurfaceImpl_ModifyLocation(IWineD3DSurface *iface, DWORD flag, BOOL persistent) {
4651 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4652 IWineD3DBaseTexture *texture;
4653 IWineD3DSurfaceImpl *overlay;
4655 TRACE("(%p)->(%s, %s)\n", iface, debug_surflocation(flag),
4656 persistent ? "TRUE" : "FALSE");
4658 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4659 if (surface_is_offscreen(iface))
4661 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
4662 if (flag & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE)) flag |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
4664 else
4666 TRACE("Surface %p is an onscreen surface\n", iface);
4670 if(persistent) {
4671 if(((This->Flags & SFLAG_INTEXTURE) && !(flag & SFLAG_INTEXTURE)) ||
4672 ((This->Flags & SFLAG_INSRGBTEX) && !(flag & SFLAG_INSRGBTEX))) {
4673 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4674 TRACE("Passing to container\n");
4675 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4676 IWineD3DBaseTexture_Release(texture);
4679 This->Flags &= ~SFLAG_LOCATIONS;
4680 This->Flags |= flag;
4682 /* Redraw emulated overlays, if any */
4683 if(flag & SFLAG_INDRAWABLE && !list_empty(&This->overlays)) {
4684 LIST_FOR_EACH_ENTRY(overlay, &This->overlays, IWineD3DSurfaceImpl, overlay_entry) {
4685 IWineD3DSurface_DrawOverlay((IWineD3DSurface *) overlay);
4688 } else {
4689 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) && (flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))) {
4690 if (IWineD3DSurface_GetContainer(iface, &IID_IWineD3DBaseTexture, (void **)&texture) == WINED3D_OK) {
4691 TRACE("Passing to container\n");
4692 IWineD3DBaseTexture_SetDirty(texture, TRUE);
4693 IWineD3DBaseTexture_Release(texture);
4696 This->Flags &= ~flag;
4699 if(!(This->Flags & SFLAG_LOCATIONS)) {
4700 ERR("%p: Surface does not have any up to date location\n", This);
4704 static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT *rect_in)
4706 IWineD3DDeviceImpl *device = This->resource.device;
4707 IWineD3DBaseTextureImpl *texture;
4708 struct wined3d_context *context;
4709 RECT rect;
4710 struct blt_info info;
4712 if(rect_in) {
4713 rect = *rect_in;
4714 } else {
4715 rect.left = 0;
4716 rect.top = 0;
4717 rect.right = This->currentDesc.Width;
4718 rect.bottom = This->currentDesc.Height;
4721 surface_get_blt_info(This->texture_target, &rect, This->pow2Width, This->pow2Height, &info);
4723 context = context_acquire(device, (IWineD3DSurface*)This, CTXUSAGE_BLIT);
4725 ENTER_GL();
4727 glEnable(info.bind_target);
4728 checkGLcall("glEnable(bind_target)");
4729 glBindTexture(info.bind_target, This->texture_name);
4730 checkGLcall("glBindTexture(bind_target, This->texture_name)");
4731 glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
4732 checkGLcall("glTexParameteri");
4733 glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
4734 checkGLcall("glTexParameteri");
4736 if (context->render_offscreen)
4738 LONG tmp = rect.top;
4739 rect.top = rect.bottom;
4740 rect.bottom = tmp;
4743 glBegin(GL_TRIANGLE_STRIP);
4744 glTexCoord3fv(info.coords[0]);
4745 glVertex2i(rect.left, rect.top);
4747 glTexCoord3fv(info.coords[1]);
4748 glVertex2i(rect.right, rect.top);
4750 glTexCoord3fv(info.coords[2]);
4751 glVertex2i(rect.left, rect.bottom);
4753 glTexCoord3fv(info.coords[3]);
4754 glVertex2i(rect.right, rect.bottom);
4755 glEnd();
4757 glDisable(info.bind_target);
4758 checkGLcall("glDisable(bind_target)");
4760 LEAVE_GL();
4762 wglFlush(); /* Flush to ensure ordering across contexts. */
4764 /* We changed the filtering settings on the texture. Inform the
4765 * container about this to get the filters reset properly next draw. */
4766 if (SUCCEEDED(IWineD3DSurface_GetContainer((IWineD3DSurface *)This, &IID_IWineD3DBaseTexture, (void **)&texture)))
4768 texture->baseTexture.texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
4769 texture->baseTexture.texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
4770 texture->baseTexture.texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
4771 IWineD3DBaseTexture_Release((IWineD3DBaseTexture *)texture);
4774 context_release(context);
4777 /*****************************************************************************
4778 * IWineD3DSurface::LoadLocation
4780 * Copies the current surface data from wherever it is to the requested
4781 * location. The location is one of the surface flags, SFLAG_INSYSMEM,
4782 * SFLAG_INTEXTURE and SFLAG_INDRAWABLE. When the surface is current in
4783 * multiple locations, the gl texture is preferred over the drawable, which is
4784 * preferred over system memory. The PBO counts as system memory. If rect is
4785 * not NULL, only the specified rectangle is copied (only supported for
4786 * sysmem<->drawable copies at the moment). If rect is NULL, the destination
4787 * location is marked up to date after the copy.
4789 * Parameters:
4790 * flag: Surface location flag to be updated
4791 * rect: rectangle to be copied
4793 * Returns:
4794 * WINED3D_OK on success
4795 * WINED3DERR_DEVICELOST on an internal error
4797 *****************************************************************************/
4798 static HRESULT WINAPI IWineD3DSurfaceImpl_LoadLocation(IWineD3DSurface *iface, DWORD flag, const RECT *rect) {
4799 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
4800 IWineD3DDeviceImpl *device = This->resource.device;
4801 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4802 GLenum format, internal, type;
4803 CONVERT_TYPES convert;
4804 int bpp;
4805 int width, pitch, outpitch;
4806 BYTE *mem;
4807 BOOL drawable_read_ok = TRUE;
4808 BOOL in_fbo = FALSE;
4810 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO) {
4811 if (surface_is_offscreen(iface))
4813 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets.
4814 * Prefer SFLAG_INTEXTURE. */
4815 if (flag == SFLAG_INDRAWABLE) flag = SFLAG_INTEXTURE;
4816 drawable_read_ok = FALSE;
4817 in_fbo = TRUE;
4819 else
4821 TRACE("Surface %p is an onscreen surface\n", iface);
4825 TRACE("(%p)->(%s, %p)\n", iface, debug_surflocation(flag), rect);
4826 if(rect) {
4827 TRACE("Rectangle: (%d,%d)-(%d,%d)\n", rect->left, rect->top, rect->right, rect->bottom);
4830 if(This->Flags & flag) {
4831 TRACE("Location already up to date\n");
4832 return WINED3D_OK;
4835 if(!(This->Flags & SFLAG_LOCATIONS)) {
4836 ERR("%p: Surface does not have any up to date location\n", This);
4837 This->Flags |= SFLAG_LOST;
4838 return WINED3DERR_DEVICELOST;
4841 if(flag == SFLAG_INSYSMEM) {
4842 surface_prepare_system_memory(This);
4844 /* Download the surface to system memory */
4845 if (This->Flags & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX))
4847 struct wined3d_context *context = NULL;
4849 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4851 surface_bind_and_dirtify(This, !(This->Flags & SFLAG_INTEXTURE));
4852 surface_download_data(This);
4854 if (context) context_release(context);
4856 else
4858 /* Note: It might be faster to download into a texture first. */
4859 read_from_framebuffer(This, rect,
4860 This->resource.allocatedMemory,
4861 IWineD3DSurface_GetPitch(iface));
4863 } else if(flag == SFLAG_INDRAWABLE) {
4864 if(This->Flags & SFLAG_INTEXTURE) {
4865 surface_blt_to_drawable(This, rect);
4866 } else {
4867 if((This->Flags & SFLAG_LOCATIONS) == SFLAG_INSRGBTEX) {
4868 /* This needs a shader to convert the srgb data sampled from the GL texture into RGB
4869 * values, otherwise we get incorrect values in the target. For now go the slow way
4870 * via a system memory copy
4872 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4875 d3dfmt_get_conv(This, TRUE /* We need color keying */, FALSE /* We won't use textures */, &format, &internal, &type, &convert, &bpp, FALSE);
4877 /* The width is in 'length' not in bytes */
4878 width = This->currentDesc.Width;
4879 pitch = IWineD3DSurface_GetPitch(iface);
4881 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4882 * but it isn't set (yet) in all cases it is getting called. */
4883 if ((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO))
4885 struct wined3d_context *context = NULL;
4887 TRACE("Removing the pbo attached to surface %p\n", This);
4889 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4890 surface_remove_pbo(This);
4891 if (context) context_release(context);
4894 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4895 int height = This->currentDesc.Height;
4897 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4898 outpitch = width * bpp;
4899 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4901 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4902 if(!mem) {
4903 ERR("Out of memory %d, %d!\n", outpitch, height);
4904 return WINED3DERR_OUTOFVIDEOMEMORY;
4906 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4908 This->Flags |= SFLAG_CONVERTED;
4909 } else {
4910 This->Flags &= ~SFLAG_CONVERTED;
4911 mem = This->resource.allocatedMemory;
4914 flush_to_framebuffer_drawpixels(This, format, type, bpp, mem);
4916 /* Don't delete PBO memory */
4917 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
4918 HeapFree(GetProcessHeap(), 0, mem);
4920 } else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */ {
4921 if (drawable_read_ok && (This->Flags & SFLAG_INDRAWABLE)) {
4922 read_from_framebuffer_texture(This, flag == SFLAG_INSRGBTEX);
4924 else
4926 /* Upload from system memory */
4927 BOOL srgb = flag == SFLAG_INSRGBTEX;
4928 struct wined3d_context *context = NULL;
4930 d3dfmt_get_conv(This, TRUE /* We need color keying */, TRUE /* We will use textures */,
4931 &format, &internal, &type, &convert, &bpp, srgb);
4933 if(srgb) {
4934 if((This->Flags & (SFLAG_INTEXTURE | SFLAG_INSYSMEM)) == SFLAG_INTEXTURE) {
4935 /* Performance warning ... */
4936 FIXME("%p: Downloading rgb texture to reload it as srgb\n", This);
4937 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4939 } else {
4940 if((This->Flags & (SFLAG_INSRGBTEX | SFLAG_INSYSMEM)) == SFLAG_INSRGBTEX) {
4941 /* Performance warning ... */
4942 FIXME("%p: Downloading srgb texture to reload it as rgb\n", This);
4943 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4946 if(!(This->Flags & SFLAG_INSYSMEM)) {
4947 /* Should not happen */
4948 ERR("Trying to load a texture from sysmem, but SFLAG_INSYSMEM is not set\n");
4949 /* Lets hope we get it from somewhere... */
4950 IWineD3DSurfaceImpl_LoadLocation(iface, SFLAG_INSYSMEM, rect);
4953 if (!device->isInDraw) context = context_acquire(device, NULL, CTXUSAGE_RESOURCELOAD);
4955 surface_prepare_texture(This, srgb);
4956 surface_bind_and_dirtify(This, srgb);
4958 if(This->CKeyFlags & WINEDDSD_CKSRCBLT) {
4959 This->Flags |= SFLAG_GLCKEY;
4960 This->glCKey = This->SrcBltCKey;
4962 else This->Flags &= ~SFLAG_GLCKEY;
4964 /* The width is in 'length' not in bytes */
4965 width = This->currentDesc.Width;
4966 pitch = IWineD3DSurface_GetPitch(iface);
4968 /* Don't use PBOs for converted surfaces. During PBO conversion we look at SFLAG_CONVERTED
4969 * but it isn't set (yet) in all cases it is getting called. */
4970 if((convert != NO_CONVERSION) && (This->Flags & SFLAG_PBO)) {
4971 TRACE("Removing the pbo attached to surface %p\n", This);
4972 surface_remove_pbo(This);
4975 if((convert != NO_CONVERSION) && This->resource.allocatedMemory) {
4976 int height = This->currentDesc.Height;
4978 /* Stick to the alignment for the converted surface too, makes it easier to load the surface */
4979 outpitch = width * bpp;
4980 outpitch = (outpitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4982 mem = HeapAlloc(GetProcessHeap(), 0, outpitch * height);
4983 if(!mem) {
4984 ERR("Out of memory %d, %d!\n", outpitch, height);
4985 if (context) context_release(context);
4986 return WINED3DERR_OUTOFVIDEOMEMORY;
4988 d3dfmt_convert_surface(This->resource.allocatedMemory, mem, pitch, width, height, outpitch, convert, This);
4990 else if (This->resource.format_desc->format == WINED3DFMT_P8_UINT
4991 && (gl_info->supported[EXT_PALETTED_TEXTURE] || device->blitter->color_fixup_supported(This->resource.format_desc->color_fixup)))
4993 d3dfmt_p8_upload_palette(iface, convert);
4994 mem = This->resource.allocatedMemory;
4995 } else {
4996 mem = This->resource.allocatedMemory;
4999 /* Make sure the correct pitch is used */
5000 ENTER_GL();
5001 glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
5002 LEAVE_GL();
5004 if (This->Flags & SFLAG_NONPOW2) {
5005 TRACE("non power of two support\n");
5006 if (mem || (This->Flags & SFLAG_PBO)) {
5007 surface_upload_data(This, internal, This->currentDesc.Width, This->currentDesc.Height, format, type, mem);
5009 } else {
5010 /* When making the realloc conditional, keep in mind that GL_APPLE_client_storage may be in use, and This->resource.allocatedMemory
5011 * changed. So also keep track of memory changes. In this case the texture has to be reallocated
5013 if (mem || (This->Flags & SFLAG_PBO)) {
5014 surface_upload_data(This, internal, This->glRect.right - This->glRect.left, This->glRect.bottom - This->glRect.top, format, type, mem);
5018 /* Restore the default pitch */
5019 ENTER_GL();
5020 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
5021 LEAVE_GL();
5023 if (context) context_release(context);
5025 /* Don't delete PBO memory */
5026 if((mem != This->resource.allocatedMemory) && !(This->Flags & SFLAG_PBO))
5027 HeapFree(GetProcessHeap(), 0, mem);
5031 if(rect == NULL) {
5032 This->Flags |= flag;
5035 if (in_fbo && (This->Flags & (SFLAG_INTEXTURE | SFLAG_INDRAWABLE))) {
5036 /* With ORM_FBO, SFLAG_INTEXTURE and SFLAG_INDRAWABLE are the same for offscreen targets. */
5037 This->Flags |= (SFLAG_INTEXTURE | SFLAG_INDRAWABLE);
5040 return WINED3D_OK;
5043 static HRESULT WINAPI IWineD3DSurfaceImpl_SetContainer(IWineD3DSurface *iface, IWineD3DBase *container)
5045 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
5046 IWineD3DSwapChain *swapchain = NULL;
5048 /* Update the drawable size method */
5049 if(container) {
5050 IWineD3DBase_QueryInterface(container, &IID_IWineD3DSwapChain, (void **) &swapchain);
5052 if(swapchain) {
5053 This->get_drawable_size = get_drawable_size_swapchain;
5054 IWineD3DSwapChain_Release(swapchain);
5055 } else if(This->resource.usage & WINED3DUSAGE_RENDERTARGET) {
5056 switch(wined3d_settings.offscreen_rendering_mode) {
5057 case ORM_FBO: This->get_drawable_size = get_drawable_size_fbo; break;
5058 case ORM_BACKBUFFER: This->get_drawable_size = get_drawable_size_backbuffer; break;
5062 return IWineD3DBaseSurfaceImpl_SetContainer(iface, container);
5065 static WINED3DSURFTYPE WINAPI IWineD3DSurfaceImpl_GetImplType(IWineD3DSurface *iface) {
5066 return SURFACE_OPENGL;
5069 static HRESULT WINAPI IWineD3DSurfaceImpl_DrawOverlay(IWineD3DSurface *iface) {
5070 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
5071 HRESULT hr;
5073 /* If there's no destination surface there is nothing to do */
5074 if(!This->overlay_dest) return WINED3D_OK;
5076 /* Blt calls ModifyLocation on the dest surface, which in turn calls DrawOverlay to
5077 * update the overlay. Prevent an endless recursion
5079 if(This->overlay_dest->Flags & SFLAG_INOVERLAYDRAW) {
5080 return WINED3D_OK;
5082 This->overlay_dest->Flags |= SFLAG_INOVERLAYDRAW;
5083 hr = IWineD3DSurfaceImpl_Blt((IWineD3DSurface *) This->overlay_dest, &This->overlay_destrect,
5084 iface, &This->overlay_srcrect, WINEDDBLT_WAIT,
5085 NULL, WINED3DTEXF_LINEAR);
5086 This->overlay_dest->Flags &= ~SFLAG_INOVERLAYDRAW;
5088 return hr;
5091 BOOL surface_is_offscreen(IWineD3DSurface *iface)
5093 IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *) iface;
5094 IWineD3DSwapChainImpl *swapchain = (IWineD3DSwapChainImpl *) This->container;
5096 /* Not on a swapchain - must be offscreen */
5097 if (!(This->Flags & SFLAG_SWAPCHAIN)) return TRUE;
5099 /* The front buffer is always onscreen */
5100 if(iface == swapchain->frontBuffer) return FALSE;
5102 /* If the swapchain is rendered to an FBO, the backbuffer is
5103 * offscreen, otherwise onscreen */
5104 return swapchain->render_to_fbo;
5107 const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl =
5109 /* IUnknown */
5110 IWineD3DBaseSurfaceImpl_QueryInterface,
5111 IWineD3DBaseSurfaceImpl_AddRef,
5112 IWineD3DSurfaceImpl_Release,
5113 /* IWineD3DResource */
5114 IWineD3DBaseSurfaceImpl_GetParent,
5115 IWineD3DBaseSurfaceImpl_SetPrivateData,
5116 IWineD3DBaseSurfaceImpl_GetPrivateData,
5117 IWineD3DBaseSurfaceImpl_FreePrivateData,
5118 IWineD3DBaseSurfaceImpl_SetPriority,
5119 IWineD3DBaseSurfaceImpl_GetPriority,
5120 IWineD3DSurfaceImpl_PreLoad,
5121 IWineD3DSurfaceImpl_UnLoad,
5122 IWineD3DBaseSurfaceImpl_GetType,
5123 /* IWineD3DSurface */
5124 IWineD3DBaseSurfaceImpl_GetContainer,
5125 IWineD3DBaseSurfaceImpl_GetDesc,
5126 IWineD3DSurfaceImpl_LockRect,
5127 IWineD3DSurfaceImpl_UnlockRect,
5128 IWineD3DSurfaceImpl_GetDC,
5129 IWineD3DSurfaceImpl_ReleaseDC,
5130 IWineD3DSurfaceImpl_Flip,
5131 IWineD3DSurfaceImpl_Blt,
5132 IWineD3DBaseSurfaceImpl_GetBltStatus,
5133 IWineD3DBaseSurfaceImpl_GetFlipStatus,
5134 IWineD3DBaseSurfaceImpl_IsLost,
5135 IWineD3DBaseSurfaceImpl_Restore,
5136 IWineD3DSurfaceImpl_BltFast,
5137 IWineD3DBaseSurfaceImpl_GetPalette,
5138 IWineD3DBaseSurfaceImpl_SetPalette,
5139 IWineD3DSurfaceImpl_RealizePalette,
5140 IWineD3DBaseSurfaceImpl_SetColorKey,
5141 IWineD3DBaseSurfaceImpl_GetPitch,
5142 IWineD3DSurfaceImpl_SetMem,
5143 IWineD3DBaseSurfaceImpl_SetOverlayPosition,
5144 IWineD3DBaseSurfaceImpl_GetOverlayPosition,
5145 IWineD3DBaseSurfaceImpl_UpdateOverlayZOrder,
5146 IWineD3DBaseSurfaceImpl_UpdateOverlay,
5147 IWineD3DBaseSurfaceImpl_SetClipper,
5148 IWineD3DBaseSurfaceImpl_GetClipper,
5149 /* Internal use: */
5150 IWineD3DSurfaceImpl_LoadTexture,
5151 IWineD3DSurfaceImpl_BindTexture,
5152 IWineD3DSurfaceImpl_SaveSnapshot,
5153 IWineD3DSurfaceImpl_SetContainer,
5154 IWineD3DBaseSurfaceImpl_GetData,
5155 IWineD3DSurfaceImpl_SetFormat,
5156 IWineD3DSurfaceImpl_PrivateSetup,
5157 IWineD3DSurfaceImpl_ModifyLocation,
5158 IWineD3DSurfaceImpl_LoadLocation,
5159 IWineD3DSurfaceImpl_GetImplType,
5160 IWineD3DSurfaceImpl_DrawOverlay
5162 #undef GLINFO_LOCATION
5164 #define GLINFO_LOCATION device->adapter->gl_info
5165 static HRESULT ffp_blit_alloc(IWineD3DDevice *iface) { return WINED3D_OK; }
5166 /* Context activation is done by the caller. */
5167 static void ffp_blit_free(IWineD3DDevice *iface) { }
5169 /* Context activation is done by the caller. */
5170 static HRESULT ffp_blit_set(IWineD3DDevice *iface, const struct GlPixelFormatDesc *format_desc,
5171 GLenum textype, UINT width, UINT height)
5173 ENTER_GL();
5174 glEnable(textype);
5175 checkGLcall("glEnable(textype)");
5176 LEAVE_GL();
5177 return WINED3D_OK;
5180 /* Context activation is done by the caller. */
5181 static void ffp_blit_unset(IWineD3DDevice *iface)
5183 IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) iface;
5184 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5186 ENTER_GL();
5187 glDisable(GL_TEXTURE_2D);
5188 checkGLcall("glDisable(GL_TEXTURE_2D)");
5189 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
5191 glDisable(GL_TEXTURE_CUBE_MAP_ARB);
5192 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
5194 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
5196 glDisable(GL_TEXTURE_RECTANGLE_ARB);
5197 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
5199 LEAVE_GL();
5202 static BOOL ffp_blit_color_fixup_supported(struct color_fixup_desc fixup)
5204 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
5206 TRACE("Checking support for fixup:\n");
5207 dump_color_fixup_desc(fixup);
5210 /* We only support identity conversions. */
5211 if (is_identity_fixup(fixup))
5213 TRACE("[OK]\n");
5214 return TRUE;
5217 TRACE("[FAILED]\n");
5218 return FALSE;
5221 const struct blit_shader ffp_blit = {
5222 ffp_blit_alloc,
5223 ffp_blit_free,
5224 ffp_blit_set,
5225 ffp_blit_unset,
5226 ffp_blit_color_fixup_supported