wined3d: Move OpenGL initialisation code to adapter_gl.c.
[wine.git] / dlls / wined3d / surface.c
blob2f74ad689fd5942368c735ad10f1bb63f80c3ebd
1 /*
2 * Copyright 1997-2000 Marcus Meissner
3 * Copyright 1998-2000 Lionel Ulmer
4 * Copyright 2000-2001 TransGaming Technologies Inc.
5 * Copyright 2002-2005 Jason Edmeades
6 * Copyright 2002-2003 Raphael Junqueira
7 * Copyright 2004 Christian Costa
8 * Copyright 2005 Oliver Stieber
9 * Copyright 2006-2011, 2013-2014 Stefan Dösinger for CodeWeavers
10 * Copyright 2007-2008 Henri Verbeet
11 * Copyright 2006-2008 Roderick Colenbrander
12 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
36 static const DWORD surface_simple_locations = WINED3D_LOCATION_SYSMEM
37 | WINED3D_LOCATION_USER_MEMORY | WINED3D_LOCATION_BUFFER;
39 /* Works correctly only for <= 4 bpp formats. */
40 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
42 masks[0] = ((1u << format->red_size) - 1) << format->red_offset;
43 masks[1] = ((1u << format->green_size) - 1) << format->green_offset;
44 masks[2] = ((1u << format->blue_size) - 1) << format->blue_offset;
47 static BOOL texture2d_is_full_rect(const struct wined3d_texture *texture, unsigned int level, const RECT *r)
49 unsigned int t;
51 t = wined3d_texture_get_level_width(texture, level);
52 if ((r->left && r->right) || abs(r->right - r->left) != t)
53 return FALSE;
54 t = wined3d_texture_get_level_height(texture, level);
55 if ((r->top && r->bottom) || abs(r->bottom - r->top) != t)
56 return FALSE;
57 return TRUE;
60 static void texture2d_depth_blt_fbo(const struct wined3d_device *device, struct wined3d_context *context,
61 struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, DWORD src_location,
62 const RECT *src_rect, struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
63 DWORD dst_location, const RECT *dst_rect)
65 const struct wined3d_gl_info *gl_info = context->gl_info;
66 DWORD src_mask, dst_mask;
67 GLbitfield gl_mask;
69 TRACE("device %p, src_texture %p, src_sub_resource_idx %u, src_location %s, src_rect %s, "
70 "dst_texture %p, dst_sub_resource_idx %u, dst_location %s, dst_rect %s.\n", device,
71 src_texture, src_sub_resource_idx, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect),
72 dst_texture, dst_sub_resource_idx, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
74 src_mask = src_texture->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
75 dst_mask = dst_texture->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
77 if (src_mask != dst_mask)
79 ERR("Incompatible formats %s and %s.\n",
80 debug_d3dformat(src_texture->resource.format->id),
81 debug_d3dformat(dst_texture->resource.format->id));
82 return;
85 if (!src_mask)
87 ERR("Not a depth / stencil format: %s.\n",
88 debug_d3dformat(src_texture->resource.format->id));
89 return;
92 gl_mask = 0;
93 if (src_mask & WINED3DFMT_FLAG_DEPTH)
94 gl_mask |= GL_DEPTH_BUFFER_BIT;
95 if (src_mask & WINED3DFMT_FLAG_STENCIL)
96 gl_mask |= GL_STENCIL_BUFFER_BIT;
98 /* Make sure the locations are up-to-date. Loading the destination
99 * surface isn't required if the entire surface is overwritten. */
100 wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, src_location);
101 if (!texture2d_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, dst_rect))
102 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
103 else
104 wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, dst_location);
106 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, 0,
107 &src_texture->resource, src_sub_resource_idx, src_location);
108 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
110 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, 0,
111 &dst_texture->resource, dst_sub_resource_idx, dst_location);
112 context_set_draw_buffer(context, GL_NONE);
113 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
114 context_invalidate_state(context, STATE_FRAMEBUFFER);
116 if (gl_mask & GL_DEPTH_BUFFER_BIT)
118 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
119 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
121 if (gl_mask & GL_STENCIL_BUFFER_BIT)
123 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
125 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
126 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
128 gl_info->gl_ops.gl.p_glStencilMask(~0U);
129 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
132 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
133 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
135 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
136 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
137 checkGLcall("glBlitFramebuffer()");
140 static BOOL is_multisample_location(const struct wined3d_texture *texture, DWORD location)
142 if (location == WINED3D_LOCATION_RB_MULTISAMPLE)
143 return TRUE;
144 if (location != WINED3D_LOCATION_TEXTURE_RGB && location != WINED3D_LOCATION_TEXTURE_SRGB)
145 return FALSE;
146 return texture->target == GL_TEXTURE_2D_MULTISAMPLE || texture->target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
149 /* Blit between surface locations. Onscreen on different swapchains is not supported.
150 * Depth / stencil is not supported. Context activation is done by the caller. */
151 static void texture2d_blt_fbo(const struct wined3d_device *device, struct wined3d_context *context,
152 enum wined3d_texture_filter_type filter, struct wined3d_texture *src_texture,
153 unsigned int src_sub_resource_idx, DWORD src_location, const RECT *src_rect,
154 struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, DWORD dst_location,
155 const RECT *dst_rect)
157 struct wined3d_texture *required_texture, *restore_texture;
158 unsigned int required_idx, restore_idx;
159 const struct wined3d_gl_info *gl_info;
160 GLenum gl_filter;
161 GLenum buffer;
162 RECT s, d;
164 TRACE("device %p, context %p, filter %s, src_texture %p, src_sub_resource_idx %u, src_location %s, "
165 "src_rect %s, dst_texture %p, dst_sub_resource_idx %u, dst_location %s, dst_rect %s.\n",
166 device, context, debug_d3dtexturefiltertype(filter), src_texture, src_sub_resource_idx,
167 wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect), dst_texture,
168 dst_sub_resource_idx, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
170 switch (filter)
172 case WINED3D_TEXF_LINEAR:
173 gl_filter = GL_LINEAR;
174 break;
176 default:
177 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
178 /* fall through */
179 case WINED3D_TEXF_NONE:
180 case WINED3D_TEXF_POINT:
181 gl_filter = GL_NEAREST;
182 break;
185 /* Resolve the source surface first if needed. */
186 if (is_multisample_location(src_texture, src_location)
187 && (src_texture->resource.format->id != dst_texture->resource.format->id
188 || abs(src_rect->bottom - src_rect->top) != abs(dst_rect->bottom - dst_rect->top)
189 || abs(src_rect->right - src_rect->left) != abs(dst_rect->right - dst_rect->left)))
190 src_location = WINED3D_LOCATION_RB_RESOLVED;
192 /* Make sure the locations are up-to-date. Loading the destination
193 * surface isn't required if the entire surface is overwritten. (And is
194 * in fact harmful if we're being called by surface_load_location() with
195 * the purpose of loading the destination surface.) */
196 wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, src_location);
197 if (!texture2d_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, dst_rect))
198 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
199 else
200 wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, dst_location);
202 if (src_location == WINED3D_LOCATION_DRAWABLE)
204 required_texture = src_texture;
205 required_idx = src_sub_resource_idx;
207 else if (dst_location == WINED3D_LOCATION_DRAWABLE)
209 required_texture = dst_texture;
210 required_idx = dst_sub_resource_idx;
212 else
214 required_texture = NULL;
215 required_idx = 0;
218 restore_texture = context->current_rt.texture;
219 restore_idx = context->current_rt.sub_resource_idx;
220 if (restore_texture != required_texture || restore_idx != required_idx)
221 context = context_acquire(device, required_texture, required_idx);
222 else
223 restore_texture = NULL;
225 if (!context->valid)
227 context_release(context);
228 WARN("Invalid context, skipping blit.\n");
229 return;
232 gl_info = context->gl_info;
234 if (src_location == WINED3D_LOCATION_DRAWABLE)
236 TRACE("Source texture %p is onscreen.\n", src_texture);
237 buffer = wined3d_texture_get_gl_buffer(src_texture);
238 s = *src_rect;
239 wined3d_texture_translate_drawable_coords(src_texture, context->win_handle, &s);
240 src_rect = &s;
242 else
244 TRACE("Source texture %p is offscreen.\n", src_texture);
245 buffer = GL_COLOR_ATTACHMENT0;
248 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER,
249 &src_texture->resource, src_sub_resource_idx, NULL, 0, src_location);
250 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
251 checkGLcall("glReadBuffer()");
252 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
254 if (dst_location == WINED3D_LOCATION_DRAWABLE)
256 TRACE("Destination texture %p is onscreen.\n", dst_texture);
257 buffer = wined3d_texture_get_gl_buffer(dst_texture);
258 d = *dst_rect;
259 wined3d_texture_translate_drawable_coords(dst_texture, context->win_handle, &d);
260 dst_rect = &d;
262 else
264 TRACE("Destination texture %p is offscreen.\n", dst_texture);
265 buffer = GL_COLOR_ATTACHMENT0;
268 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER,
269 &dst_texture->resource, dst_sub_resource_idx, NULL, 0, dst_location);
270 context_set_draw_buffer(context, buffer);
271 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
272 context_invalidate_state(context, STATE_FRAMEBUFFER);
274 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
275 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
276 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
277 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
278 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
280 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
281 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
283 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
284 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, GL_COLOR_BUFFER_BIT, gl_filter);
285 checkGLcall("glBlitFramebuffer()");
287 if (dst_location == WINED3D_LOCATION_DRAWABLE && dst_texture->swapchain->front_buffer == dst_texture)
288 gl_info->gl_ops.gl.p_glFlush();
290 if (restore_texture)
291 context_restore(context, restore_texture, restore_idx);
294 static BOOL fbo_blitter_supported(enum wined3d_blit_op blit_op, const struct wined3d_gl_info *gl_info,
295 const struct wined3d_resource *src_resource, DWORD src_location,
296 const struct wined3d_resource *dst_resource, DWORD dst_location)
298 const struct wined3d_format *src_format = src_resource->format;
299 const struct wined3d_format *dst_format = dst_resource->format;
301 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
302 return FALSE;
304 /* Source and/or destination need to be on the GL side */
305 if (!(src_resource->access & dst_resource->access & WINED3D_RESOURCE_ACCESS_GPU))
306 return FALSE;
308 if (src_resource->type != WINED3D_RTYPE_TEXTURE_2D)
309 return FALSE;
311 switch (blit_op)
313 case WINED3D_BLIT_OP_COLOR_BLIT:
314 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
315 || (src_resource->usage & WINED3DUSAGE_RENDERTARGET)))
316 return FALSE;
317 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
318 || (dst_resource->usage & WINED3DUSAGE_RENDERTARGET)))
319 return FALSE;
320 if ((src_format->id != dst_format->id || dst_location == WINED3D_LOCATION_DRAWABLE)
321 && (!is_identity_fixup(src_format->color_fixup) || !is_identity_fixup(dst_format->color_fixup)))
322 return FALSE;
323 break;
325 case WINED3D_BLIT_OP_DEPTH_BLIT:
326 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
327 return FALSE;
328 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
329 return FALSE;
330 /* Accept pure swizzle fixups for depth formats. In general we
331 * ignore the stencil component (if present) at the moment and the
332 * swizzle is not relevant with just the depth component. */
333 if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup)
334 || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup))
335 return FALSE;
336 break;
338 default:
339 return FALSE;
342 return TRUE;
345 /* This call just downloads data, the caller is responsible for binding the
346 * correct texture. */
347 /* Context activation is done by the caller. */
348 static void texture2d_download_data(struct wined3d_texture *texture, unsigned int sub_resource_idx,
349 const struct wined3d_context *context, DWORD dst_location)
351 const struct wined3d_format *format = texture->resource.format;
352 const struct wined3d_gl_info *gl_info = context->gl_info;
353 struct wined3d_texture_sub_resource *sub_resource;
354 unsigned int dst_row_pitch, dst_slice_pitch;
355 unsigned int src_row_pitch, src_slice_pitch;
356 struct wined3d_bo_address data;
357 BYTE *temporary_mem = NULL;
358 unsigned int level;
359 GLenum target;
360 void *mem;
362 /* Only support read back of converted P8 textures. */
363 if (texture->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT && !format->download)
365 ERR("Trying to read back converted texture %p, %u with format %s.\n",
366 texture, sub_resource_idx, debug_d3dformat(format->id));
367 return;
370 sub_resource = &texture->sub_resources[sub_resource_idx];
371 target = wined3d_texture_get_sub_resource_target(texture, sub_resource_idx);
372 level = sub_resource_idx % texture->level_count;
374 if (target == GL_TEXTURE_2D_ARRAY)
376 if (format->download)
378 FIXME("Reading back converted array texture %p is not supported.\n", texture);
379 return;
382 /* NP2 emulation is not allowed on array textures. */
383 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
384 ERR("Array texture %p uses NP2 emulation.\n", texture);
386 WARN_(d3d_perf)("Downloading all miplevel layers to get the data for a single sub-resource.\n");
388 if (!(temporary_mem = heap_calloc(texture->layer_count, sub_resource->size)))
390 ERR("Out of memory.\n");
391 return;
395 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
397 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
399 if (format->download)
401 FIXME("Reading back converted texture %p with NP2 emulation is not supported.\n", texture);
402 return;
405 wined3d_texture_get_pitch(texture, level, &dst_row_pitch, &dst_slice_pitch);
406 wined3d_format_calculate_pitch(format, texture->resource.device->surface_alignment,
407 wined3d_texture_get_level_pow2_width(texture, level),
408 wined3d_texture_get_level_pow2_height(texture, level),
409 &src_row_pitch, &src_slice_pitch);
410 if (!(temporary_mem = heap_alloc(src_slice_pitch)))
412 ERR("Out of memory.\n");
413 return;
416 if (data.buffer_object)
417 ERR("NP2 emulated texture uses PBO unexpectedly.\n");
418 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
419 ERR("Unexpected compressed format for NP2 emulated texture.\n");
422 if (format->download)
424 struct wined3d_format f;
426 if (data.buffer_object)
427 ERR("Converted texture %p uses PBO unexpectedly.\n", texture);
429 WARN_(d3d_perf)("Downloading converted texture %p, %u with format %s.\n",
430 texture, sub_resource_idx, debug_d3dformat(format->id));
432 f = *format;
433 f.byte_count = format->conv_byte_count;
434 wined3d_texture_get_pitch(texture, level, &dst_row_pitch, &dst_slice_pitch);
435 wined3d_format_calculate_pitch(&f, texture->resource.device->surface_alignment,
436 wined3d_texture_get_level_width(texture, level),
437 wined3d_texture_get_level_height(texture, level),
438 &src_row_pitch, &src_slice_pitch);
440 if (!(temporary_mem = heap_alloc(src_slice_pitch)))
442 ERR("Failed to allocate memory.\n");
443 return;
447 if (temporary_mem)
449 mem = temporary_mem;
451 else if (data.buffer_object)
453 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
454 checkGLcall("glBindBuffer");
455 mem = data.addr;
457 else
459 mem = data.addr;
462 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
464 TRACE("Downloading compressed texture %p, %u, level %u, format %#x, type %#x, data %p.\n",
465 texture, sub_resource_idx, level, format->glFormat, format->glType, mem);
467 GL_EXTCALL(glGetCompressedTexImage(target, level, mem));
468 checkGLcall("glGetCompressedTexImage");
470 else
472 TRACE("Downloading texture %p, %u, level %u, format %#x, type %#x, data %p.\n",
473 texture, sub_resource_idx, level, format->glFormat, format->glType, mem);
475 gl_info->gl_ops.gl.p_glGetTexImage(target, level, format->glFormat, format->glType, mem);
476 checkGLcall("glGetTexImage");
479 if (format->download)
481 format->download(mem, data.addr, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch,
482 wined3d_texture_get_level_width(texture, level),
483 wined3d_texture_get_level_height(texture, level), 1);
485 else if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
487 const BYTE *src_data;
488 unsigned int h, y;
489 BYTE *dst_data;
491 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
492 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
493 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
495 * We're doing this...
497 * instead of boxing the texture :
498 * |<-texture width ->| -->pow2width| /\
499 * |111111111111111111| | |
500 * |222 Texture 222222| boxed empty | texture height
501 * |3333 Data 33333333| | |
502 * |444444444444444444| | \/
503 * ----------------------------------- |
504 * | boxed empty | boxed empty | pow2height
505 * | | | \/
506 * -----------------------------------
509 * we're repacking the data to the expected texture width
511 * |<-texture width ->| -->pow2width| /\
512 * |111111111111111111222222222222222| |
513 * |222333333333333333333444444444444| texture height
514 * |444444 | |
515 * | | \/
516 * | | |
517 * | empty | pow2height
518 * | | \/
519 * -----------------------------------
521 * == is the same as
523 * |<-texture width ->| /\
524 * |111111111111111111|
525 * |222222222222222222|texture height
526 * |333333333333333333|
527 * |444444444444444444| \/
528 * --------------------
530 * This also means that any references to surface memory should work with the data as if it were a
531 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
533 * internally the texture is still stored in a boxed format so any references to textureName will
534 * get a boxed texture with width pow2width and not a texture of width resource.width. */
535 src_data = mem;
536 dst_data = data.addr;
537 TRACE("Repacking the surface data from pitch %u to pitch %u.\n", src_row_pitch, dst_row_pitch);
538 h = wined3d_texture_get_level_height(texture, level);
539 for (y = 0; y < h; ++y)
541 memcpy(dst_data, src_data, dst_row_pitch);
542 src_data += src_row_pitch;
543 dst_data += dst_row_pitch;
546 else if (temporary_mem)
548 unsigned int layer = sub_resource_idx / texture->level_count;
549 void *src_data = temporary_mem + layer * sub_resource->size;
550 if (data.buffer_object)
552 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
553 checkGLcall("glBindBuffer");
554 GL_EXTCALL(glBufferSubData(GL_PIXEL_PACK_BUFFER, 0, sub_resource->size, src_data));
555 checkGLcall("glBufferSubData");
557 else
559 memcpy(data.addr, src_data, sub_resource->size);
563 if (data.buffer_object)
565 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
566 checkGLcall("glBindBuffer");
569 heap_free(temporary_mem);
572 /* See also float_16_to_32() in wined3d_private.h */
573 static inline unsigned short float_32_to_16(const float *in)
575 int exp = 0;
576 float tmp = fabsf(*in);
577 unsigned int mantissa;
578 unsigned short ret;
580 /* Deal with special numbers */
581 if (*in == 0.0f)
582 return 0x0000;
583 if (isnan(*in))
584 return 0x7c01;
585 if (isinf(*in))
586 return (*in < 0.0f ? 0xfc00 : 0x7c00);
588 if (tmp < (float)(1u << 10))
592 tmp = tmp * 2.0f;
593 exp--;
594 } while (tmp < (float)(1u << 10));
596 else if (tmp >= (float)(1u << 11))
600 tmp /= 2.0f;
601 exp++;
602 } while (tmp >= (float)(1u << 11));
605 mantissa = (unsigned int)tmp;
606 if (tmp - mantissa >= 0.5f)
607 ++mantissa; /* Round to nearest, away from zero. */
609 exp += 10; /* Normalize the mantissa. */
610 exp += 15; /* Exponent is encoded with excess 15. */
612 if (exp > 30) /* too big */
614 ret = 0x7c00; /* INF */
616 else if (exp <= 0)
618 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
619 while (exp <= 0)
621 mantissa = mantissa >> 1;
622 ++exp;
624 ret = mantissa & 0x3ff;
626 else
628 ret = (exp << 10) | (mantissa & 0x3ff);
631 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
632 return ret;
635 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
636 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
638 unsigned short *dst_s;
639 const float *src_f;
640 unsigned int x, y;
642 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
644 for (y = 0; y < h; ++y)
646 src_f = (const float *)(src + y * pitch_in);
647 dst_s = (unsigned short *) (dst + y * pitch_out);
648 for (x = 0; x < w; ++x)
650 dst_s[x] = float_32_to_16(src_f + x);
655 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
656 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
658 static const unsigned char convert_5to8[] =
660 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
661 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
662 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
663 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
665 static const unsigned char convert_6to8[] =
667 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
668 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
669 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
670 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
671 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
672 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
673 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
674 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
676 unsigned int x, y;
678 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
680 for (y = 0; y < h; ++y)
682 const WORD *src_line = (const WORD *)(src + y * pitch_in);
683 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
684 for (x = 0; x < w; ++x)
686 WORD pixel = src_line[x];
687 dst_line[x] = 0xff000000u
688 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
689 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
690 | convert_5to8[(pixel & 0x001fu)];
695 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
696 * in both cases we're just setting the X / Alpha channel to 0xff. */
697 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
698 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
700 unsigned int x, y;
702 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
704 for (y = 0; y < h; ++y)
706 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
707 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
709 for (x = 0; x < w; ++x)
711 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
716 static inline BYTE cliptobyte(int x)
718 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
721 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
722 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
724 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
725 unsigned int x, y;
727 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
729 for (y = 0; y < h; ++y)
731 const BYTE *src_line = src + y * pitch_in;
732 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
733 for (x = 0; x < w; ++x)
735 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
736 * C = Y - 16; D = U - 128; E = V - 128;
737 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
738 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
739 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
740 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
741 * U and V are shared between the pixels. */
742 if (!(x & 1)) /* For every even pixel, read new U and V. */
744 d = (int) src_line[1] - 128;
745 e = (int) src_line[3] - 128;
746 r2 = 409 * e + 128;
747 g2 = - 100 * d - 208 * e + 128;
748 b2 = 516 * d + 128;
750 c2 = 298 * ((int) src_line[0] - 16);
751 dst_line[x] = 0xff000000
752 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
753 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
754 | cliptobyte((c2 + b2) >> 8); /* blue */
755 /* Scale RGB values to 0..255 range,
756 * then clip them if still not in range (may be negative),
757 * then shift them within DWORD if necessary. */
758 src_line += 2;
763 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
764 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
766 unsigned int x, y;
767 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
769 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
771 for (y = 0; y < h; ++y)
773 const BYTE *src_line = src + y * pitch_in;
774 WORD *dst_line = (WORD *)(dst + y * pitch_out);
775 for (x = 0; x < w; ++x)
777 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
778 * C = Y - 16; D = U - 128; E = V - 128;
779 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
780 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
781 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
782 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
783 * U and V are shared between the pixels. */
784 if (!(x & 1)) /* For every even pixel, read new U and V. */
786 d = (int) src_line[1] - 128;
787 e = (int) src_line[3] - 128;
788 r2 = 409 * e + 128;
789 g2 = - 100 * d - 208 * e + 128;
790 b2 = 516 * d + 128;
792 c2 = 298 * ((int) src_line[0] - 16);
793 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
794 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
795 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
796 /* Scale RGB values to 0..255 range,
797 * then clip them if still not in range (may be negative),
798 * then shift them within DWORD if necessary. */
799 src_line += 2;
804 struct d3dfmt_converter_desc
806 enum wined3d_format_id from, to;
807 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
810 static const struct d3dfmt_converter_desc converters[] =
812 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
813 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
814 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
815 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
816 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
817 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
820 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
821 enum wined3d_format_id to)
823 unsigned int i;
825 for (i = 0; i < ARRAY_SIZE(converters); ++i)
827 if (converters[i].from == from && converters[i].to == to)
828 return &converters[i];
831 return NULL;
834 static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture,
835 unsigned int sub_resource_idx, const struct wined3d_format *dst_format)
837 unsigned int texture_level = sub_resource_idx % src_texture->level_count;
838 const struct wined3d_format *src_format = src_texture->resource.format;
839 struct wined3d_device *device = src_texture->resource.device;
840 const struct d3dfmt_converter_desc *conv = NULL;
841 unsigned int src_row_pitch, src_slice_pitch;
842 struct wined3d_context *context = NULL;
843 struct wined3d_texture *dst_texture;
844 struct wined3d_bo_address src_data;
845 struct wined3d_resource_desc desc;
846 DWORD map_binding;
848 if (!(conv = find_converter(src_format->id, dst_format->id)) && (!device->d3d_initialized
849 || !is_identity_fixup(src_format->color_fixup) || src_format->conv_byte_count
850 || !is_identity_fixup(dst_format->color_fixup) || dst_format->conv_byte_count
851 || (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)))
853 FIXME("Cannot find a conversion function from format %s to %s.\n",
854 debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
855 return NULL;
858 /* FIXME: Multisampled conversion? */
859 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
860 desc.format = dst_format->id;
861 desc.multisample_type = WINED3D_MULTISAMPLE_NONE;
862 desc.multisample_quality = 0;
863 desc.usage = WINED3DUSAGE_SCRATCH | WINED3DUSAGE_PRIVATE;
864 desc.access = WINED3D_RESOURCE_ACCESS_CPU | WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W;
865 desc.width = wined3d_texture_get_level_width(src_texture, texture_level);
866 desc.height = wined3d_texture_get_level_height(src_texture, texture_level);
867 desc.depth = 1;
868 desc.size = 0;
869 if (FAILED(wined3d_texture_create(device, &desc, 1, 1,
870 WINED3D_TEXTURE_CREATE_MAPPABLE | WINED3D_TEXTURE_CREATE_DISCARD,
871 NULL, NULL, &wined3d_null_parent_ops, &dst_texture)))
873 ERR("Failed to create a destination texture for conversion.\n");
874 return NULL;
877 if (device->d3d_initialized)
878 context = context_acquire(device, NULL, 0);
880 map_binding = src_texture->resource.map_binding;
881 if (!wined3d_texture_load_location(src_texture, sub_resource_idx, context, map_binding))
882 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
883 wined3d_texture_get_pitch(src_texture, texture_level, &src_row_pitch, &src_slice_pitch);
884 wined3d_texture_get_memory(src_texture, sub_resource_idx, &src_data, map_binding);
886 if (conv)
888 unsigned int dst_row_pitch, dst_slice_pitch;
889 struct wined3d_bo_address dst_data;
890 const BYTE *src;
891 BYTE *dst;
893 map_binding = dst_texture->resource.map_binding;
894 if (!wined3d_texture_load_location(dst_texture, 0, context, map_binding))
895 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
896 wined3d_texture_get_pitch(dst_texture, 0, &dst_row_pitch, &dst_slice_pitch);
897 wined3d_texture_get_memory(dst_texture, 0, &dst_data, map_binding);
899 src = context_map_bo_address(context, &src_data,
900 src_texture->sub_resources[sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ);
901 dst = context_map_bo_address(context,
902 &dst_data, dst_texture->sub_resources[0].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_WRITE);
904 conv->convert(src, dst, src_row_pitch, dst_row_pitch, desc.width, desc.height);
906 wined3d_texture_invalidate_location(dst_texture, 0, ~map_binding);
907 context_unmap_bo_address(context, &dst_data, GL_PIXEL_UNPACK_BUFFER);
908 context_unmap_bo_address(context, &src_data, GL_PIXEL_UNPACK_BUFFER);
910 else
912 struct wined3d_box src_box = {0, 0, desc.width, desc.height, 0, 1};
914 TRACE("Using upload conversion.\n");
916 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
917 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
918 wined3d_texture_upload_data(dst_texture, 0, context, src_format, &src_box,
919 wined3d_const_bo_address(&src_data), src_row_pitch, src_slice_pitch, 0, 0, 0, FALSE);
921 wined3d_texture_validate_location(dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB);
922 wined3d_texture_invalidate_location(dst_texture, 0, ~WINED3D_LOCATION_TEXTURE_RGB);
925 if (context)
926 context_release(context);
928 return dst_texture;
931 static void texture2d_read_from_framebuffer(struct wined3d_texture *texture, unsigned int sub_resource_idx,
932 struct wined3d_context *context, DWORD src_location, DWORD dst_location)
934 struct wined3d_device *device = texture->resource.device;
935 struct wined3d_texture *restore_texture;
936 const struct wined3d_gl_info *gl_info;
937 unsigned int row_pitch, slice_pitch;
938 unsigned int width, height, level;
939 struct wined3d_bo_address data;
940 unsigned int restore_idx;
941 BYTE *row, *top, *bottom;
942 BOOL src_is_upside_down;
943 unsigned int i;
944 BYTE *mem;
946 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
948 restore_texture = context->current_rt.texture;
949 restore_idx = context->current_rt.sub_resource_idx;
950 if (restore_texture != texture || restore_idx != sub_resource_idx)
951 context = context_acquire(device, texture, sub_resource_idx);
952 else
953 restore_texture = NULL;
954 gl_info = context->gl_info;
956 if (src_location != texture->resource.draw_binding)
958 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER,
959 &texture->resource, sub_resource_idx, NULL, 0, src_location);
960 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
961 context_invalidate_state(context, STATE_FRAMEBUFFER);
963 else
965 context_apply_blit_state(context, device);
968 /* Select the correct read buffer, and give some debug output.
969 * There is no need to keep track of the current read buffer or reset it,
970 * every part of the code that reads sets the read buffer as desired.
972 if (src_location != WINED3D_LOCATION_DRAWABLE || wined3d_resource_is_offscreen(&texture->resource))
974 /* Mapping the primary render target which is not on a swapchain.
975 * Read from the back buffer. */
976 TRACE("Mapping offscreen render target.\n");
977 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
978 src_is_upside_down = TRUE;
980 else
982 /* Onscreen surfaces are always part of a swapchain */
983 GLenum buffer = wined3d_texture_get_gl_buffer(texture);
984 TRACE("Mapping %#x buffer.\n", buffer);
985 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
986 src_is_upside_down = FALSE;
988 checkGLcall("glReadBuffer");
990 if (data.buffer_object)
992 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
993 checkGLcall("glBindBuffer");
996 level = sub_resource_idx % texture->level_count;
997 wined3d_texture_get_pitch(texture, level, &row_pitch, &slice_pitch);
999 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1000 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / texture->resource.format->byte_count);
1001 checkGLcall("glPixelStorei");
1003 width = wined3d_texture_get_level_width(texture, level);
1004 height = wined3d_texture_get_level_height(texture, level);
1005 gl_info->gl_ops.gl.p_glReadPixels(0, 0, width, height,
1006 texture->resource.format->glFormat,
1007 texture->resource.format->glType, data.addr);
1008 checkGLcall("glReadPixels");
1010 /* Reset previous pixel store pack state */
1011 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
1012 checkGLcall("glPixelStorei");
1014 if (!src_is_upside_down)
1016 /* glReadPixels returns the image upside down, and there is no way to
1017 * prevent this. Flip the lines in software. */
1019 if (!(row = heap_alloc(row_pitch)))
1020 goto error;
1022 if (data.buffer_object)
1024 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
1025 checkGLcall("glMapBuffer");
1027 else
1028 mem = data.addr;
1030 top = mem;
1031 bottom = mem + row_pitch * (height - 1);
1032 for (i = 0; i < height / 2; i++)
1034 memcpy(row, top, row_pitch);
1035 memcpy(top, bottom, row_pitch);
1036 memcpy(bottom, row, row_pitch);
1037 top += row_pitch;
1038 bottom -= row_pitch;
1040 heap_free(row);
1042 if (data.buffer_object)
1043 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
1046 error:
1047 if (data.buffer_object)
1049 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1050 checkGLcall("glBindBuffer");
1053 if (restore_texture)
1054 context_restore(context, restore_texture, restore_idx);
1057 /* Read the framebuffer contents into a texture. Note that this function
1058 * doesn't do any kind of flipping. Using this on an onscreen surface will
1059 * result in a flipped D3D texture.
1061 * Context activation is done by the caller. This function may temporarily
1062 * switch to a different context and restore the original one before return. */
1063 void texture2d_load_fb_texture(struct wined3d_texture *texture,
1064 unsigned int sub_resource_idx, BOOL srgb, struct wined3d_context *context)
1066 struct wined3d_device *device = texture->resource.device;
1067 struct wined3d_texture *restore_texture;
1068 const struct wined3d_gl_info *gl_info;
1069 unsigned int restore_idx, level;
1070 GLenum target;
1072 restore_texture = context->current_rt.texture;
1073 restore_idx = context->current_rt.sub_resource_idx;
1074 if (restore_texture != texture || restore_idx != sub_resource_idx)
1075 context = context_acquire(device, texture, sub_resource_idx);
1076 else
1077 restore_texture = NULL;
1079 gl_info = context->gl_info;
1080 device_invalidate_state(device, STATE_FRAMEBUFFER);
1082 wined3d_texture_prepare_texture(texture, context, srgb);
1083 wined3d_texture_bind_and_dirtify(texture, context, srgb);
1085 TRACE("Reading back offscreen render target %p, %u.\n", texture, sub_resource_idx);
1087 if (wined3d_resource_is_offscreen(&texture->resource))
1088 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1089 else
1090 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(texture));
1091 checkGLcall("glReadBuffer");
1093 level = sub_resource_idx % texture->level_count;
1094 target = wined3d_texture_get_sub_resource_target(texture, sub_resource_idx);
1095 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(target, level, 0, 0, 0, 0,
1096 wined3d_texture_get_level_width(texture, level),
1097 wined3d_texture_get_level_height(texture, level));
1098 checkGLcall("glCopyTexSubImage2D");
1100 if (restore_texture)
1101 context_restore(context, restore_texture, restore_idx);
1104 /* Does a direct frame buffer -> texture copy. Stretching is done with single
1105 * pixel copy calls. */
1106 static void fb_copy_to_texture_direct(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
1107 const RECT *dst_rect_in, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1108 const RECT *src_rect, enum wined3d_texture_filter_type filter)
1110 struct wined3d_device *device = dst_texture->resource.device;
1111 unsigned int src_height, src_level, dst_level;
1112 const struct wined3d_gl_info *gl_info;
1113 float xrel, yrel;
1114 struct wined3d_context *context;
1115 BOOL upsidedown = FALSE;
1116 RECT dst_rect = *dst_rect_in;
1117 GLenum dst_target;
1119 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1120 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1122 if(dst_rect.top > dst_rect.bottom) {
1123 UINT tmp = dst_rect.bottom;
1124 dst_rect.bottom = dst_rect.top;
1125 dst_rect.top = tmp;
1126 upsidedown = TRUE;
1129 context = context_acquire(device, src_texture, src_sub_resource_idx);
1130 gl_info = context->gl_info;
1131 context_apply_blit_state(context, device);
1132 wined3d_texture_load(dst_texture, context, FALSE);
1134 /* Bind the target texture */
1135 context_bind_texture(context, dst_texture->target, dst_texture->texture_rgb.name);
1136 if (wined3d_resource_is_offscreen(&src_texture->resource))
1138 TRACE("Reading from an offscreen target\n");
1139 upsidedown = !upsidedown;
1140 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1142 else
1144 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
1146 checkGLcall("glReadBuffer");
1148 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
1149 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
1151 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1153 FIXME_(d3d_perf)("Doing a pixel by pixel copy from the framebuffer to a texture.\n");
1155 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1156 ERR("Texture filtering not supported in direct blit.\n");
1158 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1159 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1161 ERR("Texture filtering not supported in direct blit\n");
1164 src_level = src_sub_resource_idx % src_texture->level_count;
1165 dst_level = dst_sub_resource_idx % dst_texture->level_count;
1167 src_height = wined3d_texture_get_level_height(src_texture, src_level);
1168 dst_target = wined3d_texture_get_sub_resource_target(dst_texture, dst_sub_resource_idx);
1169 if (upsidedown
1170 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1171 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1173 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
1174 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_target, dst_level,
1175 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
1176 src_rect->left, src_height - src_rect->bottom,
1177 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1179 else
1181 LONG row;
1182 UINT yoffset = src_height - src_rect->top + dst_rect.top - 1;
1183 /* I have to process this row by row to swap the image,
1184 * otherwise it would be upside down, so stretching in y direction
1185 * doesn't cost extra time
1187 * However, stretching in x direction can be avoided if not necessary
1189 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
1190 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1192 /* Well, that stuff works, but it's very slow.
1193 * find a better way instead
1195 LONG col;
1197 for (col = dst_rect.left; col < dst_rect.right; ++col)
1199 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_target, dst_level,
1200 dst_rect.left + col /* x offset */, row /* y offset */,
1201 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
1204 else
1206 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_target, dst_level,
1207 dst_rect.left /* x offset */, row /* y offset */,
1208 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
1212 checkGLcall("glCopyTexSubImage2D");
1214 context_release(context);
1216 /* The texture is now most up to date - If the surface is a render target
1217 * and has a drawable, this path is never entered. */
1218 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1219 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1222 /* Uses the hardware to stretch and flip the image */
1223 static void fb_copy_to_texture_hwstretch(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
1224 const RECT *dst_rect_in, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1225 const RECT *src_rect, enum wined3d_texture_filter_type filter)
1227 unsigned int src_width, src_height, src_pow2_width, src_pow2_height, src_level;
1228 struct wined3d_device *device = dst_texture->resource.device;
1229 GLenum src_target, dst_target, texture_target;
1230 GLuint src, backup = 0;
1231 float left, right, top, bottom; /* Texture coordinates */
1232 const struct wined3d_gl_info *gl_info;
1233 struct wined3d_context *context;
1234 GLenum drawBuffer = GL_BACK;
1235 GLenum offscreen_buffer;
1236 BOOL noBackBufferBackup;
1237 BOOL src_offscreen;
1238 BOOL upsidedown = FALSE;
1239 RECT dst_rect = *dst_rect_in;
1241 TRACE("Using hwstretch blit\n");
1243 src_target = wined3d_texture_get_sub_resource_target(src_texture, src_sub_resource_idx);
1244 dst_target = wined3d_texture_get_sub_resource_target(dst_texture, dst_sub_resource_idx);
1246 /* Activate the Proper context for reading from the source surface, set it up for blitting */
1247 context = context_acquire(device, src_texture, src_sub_resource_idx);
1248 gl_info = context->gl_info;
1249 context_apply_ffp_blit_state(context, device);
1250 wined3d_texture_load(dst_texture, context, FALSE);
1252 offscreen_buffer = context_get_offscreen_gl_buffer(context);
1253 src_level = src_sub_resource_idx % src_texture->level_count;
1254 src_width = wined3d_texture_get_level_width(src_texture, src_level);
1255 src_height = wined3d_texture_get_level_height(src_texture, src_level);
1256 src_pow2_width = wined3d_texture_get_level_pow2_width(src_texture, src_level);
1257 src_pow2_height = wined3d_texture_get_level_pow2_height(src_texture, src_level);
1259 src_offscreen = wined3d_resource_is_offscreen(&src_texture->resource);
1260 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
1261 if (!noBackBufferBackup && !src_texture->texture_rgb.name)
1263 /* Get it a description */
1264 wined3d_texture_load(src_texture, context, FALSE);
1267 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
1268 * This way we don't have to wait for the 2nd readback to finish to leave this function.
1270 if (context->aux_buffers >= 2)
1272 /* Got more than one aux buffer? Use the 2nd aux buffer */
1273 drawBuffer = GL_AUX1;
1275 else if ((!src_offscreen || offscreen_buffer == GL_BACK) && context->aux_buffers >= 1)
1277 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
1278 drawBuffer = GL_AUX0;
1281 if (noBackBufferBackup)
1283 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
1284 checkGLcall("glGenTextures");
1285 context_bind_texture(context, GL_TEXTURE_2D, backup);
1286 texture_target = GL_TEXTURE_2D;
1288 else
1290 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
1291 * we are reading from the back buffer, the backup can be used as source texture
1293 texture_target = src_target;
1294 context_bind_texture(context, texture_target, src_texture->texture_rgb.name);
1295 gl_info->gl_ops.gl.p_glEnable(texture_target);
1296 checkGLcall("glEnable(texture_target)");
1298 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
1299 src_texture->sub_resources[src_sub_resource_idx].locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
1302 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1303 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1305 if(dst_rect.top > dst_rect.bottom) {
1306 UINT tmp = dst_rect.bottom;
1307 dst_rect.bottom = dst_rect.top;
1308 dst_rect.top = tmp;
1309 upsidedown = TRUE;
1312 if (src_offscreen)
1314 TRACE("Reading from an offscreen target\n");
1315 upsidedown = !upsidedown;
1316 gl_info->gl_ops.gl.p_glReadBuffer(offscreen_buffer);
1318 else
1320 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
1323 /* TODO: Only back up the part that will be overwritten */
1324 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, src_width, src_height);
1326 checkGLcall("glCopyTexSubImage2D");
1328 /* No issue with overriding these - the sampler is dirty due to blit usage */
1329 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
1330 checkGLcall("glTexParameteri");
1331 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
1332 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
1333 checkGLcall("glTexParameteri");
1335 if (!src_texture->swapchain || src_texture == src_texture->swapchain->back_buffers[0])
1337 src = backup ? backup : src_texture->texture_rgb.name;
1339 else
1341 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
1342 checkGLcall("glReadBuffer(GL_FRONT)");
1344 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
1345 checkGLcall("glGenTextures(1, &src)");
1346 context_bind_texture(context, GL_TEXTURE_2D, src);
1348 /* TODO: Only copy the part that will be read. Use src_rect->left,
1349 * src_rect->bottom as origin, but with the width watch out for power
1350 * of 2 sizes. */
1351 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_pow2_width,
1352 src_pow2_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
1353 checkGLcall("glTexImage2D");
1354 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, src_width, src_height);
1356 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1357 checkGLcall("glTexParameteri");
1358 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1359 checkGLcall("glTexParameteri");
1361 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
1362 checkGLcall("glReadBuffer(GL_BACK)");
1364 if (texture_target != GL_TEXTURE_2D)
1366 gl_info->gl_ops.gl.p_glDisable(texture_target);
1367 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
1368 texture_target = GL_TEXTURE_2D;
1371 checkGLcall("glEnd and previous");
1373 left = src_rect->left;
1374 right = src_rect->right;
1376 if (!upsidedown)
1378 top = src_height - src_rect->top;
1379 bottom = src_height - src_rect->bottom;
1381 else
1383 top = src_height - src_rect->bottom;
1384 bottom = src_height - src_rect->top;
1387 if (src_texture->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
1389 left /= src_pow2_width;
1390 right /= src_pow2_width;
1391 top /= src_pow2_height;
1392 bottom /= src_pow2_height;
1395 /* draw the source texture stretched and upside down. The correct surface is bound already */
1396 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1397 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1399 context_set_draw_buffer(context, drawBuffer);
1400 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
1402 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
1403 /* bottom left */
1404 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
1405 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
1407 /* top left */
1408 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
1409 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
1411 /* top right */
1412 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
1413 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1415 /* bottom right */
1416 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
1417 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
1418 gl_info->gl_ops.gl.p_glEnd();
1419 checkGLcall("glEnd and previous");
1421 if (texture_target != dst_target)
1423 gl_info->gl_ops.gl.p_glDisable(texture_target);
1424 gl_info->gl_ops.gl.p_glEnable(dst_target);
1425 texture_target = dst_target;
1428 /* Now read the stretched and upside down image into the destination texture */
1429 context_bind_texture(context, texture_target, dst_texture->texture_rgb.name);
1430 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
1432 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
1433 0, 0, /* We blitted the image to the origin */
1434 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1435 checkGLcall("glCopyTexSubImage2D");
1437 if (drawBuffer == GL_BACK)
1439 /* Write the back buffer backup back. */
1440 if (backup)
1442 if (texture_target != GL_TEXTURE_2D)
1444 gl_info->gl_ops.gl.p_glDisable(texture_target);
1445 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
1446 texture_target = GL_TEXTURE_2D;
1448 context_bind_texture(context, GL_TEXTURE_2D, backup);
1450 else
1452 if (texture_target != src_target)
1454 gl_info->gl_ops.gl.p_glDisable(texture_target);
1455 gl_info->gl_ops.gl.p_glEnable(src_target);
1456 texture_target = src_target;
1458 context_bind_texture(context, src_target, src_texture->texture_rgb.name);
1461 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
1462 /* top left */
1463 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
1464 gl_info->gl_ops.gl.p_glVertex2i(0, src_height);
1466 /* bottom left */
1467 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)src_height / (float)src_pow2_height);
1468 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
1470 /* bottom right */
1471 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width,
1472 (float)src_height / (float)src_pow2_height);
1473 gl_info->gl_ops.gl.p_glVertex2i(src_width, 0);
1475 /* top right */
1476 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width, 0.0f);
1477 gl_info->gl_ops.gl.p_glVertex2i(src_width, src_height);
1478 gl_info->gl_ops.gl.p_glEnd();
1480 gl_info->gl_ops.gl.p_glDisable(texture_target);
1481 checkGLcall("glDisable(texture_target)");
1483 /* Cleanup */
1484 if (src != src_texture->texture_rgb.name && src != backup)
1486 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
1487 checkGLcall("glDeleteTextures(1, &src)");
1489 if (backup)
1491 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
1492 checkGLcall("glDeleteTextures(1, &backup)");
1495 context_release(context);
1497 /* The texture is now most up to date - If the surface is a render target
1498 * and has a drawable, this path is never entered. */
1499 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1500 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1503 static HRESULT wined3d_texture_blt_special(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
1504 const RECT *dst_rect, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1505 const RECT *src_rect, DWORD flags, const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
1507 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1508 const struct wined3d_rendertarget_view *rtv;
1510 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_rect %s, src_texture %p, "
1511 "src_sub_resource_idx %u, src_rect %s, flags %#x, fx %p, filter %s.\n",
1512 dst_texture, dst_sub_resource_idx, wine_dbgstr_rect(dst_rect), src_texture, src_sub_resource_idx,
1513 wine_dbgstr_rect(src_rect), flags, fx, debug_d3dtexturefiltertype(filter));
1515 if (dst_texture->resource.type != WINED3D_RTYPE_TEXTURE_2D)
1517 FIXME("Not implemented for %s resources.\n", debug_d3dresourcetype(dst_texture->resource.type));
1518 return WINED3DERR_INVALIDCALL;
1521 /* Get the swapchain. One of the surfaces has to be a primary surface. */
1522 if (!(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1524 WARN("Destination resource is not GPU accessible, rejecting GL blit.\n");
1525 return WINED3DERR_INVALIDCALL;
1528 if (!(src_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1530 WARN("Source resource is not GPU accessible, rejecting GL blit.\n");
1531 return WINED3DERR_INVALIDCALL;
1534 src_swapchain = src_texture->swapchain;
1535 dst_swapchain = dst_texture->swapchain;
1537 /* Early sort out of cases where no render target is used */
1538 if (!(rtv = dst_texture->resource.device->fb.render_targets[0]) || (!src_swapchain && !dst_swapchain
1539 && (&src_texture->resource != rtv->resource || src_sub_resource_idx != rtv->sub_resource_idx)
1540 && (&dst_texture->resource != rtv->resource || dst_sub_resource_idx != rtv->sub_resource_idx)))
1542 TRACE("No surface is render target, not using hardware blit.\n");
1543 return WINED3DERR_INVALIDCALL;
1546 /* No destination color keying supported */
1547 if (flags & (WINED3D_BLT_DST_CKEY | WINED3D_BLT_DST_CKEY_OVERRIDE))
1549 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
1550 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
1551 return WINED3DERR_INVALIDCALL;
1554 if (dst_swapchain && dst_swapchain == src_swapchain)
1556 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
1557 return WINED3DERR_INVALIDCALL;
1560 if (dst_swapchain && src_swapchain)
1562 FIXME("Implement hardware blit between two different swapchains\n");
1563 return WINED3DERR_INVALIDCALL;
1566 if (dst_swapchain)
1568 /* Handled with regular texture -> swapchain blit */
1569 if (&src_texture->resource == rtv->resource && src_sub_resource_idx == rtv->sub_resource_idx)
1570 TRACE("Blit from active render target to a swapchain\n");
1572 else if (src_swapchain && &dst_texture->resource == rtv->resource
1573 && dst_sub_resource_idx == rtv->sub_resource_idx)
1575 FIXME("Implement blit from a swapchain to the active render target\n");
1576 return WINED3DERR_INVALIDCALL;
1579 if (!dst_swapchain && (src_swapchain || (&src_texture->resource == rtv->resource
1580 && src_sub_resource_idx == rtv->sub_resource_idx)))
1582 unsigned int src_level, src_width, src_height;
1583 /* Blit from render target to texture */
1584 BOOL stretchx;
1586 /* P8 read back is not implemented */
1587 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT
1588 || dst_texture->resource.format->id == WINED3DFMT_P8_UINT)
1590 TRACE("P8 read back not supported by frame buffer to texture blit\n");
1591 return WINED3DERR_INVALIDCALL;
1594 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_SRC_CKEY_OVERRIDE))
1596 TRACE("Color keying not supported by frame buffer to texture blit\n");
1597 return WINED3DERR_INVALIDCALL;
1598 /* Destination color key is checked above */
1601 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
1602 stretchx = TRUE;
1603 else
1604 stretchx = FALSE;
1606 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
1607 * flip the image nor scale it.
1609 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
1610 * -> If the app wants an image width an unscaled width, copy it line per line
1611 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
1612 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
1613 * back buffer. This is slower than reading line per line, thus not used for flipping
1614 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
1615 * pixel by pixel. */
1616 src_level = src_sub_resource_idx % src_texture->level_count;
1617 src_width = wined3d_texture_get_level_width(src_texture, src_level);
1618 src_height = wined3d_texture_get_level_height(src_texture, src_level);
1619 if (!stretchx || dst_rect->right - dst_rect->left > src_width
1620 || dst_rect->bottom - dst_rect->top > src_height)
1622 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
1623 fb_copy_to_texture_direct(dst_texture, dst_sub_resource_idx, dst_rect,
1624 src_texture, src_sub_resource_idx, src_rect, filter);
1626 else
1628 TRACE("Using hardware stretching to flip / stretch the texture.\n");
1629 fb_copy_to_texture_hwstretch(dst_texture, dst_sub_resource_idx, dst_rect,
1630 src_texture, src_sub_resource_idx, src_rect, filter);
1633 return WINED3D_OK;
1636 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
1637 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
1638 return WINED3DERR_INVALIDCALL;
1641 /* Context activation is done by the caller. */
1642 BOOL texture2d_load_sysmem(struct wined3d_texture *texture, unsigned int sub_resource_idx,
1643 struct wined3d_context *context, DWORD dst_location)
1645 struct wined3d_texture_sub_resource *sub_resource;
1647 sub_resource = &texture->sub_resources[sub_resource_idx];
1648 wined3d_texture_prepare_location(texture, sub_resource_idx, context, dst_location);
1650 /* We cannot download data from multisample textures directly. */
1651 if (is_multisample_location(texture, WINED3D_LOCATION_TEXTURE_RGB))
1653 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_RB_RESOLVED);
1654 texture2d_read_from_framebuffer(texture, sub_resource_idx, context,
1655 WINED3D_LOCATION_RB_RESOLVED, dst_location);
1656 return TRUE;
1659 if (sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
1660 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
1662 /* Download the sub-resource to system memory. */
1663 if (sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
1665 wined3d_texture_bind_and_dirtify(texture, context,
1666 !(sub_resource->locations & WINED3D_LOCATION_TEXTURE_RGB));
1667 texture2d_download_data(texture, sub_resource_idx, context, dst_location);
1668 ++texture->download_count;
1669 return TRUE;
1672 if (!(texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
1673 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
1675 texture2d_read_from_framebuffer(texture, sub_resource_idx, context,
1676 texture->resource.draw_binding, dst_location);
1677 return TRUE;
1680 FIXME("Can't load texture %p, %u with location flags %s into sysmem.\n",
1681 texture, sub_resource_idx, wined3d_debug_location(sub_resource->locations));
1682 return FALSE;
1685 /* Context activation is done by the caller. */
1686 BOOL texture2d_load_drawable(struct wined3d_texture *texture,
1687 unsigned int sub_resource_idx, struct wined3d_context *context)
1689 struct wined3d_texture *restore_texture;
1690 struct wined3d_device *device;
1691 unsigned int restore_idx;
1692 unsigned int level;
1693 RECT r;
1695 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
1697 DWORD current = texture->sub_resources[sub_resource_idx].locations;
1698 FIXME("Unimplemented copy from %s for depth/stencil buffers.\n",
1699 wined3d_debug_location(current));
1700 return FALSE;
1703 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
1704 && wined3d_resource_is_offscreen(&texture->resource))
1706 ERR("Trying to load offscreen texture into WINED3D_LOCATION_DRAWABLE.\n");
1707 return FALSE;
1710 device = texture->resource.device;
1711 restore_texture = context->current_rt.texture;
1712 restore_idx = context->current_rt.sub_resource_idx;
1713 if (restore_texture != texture || restore_idx != sub_resource_idx)
1714 context = context_acquire(device, texture, sub_resource_idx);
1715 else
1716 restore_texture = NULL;
1718 level = sub_resource_idx % texture->level_count;
1719 SetRect(&r, 0, 0, wined3d_texture_get_level_width(texture, level),
1720 wined3d_texture_get_level_height(texture, level));
1721 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
1722 device->blitter->ops->blitter_blit(device->blitter, WINED3D_BLIT_OP_COLOR_BLIT, context,
1723 texture, sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB, &r,
1724 texture, sub_resource_idx, WINED3D_LOCATION_DRAWABLE, &r,
1725 NULL, WINED3D_TEXF_POINT);
1727 if (restore_texture)
1728 context_restore(context, restore_texture, restore_idx);
1730 return TRUE;
1733 BOOL texture2d_load_texture(struct wined3d_texture *texture, unsigned int sub_resource_idx,
1734 struct wined3d_context *context, BOOL srgb)
1736 unsigned int width, height, level, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch;
1737 const struct wined3d_gl_info *gl_info = context->gl_info;
1738 struct wined3d_device *device = texture->resource.device;
1739 const struct wined3d_color_key_conversion *conversion;
1740 struct wined3d_texture_sub_resource *sub_resource;
1741 const struct wined3d_format *format;
1742 struct wined3d_bo_address data;
1743 BYTE *src_mem, *dst_mem = NULL;
1744 struct wined3d_box src_box;
1745 BOOL depth;
1747 depth = texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL;
1748 sub_resource = &texture->sub_resources[sub_resource_idx];
1750 if (!depth && wined3d_settings.offscreen_rendering_mode != ORM_FBO
1751 && wined3d_resource_is_offscreen(&texture->resource)
1752 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
1754 texture2d_load_fb_texture(texture, sub_resource_idx, srgb, context);
1756 return TRUE;
1759 level = sub_resource_idx % texture->level_count;
1760 width = wined3d_texture_get_level_width(texture, level);
1761 height = wined3d_texture_get_level_height(texture, level);
1762 wined3d_box_set(&src_box, 0, 0, width, height, 0, 1);
1764 if (!depth && sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
1765 && (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
1766 && fbo_blitter_supported(WINED3D_BLIT_OP_COLOR_BLIT, gl_info,
1767 &texture->resource, WINED3D_LOCATION_TEXTURE_RGB,
1768 &texture->resource, WINED3D_LOCATION_TEXTURE_SRGB))
1770 RECT src_rect;
1772 SetRect(&src_rect, 0, 0, width, height);
1773 if (srgb)
1774 texture2d_blt_fbo(device, context, WINED3D_TEXF_POINT,
1775 texture, sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB, &src_rect,
1776 texture, sub_resource_idx, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
1777 else
1778 texture2d_blt_fbo(device, context, WINED3D_TEXF_POINT,
1779 texture, sub_resource_idx, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect,
1780 texture, sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
1782 return TRUE;
1785 if (!depth && sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
1786 && (!srgb || (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)))
1788 DWORD src_location = sub_resource->locations & WINED3D_LOCATION_RB_RESOLVED ?
1789 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
1790 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
1791 RECT src_rect;
1793 SetRect(&src_rect, 0, 0, width, height);
1794 if (fbo_blitter_supported(WINED3D_BLIT_OP_COLOR_BLIT, gl_info,
1795 &texture->resource, src_location, &texture->resource, dst_location))
1796 texture2d_blt_fbo(device, context, WINED3D_TEXF_POINT, texture, sub_resource_idx,
1797 src_location, &src_rect, texture, sub_resource_idx, dst_location, &src_rect);
1799 return TRUE;
1802 /* Upload from system memory */
1804 if (srgb)
1806 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | texture->resource.map_binding))
1807 == WINED3D_LOCATION_TEXTURE_RGB)
1809 FIXME_(d3d_perf)("Downloading RGB texture %p, %u to reload it as sRGB.\n", texture, sub_resource_idx);
1810 wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding);
1813 else
1815 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | texture->resource.map_binding))
1816 == WINED3D_LOCATION_TEXTURE_SRGB)
1818 FIXME_(d3d_perf)("Downloading sRGB texture %p, %u to reload it as RGB.\n", texture, sub_resource_idx);
1819 wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding);
1823 if (!(sub_resource->locations & surface_simple_locations))
1825 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
1826 /* Lets hope we get it from somewhere... */
1827 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM);
1830 wined3d_texture_prepare_texture(texture, context, srgb);
1831 wined3d_texture_bind_and_dirtify(texture, context, srgb);
1832 wined3d_texture_get_pitch(texture, level, &src_row_pitch, &src_slice_pitch);
1834 format = texture->resource.format;
1835 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
1836 format = wined3d_get_format(gl_info, conversion->dst_format, texture->resource.usage);
1838 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
1839 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
1840 * getting called. */
1841 if (conversion && sub_resource->buffer_object)
1843 TRACE("Removing the pbo attached to texture %p, %u.\n", texture, sub_resource_idx);
1845 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM);
1846 wined3d_texture_set_map_binding(texture, WINED3D_LOCATION_SYSMEM);
1849 wined3d_texture_get_memory(texture, sub_resource_idx, &data, sub_resource->locations);
1850 if (conversion)
1852 wined3d_format_calculate_pitch(format, device->surface_alignment,
1853 width, height, &dst_row_pitch, &dst_slice_pitch);
1855 src_mem = context_map_bo_address(context, &data, src_slice_pitch,
1856 GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ);
1857 if (!(dst_mem = heap_alloc(dst_slice_pitch)))
1859 ERR("Out of memory (%u).\n", dst_slice_pitch);
1860 context_release(context);
1861 return FALSE;
1863 conversion->convert(src_mem, src_row_pitch, dst_mem, dst_row_pitch,
1864 width, height, &texture->async.gl_color_key);
1865 src_row_pitch = dst_row_pitch;
1866 src_slice_pitch = dst_slice_pitch;
1867 context_unmap_bo_address(context, &data, GL_PIXEL_UNPACK_BUFFER);
1869 data.buffer_object = 0;
1870 data.addr = dst_mem;
1873 wined3d_texture_upload_data(texture, sub_resource_idx, context, format, &src_box,
1874 wined3d_const_bo_address(&data), src_row_pitch, src_slice_pitch, 0, 0, 0, srgb);
1876 heap_free(dst_mem);
1878 return TRUE;
1881 /* Context activation is done by the caller. */
1882 BOOL texture2d_load_renderbuffer(struct wined3d_texture *texture, unsigned int sub_resource_idx,
1883 struct wined3d_context *context, DWORD dst_location)
1885 unsigned int level = sub_resource_idx % texture->level_count;
1886 const RECT rect = {0, 0,
1887 wined3d_texture_get_level_width(texture, level),
1888 wined3d_texture_get_level_height(texture, level)};
1889 struct wined3d_texture_sub_resource *sub_resource;
1890 DWORD src_location, locations;
1892 sub_resource = &texture->sub_resources[sub_resource_idx];
1893 locations = sub_resource->locations;
1894 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
1896 FIXME("Unimplemented copy from %s for depth/stencil buffers.\n",
1897 wined3d_debug_location(locations));
1898 return FALSE;
1901 if (locations & WINED3D_LOCATION_RB_MULTISAMPLE)
1902 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
1903 else if (locations & WINED3D_LOCATION_RB_RESOLVED)
1904 src_location = WINED3D_LOCATION_RB_RESOLVED;
1905 else if (locations & WINED3D_LOCATION_TEXTURE_SRGB)
1906 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
1907 else if (locations & WINED3D_LOCATION_TEXTURE_RGB)
1908 src_location = WINED3D_LOCATION_TEXTURE_RGB;
1909 else if (locations & WINED3D_LOCATION_DRAWABLE)
1910 src_location = WINED3D_LOCATION_DRAWABLE;
1911 else /* texture2d_blt_fbo() will load the source location if necessary. */
1912 src_location = WINED3D_LOCATION_TEXTURE_RGB;
1914 texture2d_blt_fbo(texture->resource.device, context, WINED3D_TEXF_POINT, texture,
1915 sub_resource_idx, src_location, &rect, texture, sub_resource_idx, dst_location, &rect);
1917 return TRUE;
1920 /* Context activation is done by the caller. */
1921 static void fbo_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
1923 struct wined3d_blitter *next;
1925 if ((next = blitter->next))
1926 next->ops->blitter_destroy(next, context);
1928 heap_free(blitter);
1931 static void fbo_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
1932 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
1933 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
1935 struct wined3d_blitter *next;
1937 if ((next = blitter->next))
1938 next->ops->blitter_clear(next, device, rt_count, fb, rect_count,
1939 clear_rects, draw_rect, flags, colour, depth, stencil);
1942 static DWORD fbo_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
1943 struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1944 DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture,
1945 unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect,
1946 const struct wined3d_color_key *colour_key, enum wined3d_texture_filter_type filter)
1948 struct wined3d_resource *src_resource, *dst_resource;
1949 enum wined3d_blit_op blit_op = op;
1950 struct wined3d_device *device;
1951 struct wined3d_blitter *next;
1953 TRACE("blitter %p, op %#x, context %p, src_texture %p, src_sub_resource_idx %u, src_location %s, src_rect %s, "
1954 "dst_texture %p, dst_sub_resource_idx %u, dst_location %s, dst_rect %s, colour_key %p, filter %s.\n",
1955 blitter, op, context, src_texture, src_sub_resource_idx, wined3d_debug_location(src_location),
1956 wine_dbgstr_rect(src_rect), dst_texture, dst_sub_resource_idx, wined3d_debug_location(dst_location),
1957 wine_dbgstr_rect(dst_rect), colour_key, debug_d3dtexturefiltertype(filter));
1959 src_resource = &src_texture->resource;
1960 dst_resource = &dst_texture->resource;
1962 device = dst_resource->device;
1964 if (blit_op == WINED3D_BLIT_OP_RAW_BLIT && dst_resource->format->id == src_resource->format->id)
1966 if (dst_resource->format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
1967 blit_op = WINED3D_BLIT_OP_DEPTH_BLIT;
1968 else
1969 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
1972 if (!fbo_blitter_supported(blit_op, context->gl_info,
1973 src_resource, src_location, dst_resource, dst_location))
1975 if (!(next = blitter->next))
1977 ERR("No blitter to handle blit op %#x.\n", op);
1978 return dst_location;
1981 TRACE("Forwarding to blitter %p.\n", next);
1982 return next->ops->blitter_blit(next, op, context, src_texture, src_sub_resource_idx, src_location,
1983 src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect, colour_key, filter);
1986 if (blit_op == WINED3D_BLIT_OP_COLOR_BLIT)
1988 TRACE("Colour blit.\n");
1989 texture2d_blt_fbo(device, context, filter, src_texture, src_sub_resource_idx, src_location,
1990 src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect);
1991 return dst_location;
1994 if (blit_op == WINED3D_BLIT_OP_DEPTH_BLIT)
1996 TRACE("Depth/stencil blit.\n");
1997 texture2d_depth_blt_fbo(device, context, src_texture, src_sub_resource_idx, src_location,
1998 src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect);
1999 return dst_location;
2002 ERR("This blitter does not implement blit op %#x.\n", blit_op);
2003 return dst_location;
2006 static const struct wined3d_blitter_ops fbo_blitter_ops =
2008 fbo_blitter_destroy,
2009 fbo_blitter_clear,
2010 fbo_blitter_blit,
2013 void wined3d_fbo_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
2015 struct wined3d_blitter *blitter;
2017 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
2018 return;
2020 if (!(blitter = heap_alloc(sizeof(*blitter))))
2021 return;
2023 TRACE("Created blitter %p.\n", blitter);
2025 blitter->ops = &fbo_blitter_ops;
2026 blitter->next = *next;
2027 *next = blitter;
2030 /* Context activation is done by the caller. */
2031 static void raw_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2033 struct wined3d_blitter *next;
2035 if ((next = blitter->next))
2036 next->ops->blitter_destroy(next, context);
2038 heap_free(blitter);
2041 /* Context activation is done by the caller. */
2042 static void raw_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
2043 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
2044 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
2046 struct wined3d_blitter *next;
2048 if (!(next = blitter->next))
2050 ERR("No blitter to handle clear.\n");
2051 return;
2054 TRACE("Forwarding to blitter %p.\n", next);
2055 next->ops->blitter_clear(next, device, rt_count, fb, rect_count,
2056 clear_rects, draw_rect, flags, colour, depth, stencil);
2059 /* Context activation is done by the caller. */
2060 static DWORD raw_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
2061 struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
2062 DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture,
2063 unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect,
2064 const struct wined3d_color_key *colour_key, enum wined3d_texture_filter_type filter)
2066 const struct wined3d_gl_info *gl_info = context->gl_info;
2067 unsigned int src_level, src_layer, dst_level, dst_layer;
2068 struct wined3d_blitter *next;
2069 GLuint src_name, dst_name;
2070 DWORD location;
2072 /* If we would need to copy from a renderbuffer or drawable, we'd probably
2073 * be better of using the FBO blitter directly, since we'd need to use it
2074 * to copy the resource contents to the texture anyway. */
2075 if (op != WINED3D_BLIT_OP_RAW_BLIT
2076 || (src_texture->resource.format->id == dst_texture->resource.format->id
2077 && (!(src_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
2078 || !(dst_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB)))))
2080 if (!(next = blitter->next))
2082 ERR("No blitter to handle blit op %#x.\n", op);
2083 return dst_location;
2086 TRACE("Forwarding to blitter %p.\n", next);
2087 return next->ops->blitter_blit(next, op, context, src_texture, src_sub_resource_idx, src_location,
2088 src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect, colour_key, filter);
2091 TRACE("Blit using ARB_copy_image.\n");
2093 src_level = src_sub_resource_idx % src_texture->level_count;
2094 src_layer = src_sub_resource_idx / src_texture->level_count;
2096 dst_level = dst_sub_resource_idx % dst_texture->level_count;
2097 dst_layer = dst_sub_resource_idx / dst_texture->level_count;
2099 location = src_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB);
2100 if (!location)
2101 location = src_texture->flags & WINED3D_TEXTURE_IS_SRGB
2102 ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2103 if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, location))
2104 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(location));
2105 src_name = wined3d_texture_get_texture_name(src_texture, context, location == WINED3D_LOCATION_TEXTURE_SRGB);
2107 location = dst_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB);
2108 if (!location)
2109 location = dst_texture->flags & WINED3D_TEXTURE_IS_SRGB
2110 ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2111 if (texture2d_is_full_rect(dst_texture, dst_level, dst_rect))
2113 if (!wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, location))
2114 ERR("Failed to prepare the destination sub-resource into %s.\n", wined3d_debug_location(location));
2116 else
2118 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, location))
2119 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(location));
2121 dst_name = wined3d_texture_get_texture_name(dst_texture, context, location == WINED3D_LOCATION_TEXTURE_SRGB);
2123 GL_EXTCALL(glCopyImageSubData(src_name, src_texture->target, src_level,
2124 src_rect->left, src_rect->top, src_layer, dst_name, dst_texture->target, dst_level,
2125 dst_rect->left, dst_rect->top, dst_layer, src_rect->right - src_rect->left,
2126 src_rect->bottom - src_rect->top, 1));
2127 checkGLcall("copy image data");
2129 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, location);
2130 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~location);
2131 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location))
2132 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(dst_location));
2134 return dst_location | location;
2137 static const struct wined3d_blitter_ops raw_blitter_ops =
2139 raw_blitter_destroy,
2140 raw_blitter_clear,
2141 raw_blitter_blit,
2144 void wined3d_raw_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
2146 struct wined3d_blitter *blitter;
2148 if (!gl_info->supported[ARB_COPY_IMAGE])
2149 return;
2151 if (!(blitter = heap_alloc(sizeof(*blitter))))
2152 return;
2154 TRACE("Created blitter %p.\n", blitter);
2156 blitter->ops = &raw_blitter_ops;
2157 blitter->next = *next;
2158 *next = blitter;
2161 /* Context activation is done by the caller. */
2162 static void ffp_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2164 struct wined3d_blitter *next;
2166 if ((next = blitter->next))
2167 next->ops->blitter_destroy(next, context);
2169 heap_free(blitter);
2172 static BOOL ffp_blit_supported(enum wined3d_blit_op blit_op, const struct wined3d_context *context,
2173 const struct wined3d_resource *src_resource, DWORD src_location,
2174 const struct wined3d_resource *dst_resource, DWORD dst_location)
2176 const struct wined3d_format *src_format = src_resource->format;
2177 const struct wined3d_format *dst_format = dst_resource->format;
2178 BOOL decompress;
2180 if (src_resource->type != WINED3D_RTYPE_TEXTURE_2D)
2181 return FALSE;
2183 decompress = src_format && (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
2184 && !(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED);
2185 if (!decompress && !(src_resource->access & dst_resource->access & WINED3D_RESOURCE_ACCESS_GPU))
2187 TRACE("Source or destination resource is not GPU accessible.\n");
2188 return FALSE;
2191 if (blit_op == WINED3D_BLIT_OP_RAW_BLIT && dst_format->id == src_format->id)
2193 if (dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
2194 blit_op = WINED3D_BLIT_OP_DEPTH_BLIT;
2195 else
2196 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
2199 switch (blit_op)
2201 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
2202 if (context->d3d_info->shader_color_key)
2204 TRACE("Color keying requires converted textures.\n");
2205 return FALSE;
2207 case WINED3D_BLIT_OP_COLOR_BLIT:
2208 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
2209 if (!context->gl_info->supported[WINED3D_GL_LEGACY_CONTEXT])
2210 return FALSE;
2212 if (TRACE_ON(d3d))
2214 TRACE("Checking support for fixup:\n");
2215 dump_color_fixup_desc(src_format->color_fixup);
2218 /* We only support identity conversions. */
2219 if (!is_identity_fixup(src_format->color_fixup)
2220 || !is_identity_fixup(dst_format->color_fixup))
2222 if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER
2223 && dst_format->id == src_format->id && dst_location == WINED3D_LOCATION_DRAWABLE)
2225 WARN("Claiming fixup support because of ORM_BACKBUFFER.\n");
2227 else
2229 TRACE("Fixups are not supported.\n");
2230 return FALSE;
2234 if (!(dst_resource->usage & WINED3DUSAGE_RENDERTARGET))
2236 TRACE("Can only blit to render targets.\n");
2237 return FALSE;
2239 return TRUE;
2241 default:
2242 TRACE("Unsupported blit operation %#x.\n", blit_op);
2243 return FALSE;
2247 static BOOL ffp_blitter_use_cpu_clear(struct wined3d_rendertarget_view *view)
2249 struct wined3d_resource *resource;
2250 struct wined3d_texture *texture;
2251 DWORD locations;
2253 resource = view->resource;
2254 if (resource->type == WINED3D_RTYPE_BUFFER)
2255 return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU);
2257 texture = texture_from_resource(resource);
2258 locations = texture->sub_resources[view->sub_resource_idx].locations;
2259 if (locations & (resource->map_binding | WINED3D_LOCATION_DISCARDED))
2260 return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU)
2261 || (texture->flags & WINED3D_TEXTURE_PIN_SYSMEM);
2263 return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU)
2264 && !(texture->flags & WINED3D_TEXTURE_CONVERTED);
2267 static void ffp_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
2268 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
2269 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
2271 struct wined3d_rendertarget_view *view;
2272 struct wined3d_blitter *next;
2273 DWORD next_flags = 0;
2274 unsigned int i;
2276 if (flags & WINED3DCLEAR_TARGET)
2278 for (i = 0; i < rt_count; ++i)
2280 if (!(view = fb->render_targets[i]))
2281 continue;
2283 if (ffp_blitter_use_cpu_clear(view)
2284 || (!(view->resource->usage & WINED3DUSAGE_RENDERTARGET)
2285 && (wined3d_settings.offscreen_rendering_mode != ORM_FBO
2286 || !(view->format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE))))
2288 next_flags |= WINED3DCLEAR_TARGET;
2289 flags &= ~WINED3DCLEAR_TARGET;
2290 break;
2293 /* FIXME: We should reject colour fills on formats with fixups,
2294 * but this would break P8 colour fills for example. */
2298 if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil)
2299 && (!view->format->depth_size || (flags & WINED3DCLEAR_ZBUFFER))
2300 && (!view->format->stencil_size || (flags & WINED3DCLEAR_STENCIL))
2301 && ffp_blitter_use_cpu_clear(view))
2303 next_flags |= flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL);
2304 flags &= ~(WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL);
2307 if (flags)
2308 device_clear_render_targets(device, rt_count, fb, rect_count,
2309 clear_rects, draw_rect, flags, colour, depth, stencil);
2311 if (next_flags && (next = blitter->next))
2312 next->ops->blitter_clear(next, device, rt_count, fb, rect_count,
2313 clear_rects, draw_rect, next_flags, colour, depth, stencil);
2316 static DWORD ffp_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
2317 struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
2318 DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture,
2319 unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect,
2320 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter)
2322 const struct wined3d_gl_info *gl_info = context->gl_info;
2323 struct wined3d_resource *src_resource, *dst_resource;
2324 struct wined3d_texture *staging_texture = NULL;
2325 struct wined3d_color_key old_blt_key;
2326 struct wined3d_device *device;
2327 struct wined3d_blitter *next;
2328 DWORD old_color_key_flags;
2329 RECT r;
2331 src_resource = &src_texture->resource;
2332 dst_resource = &dst_texture->resource;
2333 device = dst_resource->device;
2335 if (!ffp_blit_supported(op, context, src_resource, src_location, dst_resource, dst_location))
2337 if ((next = blitter->next))
2338 return next->ops->blitter_blit(next, op, context, src_texture, src_sub_resource_idx, src_location,
2339 src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect, color_key, filter);
2342 TRACE("Blt from texture %p, %u to rendertarget %p, %u.\n",
2343 src_texture, src_sub_resource_idx, dst_texture, dst_sub_resource_idx);
2345 old_blt_key = src_texture->async.src_blt_color_key;
2346 old_color_key_flags = src_texture->async.color_key_flags;
2347 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT, color_key);
2349 if (!(src_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
2351 struct wined3d_resource_desc desc;
2352 struct wined3d_box upload_box;
2353 unsigned int src_level;
2354 HRESULT hr;
2356 TRACE("Source texture is not GPU accessible, creating a staging texture.\n");
2358 src_level = src_sub_resource_idx % src_texture->level_count;
2359 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
2360 desc.format = src_texture->resource.format->id;
2361 desc.multisample_type = src_texture->resource.multisample_type;
2362 desc.multisample_quality = src_texture->resource.multisample_quality;
2363 desc.usage = WINED3DUSAGE_PRIVATE;
2364 desc.access = WINED3D_RESOURCE_ACCESS_GPU;
2365 desc.width = wined3d_texture_get_level_width(src_texture, src_level);
2366 desc.height = wined3d_texture_get_level_height(src_texture, src_level);
2367 desc.depth = 1;
2368 desc.size = 0;
2370 if (FAILED(hr = wined3d_texture_create(device, &desc, 1, 1, 0,
2371 NULL, NULL, &wined3d_null_parent_ops, &staging_texture)))
2373 ERR("Failed to create staging texture, hr %#x.\n", hr);
2374 return dst_location;
2377 wined3d_box_set(&upload_box, 0, 0, desc.width, desc.height, 0, desc.depth);
2378 wined3d_texture_upload_from_texture(staging_texture, 0, 0, 0, 0,
2379 src_texture, src_sub_resource_idx, &upload_box);
2381 src_texture = staging_texture;
2382 src_sub_resource_idx = 0;
2384 else
2386 /* Make sure the surface is up-to-date. This should probably use
2387 * surface_load_location() and worry about the destination surface
2388 * too, unless we're overwriting it completely. */
2389 wined3d_texture_load(src_texture, context, FALSE);
2392 context_apply_ffp_blit_state(context, device);
2394 if (dst_location == WINED3D_LOCATION_DRAWABLE)
2396 r = *dst_rect;
2397 wined3d_texture_translate_drawable_coords(dst_texture, context->win_handle, &r);
2398 dst_rect = &r;
2401 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
2403 GLenum buffer;
2405 if (dst_location == WINED3D_LOCATION_DRAWABLE)
2407 TRACE("Destination texture %p is onscreen.\n", dst_texture);
2408 buffer = wined3d_texture_get_gl_buffer(dst_texture);
2410 else
2412 TRACE("Destination texture %p is offscreen.\n", dst_texture);
2413 buffer = GL_COLOR_ATTACHMENT0;
2415 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER,
2416 dst_resource, dst_sub_resource_idx, NULL, 0, dst_location);
2417 context_set_draw_buffer(context, buffer);
2418 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
2419 context_invalidate_state(context, STATE_FRAMEBUFFER);
2422 gl_info->gl_ops.gl.p_glEnable(src_texture->target);
2423 checkGLcall("glEnable(target)");
2425 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST || color_key)
2427 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
2428 checkGLcall("glEnable(GL_ALPHA_TEST)");
2431 if (color_key)
2433 /* For P8 surfaces, the alpha component contains the palette index.
2434 * Which means that the colorkey is one of the palette entries. In
2435 * other cases pixels that should be masked away have alpha set to 0. */
2436 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT)
2437 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
2438 (float)src_texture->async.src_blt_color_key.color_space_low_value / 255.0f);
2439 else
2440 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
2441 checkGLcall("glAlphaFunc");
2444 context_draw_textured_quad(context, src_texture, src_sub_resource_idx, src_rect, dst_rect, filter);
2446 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST || color_key)
2448 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2449 checkGLcall("glDisable(GL_ALPHA_TEST)");
2452 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
2453 checkGLcall("glDisable(GL_TEXTURE_2D)");
2454 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
2456 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
2457 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
2459 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
2461 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
2462 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
2465 if (dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture)
2466 gl_info->gl_ops.gl.p_glFlush();
2468 /* Restore the color key parameters */
2469 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT,
2470 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
2472 if (staging_texture)
2473 wined3d_texture_decref(staging_texture);
2475 return dst_location;
2478 static const struct wined3d_blitter_ops ffp_blitter_ops =
2480 ffp_blitter_destroy,
2481 ffp_blitter_clear,
2482 ffp_blitter_blit,
2485 void wined3d_ffp_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
2487 struct wined3d_blitter *blitter;
2489 if (!(blitter = heap_alloc(sizeof(*blitter))))
2490 return;
2492 TRACE("Created blitter %p.\n", blitter);
2494 blitter->ops = &ffp_blitter_ops;
2495 blitter->next = *next;
2496 *next = blitter;
2499 /* Context activation is done by the caller. */
2500 static void cpu_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2502 struct wined3d_blitter *next;
2504 if ((next = blitter->next))
2505 next->ops->blitter_destroy(next, context);
2507 heap_free(blitter);
2510 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
2511 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
2512 const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx)
2514 UINT row_block_count;
2515 const BYTE *src_row;
2516 BYTE *dst_row;
2517 UINT x, y;
2519 src_row = src_data;
2520 dst_row = dst_data;
2522 row_block_count = (update_w + format->block_width - 1) / format->block_width;
2524 if (!flags)
2526 for (y = 0; y < update_h; y += format->block_height)
2528 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
2529 src_row += src_pitch;
2530 dst_row += dst_pitch;
2533 return WINED3D_OK;
2536 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
2538 src_row += (((update_h / format->block_height) - 1) * src_pitch);
2540 switch (format->id)
2542 case WINED3DFMT_DXT1:
2543 for (y = 0; y < update_h; y += format->block_height)
2545 struct block
2547 WORD color[2];
2548 BYTE control_row[4];
2551 const struct block *s = (const struct block *)src_row;
2552 struct block *d = (struct block *)dst_row;
2554 for (x = 0; x < row_block_count; ++x)
2556 d[x].color[0] = s[x].color[0];
2557 d[x].color[1] = s[x].color[1];
2558 d[x].control_row[0] = s[x].control_row[3];
2559 d[x].control_row[1] = s[x].control_row[2];
2560 d[x].control_row[2] = s[x].control_row[1];
2561 d[x].control_row[3] = s[x].control_row[0];
2563 src_row -= src_pitch;
2564 dst_row += dst_pitch;
2566 return WINED3D_OK;
2568 case WINED3DFMT_DXT2:
2569 case WINED3DFMT_DXT3:
2570 for (y = 0; y < update_h; y += format->block_height)
2572 struct block
2574 WORD alpha_row[4];
2575 WORD color[2];
2576 BYTE control_row[4];
2579 const struct block *s = (const struct block *)src_row;
2580 struct block *d = (struct block *)dst_row;
2582 for (x = 0; x < row_block_count; ++x)
2584 d[x].alpha_row[0] = s[x].alpha_row[3];
2585 d[x].alpha_row[1] = s[x].alpha_row[2];
2586 d[x].alpha_row[2] = s[x].alpha_row[1];
2587 d[x].alpha_row[3] = s[x].alpha_row[0];
2588 d[x].color[0] = s[x].color[0];
2589 d[x].color[1] = s[x].color[1];
2590 d[x].control_row[0] = s[x].control_row[3];
2591 d[x].control_row[1] = s[x].control_row[2];
2592 d[x].control_row[2] = s[x].control_row[1];
2593 d[x].control_row[3] = s[x].control_row[0];
2595 src_row -= src_pitch;
2596 dst_row += dst_pitch;
2598 return WINED3D_OK;
2600 default:
2601 FIXME("Compressed flip not implemented for format %s.\n",
2602 debug_d3dformat(format->id));
2603 return E_NOTIMPL;
2607 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
2608 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
2610 return E_NOTIMPL;
2613 static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
2614 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
2615 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
2616 enum wined3d_texture_filter_type filter)
2618 unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count;
2619 struct wined3d_device *device = dst_texture->resource.device;
2620 const struct wined3d_format *src_format, *dst_format;
2621 struct wined3d_texture *converted_texture = NULL;
2622 struct wined3d_bo_address src_data, dst_data;
2623 unsigned int src_fmt_flags, dst_fmt_flags;
2624 struct wined3d_map_desc dst_map, src_map;
2625 struct wined3d_context *context = NULL;
2626 unsigned int x, sx, xinc, y, sy, yinc;
2627 unsigned int texture_level;
2628 HRESULT hr = WINED3D_OK;
2629 BOOL same_sub_resource;
2630 DWORD map_binding;
2631 const BYTE *sbase;
2632 const BYTE *sbuf;
2633 BYTE *dbuf;
2635 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
2636 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
2637 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture,
2638 src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
2640 if (device->d3d_initialized)
2641 context = context_acquire(device, NULL, 0);
2643 if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx)
2645 same_sub_resource = TRUE;
2647 map_binding = dst_texture->resource.map_binding;
2648 texture_level = dst_sub_resource_idx % dst_texture->level_count;
2649 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
2650 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
2651 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
2652 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
2653 wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
2654 dst_map.data = context_map_bo_address(context, &dst_data,
2655 dst_texture->sub_resources[dst_sub_resource_idx].size,
2656 GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ | WINED3D_MAP_WRITE);
2658 src_map = dst_map;
2659 src_format = dst_texture->resource.format;
2660 dst_format = src_format;
2661 dst_fmt_flags = dst_texture->resource.format_flags;
2662 src_fmt_flags = dst_fmt_flags;
2664 else
2666 same_sub_resource = FALSE;
2667 dst_format = dst_texture->resource.format;
2668 dst_fmt_flags = dst_texture->resource.format_flags;
2669 if (!(flags & WINED3D_BLT_RAW) && dst_texture->resource.format->id != src_texture->resource.format->id)
2671 if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format)))
2673 FIXME("Cannot convert %s to %s.\n", debug_d3dformat(src_texture->resource.format->id),
2674 debug_d3dformat(dst_texture->resource.format->id));
2675 if (context)
2676 context_release(context);
2677 return WINED3DERR_NOTAVAILABLE;
2679 src_texture = converted_texture;
2680 src_sub_resource_idx = 0;
2682 src_format = src_texture->resource.format;
2683 src_fmt_flags = src_texture->resource.format_flags;
2685 map_binding = src_texture->resource.map_binding;
2686 texture_level = src_sub_resource_idx % src_texture->level_count;
2687 if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, map_binding))
2688 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
2689 wined3d_texture_get_pitch(src_texture, texture_level, &src_map.row_pitch, &src_map.slice_pitch);
2690 wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &src_data, map_binding);
2691 src_map.data = context_map_bo_address(context, &src_data,
2692 src_texture->sub_resources[src_sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ);
2694 map_binding = dst_texture->resource.map_binding;
2695 texture_level = dst_sub_resource_idx % dst_texture->level_count;
2696 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
2697 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
2698 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
2699 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
2700 wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
2701 dst_map.data = context_map_bo_address(context, &dst_data,
2702 dst_texture->sub_resources[dst_sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_WRITE);
2704 flags &= ~WINED3D_BLT_RAW;
2706 bpp = dst_format->byte_count;
2707 src_height = src_box->bottom - src_box->top;
2708 src_width = src_box->right - src_box->left;
2709 dst_height = dst_box->bottom - dst_box->top;
2710 dst_width = dst_box->right - dst_box->left;
2711 row_byte_count = dst_width * bpp;
2713 sbase = (BYTE *)src_map.data
2714 + ((src_box->top / src_format->block_height) * src_map.row_pitch)
2715 + ((src_box->left / src_format->block_width) * src_format->block_byte_count);
2716 dbuf = (BYTE *)dst_map.data
2717 + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch)
2718 + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count);
2720 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
2722 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
2724 if (same_sub_resource)
2726 FIXME("Only plain blits supported on compressed surfaces.\n");
2727 hr = E_NOTIMPL;
2728 goto release;
2731 if (src_height != dst_height || src_width != dst_width)
2733 WARN("Stretching not supported on compressed surfaces.\n");
2734 hr = WINED3DERR_INVALIDCALL;
2735 goto release;
2738 hr = surface_cpu_blt_compressed(sbase, dbuf,
2739 src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height,
2740 src_format, flags, fx);
2741 goto release;
2744 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
2745 && (src_width != dst_width || src_height != dst_height))
2747 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
2748 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
2751 xinc = (src_width << 16) / dst_width;
2752 yinc = (src_height << 16) / dst_height;
2754 if (!flags)
2756 /* No effects, we can cheat here. */
2757 if (dst_width == src_width)
2759 if (dst_height == src_height)
2761 /* No stretching in either direction. This needs to be as fast
2762 * as possible. */
2763 sbuf = sbase;
2765 /* Check for overlapping surfaces. */
2766 if (!same_sub_resource || dst_box->top < src_box->top
2767 || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
2769 /* No overlap, or dst above src, so copy from top downwards. */
2770 for (y = 0; y < dst_height; ++y)
2772 memcpy(dbuf, sbuf, row_byte_count);
2773 sbuf += src_map.row_pitch;
2774 dbuf += dst_map.row_pitch;
2777 else if (dst_box->top > src_box->top)
2779 /* Copy from bottom upwards. */
2780 sbuf += src_map.row_pitch * dst_height;
2781 dbuf += dst_map.row_pitch * dst_height;
2782 for (y = 0; y < dst_height; ++y)
2784 sbuf -= src_map.row_pitch;
2785 dbuf -= dst_map.row_pitch;
2786 memcpy(dbuf, sbuf, row_byte_count);
2789 else
2791 /* Src and dst overlapping on the same line, use memmove. */
2792 for (y = 0; y < dst_height; ++y)
2794 memmove(dbuf, sbuf, row_byte_count);
2795 sbuf += src_map.row_pitch;
2796 dbuf += dst_map.row_pitch;
2800 else
2802 /* Stretching in y direction only. */
2803 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
2805 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
2806 memcpy(dbuf, sbuf, row_byte_count);
2807 dbuf += dst_map.row_pitch;
2811 else
2813 /* Stretching in X direction. */
2814 unsigned int last_sy = ~0u;
2815 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
2817 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
2819 if ((sy >> 16) == (last_sy >> 16))
2821 /* This source row is the same as last source row -
2822 * Copy the already stretched row. */
2823 memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count);
2825 else
2827 #define STRETCH_ROW(type) \
2828 do { \
2829 const type *s = (const type *)sbuf; \
2830 type *d = (type *)dbuf; \
2831 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
2832 d[x] = s[sx >> 16]; \
2833 } while(0)
2835 switch(bpp)
2837 case 1:
2838 STRETCH_ROW(BYTE);
2839 break;
2840 case 2:
2841 STRETCH_ROW(WORD);
2842 break;
2843 case 4:
2844 STRETCH_ROW(DWORD);
2845 break;
2846 case 3:
2848 const BYTE *s;
2849 BYTE *d = dbuf;
2850 for (x = sx = 0; x < dst_width; x++, sx+= xinc)
2852 DWORD pixel;
2854 s = sbuf + 3 * (sx >> 16);
2855 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
2856 d[0] = (pixel ) & 0xff;
2857 d[1] = (pixel >> 8) & 0xff;
2858 d[2] = (pixel >> 16) & 0xff;
2859 d += 3;
2861 break;
2863 default:
2864 FIXME("Stretched blit not implemented for bpp %u.\n", bpp * 8);
2865 hr = WINED3DERR_NOTAVAILABLE;
2866 goto error;
2868 #undef STRETCH_ROW
2870 dbuf += dst_map.row_pitch;
2871 last_sy = sy;
2875 else
2877 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
2878 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
2879 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
2880 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
2881 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
2883 /* The color keying flags are checked for correctness in ddraw. */
2884 if (flags & WINED3D_BLT_SRC_CKEY)
2886 keylow = src_texture->async.src_blt_color_key.color_space_low_value;
2887 keyhigh = src_texture->async.src_blt_color_key.color_space_high_value;
2889 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
2891 keylow = fx->src_color_key.color_space_low_value;
2892 keyhigh = fx->src_color_key.color_space_high_value;
2895 if (flags & WINED3D_BLT_DST_CKEY)
2897 /* Destination color keys are taken from the source surface! */
2898 destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value;
2899 destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value;
2901 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
2903 destkeylow = fx->dst_color_key.color_space_low_value;
2904 destkeyhigh = fx->dst_color_key.color_space_high_value;
2907 if (bpp == 1)
2909 keymask = 0xff;
2911 else
2913 DWORD masks[3];
2914 get_color_masks(src_format, masks);
2915 keymask = masks[0] | masks[1] | masks[2];
2917 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
2918 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
2921 if (flags & WINED3D_BLT_FX)
2923 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
2924 LONG tmpxy;
2925 dTopLeft = dbuf;
2926 dTopRight = dbuf + ((dst_width - 1) * bpp);
2927 dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
2928 dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
2930 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
2932 /* I don't think we need to do anything about this flag. */
2933 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
2935 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
2937 tmp = dTopRight;
2938 dTopRight = dTopLeft;
2939 dTopLeft = tmp;
2940 tmp = dBottomRight;
2941 dBottomRight = dBottomLeft;
2942 dBottomLeft = tmp;
2943 dstxinc = dstxinc * -1;
2945 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
2947 tmp = dTopLeft;
2948 dTopLeft = dBottomLeft;
2949 dBottomLeft = tmp;
2950 tmp = dTopRight;
2951 dTopRight = dBottomRight;
2952 dBottomRight = tmp;
2953 dstyinc = dstyinc * -1;
2955 if (fx->fx & WINEDDBLTFX_NOTEARING)
2957 /* I don't think we need to do anything about this flag. */
2958 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
2960 if (fx->fx & WINEDDBLTFX_ROTATE180)
2962 tmp = dBottomRight;
2963 dBottomRight = dTopLeft;
2964 dTopLeft = tmp;
2965 tmp = dBottomLeft;
2966 dBottomLeft = dTopRight;
2967 dTopRight = tmp;
2968 dstxinc = dstxinc * -1;
2969 dstyinc = dstyinc * -1;
2971 if (fx->fx & WINEDDBLTFX_ROTATE270)
2973 tmp = dTopLeft;
2974 dTopLeft = dBottomLeft;
2975 dBottomLeft = dBottomRight;
2976 dBottomRight = dTopRight;
2977 dTopRight = tmp;
2978 tmpxy = dstxinc;
2979 dstxinc = dstyinc;
2980 dstyinc = tmpxy;
2981 dstxinc = dstxinc * -1;
2983 if (fx->fx & WINEDDBLTFX_ROTATE90)
2985 tmp = dTopLeft;
2986 dTopLeft = dTopRight;
2987 dTopRight = dBottomRight;
2988 dBottomRight = dBottomLeft;
2989 dBottomLeft = tmp;
2990 tmpxy = dstxinc;
2991 dstxinc = dstyinc;
2992 dstyinc = tmpxy;
2993 dstyinc = dstyinc * -1;
2995 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
2997 /* I don't think we need to do anything about this flag. */
2998 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
3000 dbuf = dTopLeft;
3001 flags &= ~(WINED3D_BLT_FX);
3004 #define COPY_COLORKEY_FX(type) \
3005 do { \
3006 const type *s; \
3007 type *d = (type *)dbuf, *dx, tmp; \
3008 for (y = sy = 0; y < dst_height; ++y, sy += yinc) \
3010 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
3011 dx = d; \
3012 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3014 tmp = s[sx >> 16]; \
3015 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
3016 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
3018 dx[0] = tmp; \
3020 dx = (type *)(((BYTE *)dx) + dstxinc); \
3022 d = (type *)(((BYTE *)d) + dstyinc); \
3024 } while(0)
3026 switch (bpp)
3028 case 1:
3029 COPY_COLORKEY_FX(BYTE);
3030 break;
3031 case 2:
3032 COPY_COLORKEY_FX(WORD);
3033 break;
3034 case 4:
3035 COPY_COLORKEY_FX(DWORD);
3036 break;
3037 case 3:
3039 const BYTE *s;
3040 BYTE *d = dbuf, *dx;
3041 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3043 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3044 dx = d;
3045 for (x = sx = 0; x < dst_width; ++x, sx+= xinc)
3047 DWORD pixel, dpixel = 0;
3048 s = sbuf + 3 * (sx>>16);
3049 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3050 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
3051 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
3052 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
3054 dx[0] = (pixel ) & 0xff;
3055 dx[1] = (pixel >> 8) & 0xff;
3056 dx[2] = (pixel >> 16) & 0xff;
3058 dx += dstxinc;
3060 d += dstyinc;
3062 break;
3064 default:
3065 FIXME("%s color-keyed blit not implemented for bpp %u.\n",
3066 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
3067 hr = WINED3DERR_NOTAVAILABLE;
3068 goto error;
3069 #undef COPY_COLORKEY_FX
3073 error:
3074 if (flags)
3075 FIXME(" Unsupported flags %#x.\n", flags);
3077 release:
3078 context_unmap_bo_address(context, &dst_data, GL_PIXEL_UNPACK_BUFFER);
3079 if (!same_sub_resource)
3080 context_unmap_bo_address(context, &src_data, GL_PIXEL_UNPACK_BUFFER);
3081 if (SUCCEEDED(hr) && dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture)
3083 SetRect(&dst_texture->swapchain->front_buffer_update,
3084 dst_box->left, dst_box->top, dst_box->right, dst_box->bottom);
3085 dst_texture->swapchain->swapchain_ops->swapchain_frontbuffer_updated(dst_texture->swapchain);
3087 if (converted_texture)
3088 wined3d_texture_decref(converted_texture);
3089 if (context)
3090 context_release(context);
3092 return hr;
3095 static void surface_cpu_blt_colour_fill(struct wined3d_rendertarget_view *view,
3096 const struct wined3d_box *box, const struct wined3d_color *colour)
3098 struct wined3d_device *device = view->resource->device;
3099 struct wined3d_context *context = NULL;
3100 struct wined3d_texture *texture;
3101 struct wined3d_bo_address data;
3102 unsigned int x, y, w, h, bpp;
3103 struct wined3d_map_desc map;
3104 DWORD map_binding;
3105 BYTE *row;
3106 DWORD c;
3108 TRACE("view %p, box %s, colour %s.\n", view, debug_box(box), debug_color(colour));
3110 if (view->format_flags & WINED3DFMT_FLAG_BLOCKS)
3112 FIXME("Not implemented for format %s.\n", debug_d3dformat(view->format->id));
3113 return;
3116 if (view->format->id != view->resource->format->id)
3117 FIXME("View format %s doesn't match resource format %s.\n",
3118 debug_d3dformat(view->format->id), debug_d3dformat(view->resource->format->id));
3120 if (view->resource->type == WINED3D_RTYPE_BUFFER)
3122 FIXME("Not implemented for buffers.\n");
3123 return;
3126 if (device->d3d_initialized)
3127 context = context_acquire(device, NULL, 0);
3129 c = wined3d_format_convert_from_float(view->format, colour);
3130 bpp = view->format->byte_count;
3131 w = box->right - box->left;
3132 h = box->bottom - box->top;
3134 texture = texture_from_resource(view->resource);
3135 map_binding = texture->resource.map_binding;
3136 if (!wined3d_texture_load_location(texture, view->sub_resource_idx, context, map_binding))
3137 ERR("Failed to load the sub-resource into %s.\n", wined3d_debug_location(map_binding));
3138 wined3d_texture_invalidate_location(texture, view->sub_resource_idx, ~map_binding);
3139 wined3d_texture_get_pitch(texture, view->sub_resource_idx % texture->level_count,
3140 &map.row_pitch, &map.slice_pitch);
3141 wined3d_texture_get_memory(texture, view->sub_resource_idx, &data, map_binding);
3142 map.data = context_map_bo_address(context, &data,
3143 texture->sub_resources[view->sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_WRITE);
3144 map.data = (BYTE *)map.data
3145 + (box->front * map.slice_pitch)
3146 + ((box->top / view->format->block_height) * map.row_pitch)
3147 + ((box->left / view->format->block_width) * view->format->block_byte_count);
3149 switch (bpp)
3151 case 1:
3152 for (x = 0; x < w; ++x)
3154 ((BYTE *)map.data)[x] = c;
3156 break;
3158 case 2:
3159 for (x = 0; x < w; ++x)
3161 ((WORD *)map.data)[x] = c;
3163 break;
3165 case 3:
3167 row = map.data;
3168 for (x = 0; x < w; ++x, row += 3)
3170 row[0] = (c ) & 0xff;
3171 row[1] = (c >> 8) & 0xff;
3172 row[2] = (c >> 16) & 0xff;
3174 break;
3176 case 4:
3177 for (x = 0; x < w; ++x)
3179 ((DWORD *)map.data)[x] = c;
3181 break;
3183 default:
3184 FIXME("Not implemented for bpp %u.\n", bpp);
3185 wined3d_resource_unmap(view->resource, view->sub_resource_idx);
3186 return;
3189 row = map.data;
3190 for (y = 1; y < h; ++y)
3192 row += map.row_pitch;
3193 memcpy(row, map.data, w * bpp);
3196 context_unmap_bo_address(context, &data, GL_PIXEL_UNPACK_BUFFER);
3197 if (context)
3198 context_release(context);
3201 static void cpu_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
3202 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
3203 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
3205 struct wined3d_color c = {depth, 0.0f, 0.0f, 0.0f};
3206 struct wined3d_rendertarget_view *view;
3207 struct wined3d_box box;
3208 unsigned int i, j;
3210 if (!rect_count)
3212 rect_count = 1;
3213 clear_rects = draw_rect;
3216 for (i = 0; i < rect_count; ++i)
3218 box.left = max(clear_rects[i].left, draw_rect->left);
3219 box.top = max(clear_rects[i].top, draw_rect->top);
3220 box.right = min(clear_rects[i].right, draw_rect->right);
3221 box.bottom = min(clear_rects[i].bottom, draw_rect->bottom);
3222 box.front = 0;
3223 box.back = 1;
3225 if (box.left >= box.right || box.top >= box.bottom)
3226 continue;
3228 if (flags & WINED3DCLEAR_TARGET)
3230 for (j = 0; j < rt_count; ++j)
3232 if ((view = fb->render_targets[j]))
3233 surface_cpu_blt_colour_fill(view, &box, colour);
3237 if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil))
3239 if ((view->format->depth_size && !(flags & WINED3DCLEAR_ZBUFFER))
3240 || (view->format->stencil_size && !(flags & WINED3DCLEAR_STENCIL)))
3241 FIXME("Clearing %#x on %s.\n", flags, debug_d3dformat(view->format->id));
3243 surface_cpu_blt_colour_fill(view, &box, &c);
3248 static DWORD cpu_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
3249 struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
3250 DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture,
3251 unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect,
3252 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter)
3254 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
3255 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
3256 struct wined3d_blt_fx fx;
3257 DWORD flags = 0;
3259 memset(&fx, 0, sizeof(fx));
3260 switch (op)
3262 case WINED3D_BLIT_OP_COLOR_BLIT:
3263 case WINED3D_BLIT_OP_DEPTH_BLIT:
3264 case WINED3D_BLIT_OP_RAW_BLIT:
3265 break;
3266 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
3267 flags |= WINED3D_BLT_ALPHA_TEST;
3268 break;
3269 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
3270 flags |= WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_FX;
3271 fx.src_color_key = *color_key;
3272 break;
3273 default:
3274 FIXME("Unhandled op %#x.\n", op);
3275 break;
3278 if (FAILED(surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
3279 src_texture, src_sub_resource_idx, &src_box, flags, &fx, filter)))
3280 ERR("Failed to blit.\n");
3281 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
3283 return dst_location | (dst_texture->sub_resources[dst_sub_resource_idx].locations
3284 & dst_texture->resource.map_binding);
3287 static const struct wined3d_blitter_ops cpu_blitter_ops =
3289 cpu_blitter_destroy,
3290 cpu_blitter_clear,
3291 cpu_blitter_blit,
3294 struct wined3d_blitter *wined3d_cpu_blitter_create(void)
3296 struct wined3d_blitter *blitter;
3298 if (!(blitter = heap_alloc(sizeof(*blitter))))
3299 return NULL;
3301 TRACE("Created blitter %p.\n", blitter);
3303 blitter->ops = &cpu_blitter_ops;
3304 blitter->next = NULL;
3306 return blitter;
3309 HRESULT texture2d_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
3310 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
3311 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
3312 enum wined3d_texture_filter_type filter)
3314 struct wined3d_texture_sub_resource *src_sub_resource, *dst_sub_resource;
3315 struct wined3d_device *device = dst_texture->resource.device;
3316 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3317 const struct wined3d_color_key *colour_key = NULL;
3318 DWORD dst_location, valid_locations;
3319 DWORD src_ds_flags, dst_ds_flags;
3320 struct wined3d_context *context;
3321 enum wined3d_blit_op blit_op;
3322 BOOL scale, convert, resolve;
3323 RECT src_rect, dst_rect;
3325 static const DWORD simple_blit = WINED3D_BLT_SRC_CKEY
3326 | WINED3D_BLT_SRC_CKEY_OVERRIDE
3327 | WINED3D_BLT_ALPHA_TEST
3328 | WINED3D_BLT_RAW;
3330 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
3331 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
3332 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture, src_sub_resource_idx,
3333 debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
3334 TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage));
3336 if (fx)
3338 TRACE("fx %#x.\n", fx->fx);
3339 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
3340 fx->dst_color_key.color_space_low_value,
3341 fx->dst_color_key.color_space_high_value);
3342 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
3343 fx->src_color_key.color_space_low_value,
3344 fx->src_color_key.color_space_high_value);
3347 SetRect(&src_rect, src_box->left, src_box->top, src_box->right, src_box->bottom);
3348 SetRect(&dst_rect, dst_box->left, dst_box->top, dst_box->right, dst_box->bottom);
3350 if (!fx || !(fx->fx))
3351 flags &= ~WINED3D_BLT_FX;
3353 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
3354 if (flags & WINED3D_BLT_DO_NOT_WAIT)
3356 static unsigned int once;
3358 if (!once++)
3359 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
3362 flags &= ~(WINED3D_BLT_SYNCHRONOUS | WINED3D_BLT_DO_NOT_WAIT | WINED3D_BLT_WAIT);
3364 if (!device->d3d_initialized)
3366 WARN("D3D not initialized, using fallback.\n");
3367 goto cpu;
3370 if (flags & ~simple_blit)
3372 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
3373 goto fallback;
3376 src_swapchain = src_texture->swapchain;
3377 dst_swapchain = dst_texture->swapchain;
3379 /* This isn't strictly needed. FBO blits for example could deal with
3380 * cross-swapchain blits by first downloading the source to a texture
3381 * before switching to the destination context. We just have this here to
3382 * not have to deal with the issue, since cross-swapchain blits should be
3383 * rare. */
3384 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
3386 FIXME("Using fallback for cross-swapchain blit.\n");
3387 goto fallback;
3390 scale = src_box->right - src_box->left != dst_box->right - dst_box->left
3391 || src_box->bottom - src_box->top != dst_box->bottom - dst_box->top;
3392 convert = src_texture->resource.format->id != dst_texture->resource.format->id;
3393 resolve = src_texture->resource.multisample_type != dst_texture->resource.multisample_type;
3395 dst_ds_flags = dst_texture->resource.format_flags
3396 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
3397 src_ds_flags = src_texture->resource.format_flags
3398 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
3400 if (src_ds_flags || dst_ds_flags)
3402 TRACE("Depth/stencil blit.\n");
3404 if (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)
3405 dst_location = dst_texture->resource.draw_binding;
3406 else
3407 dst_location = dst_texture->resource.map_binding;
3409 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3410 valid_locations = device->blitter->ops->blitter_blit(device->blitter,
3411 WINED3D_BLIT_OP_DEPTH_BLIT, context,
3412 src_texture, src_sub_resource_idx, src_texture->resource.draw_binding, &src_rect,
3413 dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, NULL, filter);
3414 context_release(context);
3416 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
3417 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
3419 return WINED3D_OK;
3422 TRACE("Colour blit.\n");
3424 dst_sub_resource = &dst_texture->sub_resources[dst_sub_resource_idx];
3425 src_sub_resource = &src_texture->sub_resources[src_sub_resource_idx];
3427 /* In principle this would apply to depth blits as well, but we don't
3428 * implement those in the CPU blitter at the moment. */
3429 if ((dst_sub_resource->locations & dst_texture->resource.map_binding)
3430 && (src_sub_resource->locations & src_texture->resource.map_binding))
3432 if (scale)
3433 TRACE("Not doing sysmem blit because of scaling.\n");
3434 else if (convert)
3435 TRACE("Not doing sysmem blit because of format conversion.\n");
3436 else
3437 goto cpu;
3440 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
3441 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3443 colour_key = &fx->src_color_key;
3444 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
3446 else if (flags & WINED3D_BLT_SRC_CKEY)
3448 colour_key = &src_texture->async.src_blt_color_key;
3449 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
3451 else if (flags & WINED3D_BLT_ALPHA_TEST)
3453 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
3455 else if ((src_sub_resource->locations & surface_simple_locations)
3456 && !(dst_sub_resource->locations & surface_simple_locations))
3458 /* Upload */
3459 if (scale)
3460 TRACE("Not doing upload because of scaling.\n");
3461 else if (convert)
3462 TRACE("Not doing upload because of format conversion.\n");
3463 else if (dst_texture->resource.format->conv_byte_count)
3464 TRACE("Not doing upload because the destination format needs conversion.\n");
3465 else
3467 wined3d_texture_upload_from_texture(dst_texture, dst_sub_resource_idx, dst_box->left,
3468 dst_box->top, dst_box->front, src_texture, src_sub_resource_idx, src_box);
3469 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
3471 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3472 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx,
3473 context, dst_texture->resource.draw_binding);
3474 context_release(context);
3476 return WINED3D_OK;
3479 else if (dst_swapchain && dst_swapchain->back_buffers
3480 && dst_texture == dst_swapchain->front_buffer
3481 && src_texture == dst_swapchain->back_buffers[0])
3483 /* Use present for back -> front blits. The idea behind this is that
3484 * present is potentially faster than a blit, in particular when FBO
3485 * blits aren't available. Some ddraw applications like Half-Life and
3486 * Prince of Persia 3D use Blt() from the backbuffer to the
3487 * frontbuffer instead of doing a Flip(). D3d8 and d3d9 applications
3488 * can't blit directly to the frontbuffer. */
3489 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
3491 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
3493 /* Set the swap effect to COPY, we don't want the backbuffer to become
3494 * undefined. */
3495 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
3496 wined3d_swapchain_present(dst_swapchain, NULL, NULL,
3497 dst_swapchain->win_handle, dst_swapchain->swap_interval, 0);
3498 dst_swapchain->desc.swap_effect = swap_effect;
3500 return WINED3D_OK;
3502 else if ((flags & WINED3D_BLT_RAW) || (!scale && !convert && !resolve))
3504 blit_op = WINED3D_BLIT_OP_RAW_BLIT;
3507 if (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)
3508 dst_location = dst_texture->resource.draw_binding;
3509 else
3510 dst_location = dst_texture->resource.map_binding;
3512 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3513 valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context,
3514 src_texture, src_sub_resource_idx, src_texture->resource.draw_binding, &src_rect,
3515 dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, colour_key, filter);
3516 context_release(context);
3518 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
3519 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
3521 return WINED3D_OK;
3523 fallback:
3524 /* Special cases for render targets. */
3525 if (SUCCEEDED(wined3d_texture_blt_special(dst_texture, dst_sub_resource_idx, &dst_rect,
3526 src_texture, src_sub_resource_idx, &src_rect, flags, fx, filter)))
3527 return WINED3D_OK;
3529 cpu:
3530 return surface_cpu_blt(dst_texture, dst_sub_resource_idx, dst_box,
3531 src_texture, src_sub_resource_idx, src_box, flags, fx, filter);