wined3d: Use EXT_framebuffer_multisample_blit_scaled for scaled resolves.
[wine.git] / dlls / wined3d / surface.c
blobb7dd54cb61f6c8ba95846d7e709469021619584a
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 struct wined3d_context_gl *context_gl = wined3d_context_gl(context);
66 const struct wined3d_gl_info *gl_info = context_gl->gl_info;
67 DWORD src_mask, dst_mask;
68 GLbitfield gl_mask;
70 TRACE("device %p, src_texture %p, src_sub_resource_idx %u, src_location %s, src_rect %s, "
71 "dst_texture %p, dst_sub_resource_idx %u, dst_location %s, dst_rect %s.\n", device,
72 src_texture, src_sub_resource_idx, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect),
73 dst_texture, dst_sub_resource_idx, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
75 src_mask = src_texture->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
76 dst_mask = dst_texture->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
78 if (src_mask != dst_mask)
80 ERR("Incompatible formats %s and %s.\n",
81 debug_d3dformat(src_texture->resource.format->id),
82 debug_d3dformat(dst_texture->resource.format->id));
83 return;
86 if (!src_mask)
88 ERR("Not a depth / stencil format: %s.\n",
89 debug_d3dformat(src_texture->resource.format->id));
90 return;
93 gl_mask = 0;
94 if (src_mask & WINED3DFMT_FLAG_DEPTH)
95 gl_mask |= GL_DEPTH_BUFFER_BIT;
96 if (src_mask & WINED3DFMT_FLAG_STENCIL)
97 gl_mask |= GL_STENCIL_BUFFER_BIT;
99 /* Make sure the locations are up-to-date. Loading the destination
100 * surface isn't required if the entire surface is overwritten. */
101 wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, src_location);
102 if (!texture2d_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, dst_rect))
103 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
104 else
105 wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, dst_location);
107 wined3d_context_gl_apply_fbo_state_blit(context_gl, GL_READ_FRAMEBUFFER, NULL, 0,
108 &src_texture->resource, src_sub_resource_idx, src_location);
109 wined3d_context_gl_check_fbo_status(context_gl, GL_READ_FRAMEBUFFER);
111 wined3d_context_gl_apply_fbo_state_blit(context_gl, GL_DRAW_FRAMEBUFFER, NULL, 0,
112 &dst_texture->resource, dst_sub_resource_idx, dst_location);
113 wined3d_context_gl_set_draw_buffer(context_gl, GL_NONE);
114 wined3d_context_gl_check_fbo_status(context_gl, GL_DRAW_FRAMEBUFFER);
115 context_invalidate_state(context, STATE_FRAMEBUFFER);
117 if (gl_mask & GL_DEPTH_BUFFER_BIT)
119 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
120 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
122 if (gl_mask & GL_STENCIL_BUFFER_BIT)
124 if (gl_info->supported[EXT_STENCIL_TWO_SIDE])
126 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
127 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
129 gl_info->gl_ops.gl.p_glStencilMask(~0U);
130 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
133 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
134 context_invalidate_state(context, STATE_RASTERIZER);
136 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
137 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
138 checkGLcall("glBlitFramebuffer()");
141 /* Blit between surface locations. Onscreen on different swapchains is not supported.
142 * Depth / stencil is not supported. Context activation is done by the caller. */
143 void texture2d_blt_fbo(struct wined3d_device *device, struct wined3d_context *context,
144 enum wined3d_texture_filter_type filter, struct wined3d_texture *src_texture,
145 unsigned int src_sub_resource_idx, DWORD src_location, const RECT *src_rect,
146 struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, DWORD dst_location,
147 const RECT *dst_rect)
149 struct wined3d_texture *required_texture, *restore_texture;
150 const struct wined3d_gl_info *gl_info;
151 struct wined3d_context_gl *context_gl;
152 unsigned int restore_idx;
153 BOOL scaled_resolve;
154 GLenum gl_filter;
155 GLenum buffer;
156 RECT s, d;
158 TRACE("device %p, context %p, filter %s, src_texture %p, src_sub_resource_idx %u, src_location %s, "
159 "src_rect %s, dst_texture %p, dst_sub_resource_idx %u, dst_location %s, dst_rect %s.\n",
160 device, context, debug_d3dtexturefiltertype(filter), src_texture, src_sub_resource_idx,
161 wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect), dst_texture,
162 dst_sub_resource_idx, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
164 scaled_resolve = wined3d_texture_gl_is_multisample_location(wined3d_texture_gl(src_texture), src_location)
165 && (abs(src_rect->bottom - src_rect->top) != abs(dst_rect->bottom - dst_rect->top)
166 || abs(src_rect->right - src_rect->left) != abs(dst_rect->right - dst_rect->left));
168 switch (filter)
170 case WINED3D_TEXF_LINEAR:
171 gl_filter = scaled_resolve ? GL_SCALED_RESOLVE_NICEST_EXT : GL_LINEAR;
172 break;
174 default:
175 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
176 /* fall through */
177 case WINED3D_TEXF_NONE:
178 case WINED3D_TEXF_POINT:
179 gl_filter = scaled_resolve ? GL_SCALED_RESOLVE_FASTEST_EXT : GL_NEAREST;
180 break;
183 /* Make sure the locations are up-to-date. Loading the destination
184 * surface isn't required if the entire surface is overwritten. (And is
185 * in fact harmful if we're being called by surface_load_location() with
186 * the purpose of loading the destination surface.) */
187 wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, src_location);
188 if (!texture2d_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, dst_rect))
189 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
190 else
191 wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, dst_location);
193 /* Acquire a context for the front-buffer, even though we may be blitting
194 * to/from a back-buffer. Since context_acquire() doesn't take the
195 * resource location into account, it may consider the back-buffer to be
196 * offscreen. */
197 if (src_location == WINED3D_LOCATION_DRAWABLE)
198 required_texture = src_texture->swapchain->front_buffer;
199 else if (dst_location == WINED3D_LOCATION_DRAWABLE)
200 required_texture = dst_texture->swapchain->front_buffer;
201 else
202 required_texture = NULL;
204 restore_texture = context->current_rt.texture;
205 restore_idx = context->current_rt.sub_resource_idx;
206 if (restore_texture != required_texture)
207 context = context_acquire(device, required_texture, 0);
208 else
209 restore_texture = NULL;
211 context_gl = wined3d_context_gl(context);
212 if (!context_gl->valid)
214 context_release(context);
215 WARN("Invalid context, skipping blit.\n");
216 return;
219 gl_info = context_gl->gl_info;
221 if (src_location == WINED3D_LOCATION_DRAWABLE)
223 TRACE("Source texture %p is onscreen.\n", src_texture);
224 buffer = wined3d_texture_get_gl_buffer(src_texture);
225 s = *src_rect;
226 wined3d_texture_translate_drawable_coords(src_texture, context_gl->window, &s);
227 src_rect = &s;
229 else
231 TRACE("Source texture %p is offscreen.\n", src_texture);
232 buffer = GL_COLOR_ATTACHMENT0;
235 wined3d_context_gl_apply_fbo_state_blit(context_gl, GL_READ_FRAMEBUFFER,
236 &src_texture->resource, src_sub_resource_idx, NULL, 0, src_location);
237 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
238 checkGLcall("glReadBuffer()");
239 wined3d_context_gl_check_fbo_status(context_gl, GL_READ_FRAMEBUFFER);
241 if (dst_location == WINED3D_LOCATION_DRAWABLE)
243 TRACE("Destination texture %p is onscreen.\n", dst_texture);
244 buffer = wined3d_texture_get_gl_buffer(dst_texture);
245 d = *dst_rect;
246 wined3d_texture_translate_drawable_coords(dst_texture, context_gl->window, &d);
247 dst_rect = &d;
249 else
251 TRACE("Destination texture %p is offscreen.\n", dst_texture);
252 buffer = GL_COLOR_ATTACHMENT0;
255 wined3d_context_gl_apply_fbo_state_blit(context_gl, GL_DRAW_FRAMEBUFFER,
256 &dst_texture->resource, dst_sub_resource_idx, NULL, 0, dst_location);
257 wined3d_context_gl_set_draw_buffer(context_gl, buffer);
258 wined3d_context_gl_check_fbo_status(context_gl, GL_DRAW_FRAMEBUFFER);
259 context_invalidate_state(context, STATE_FRAMEBUFFER);
261 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
262 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
263 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
264 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
265 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
267 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
268 context_invalidate_state(context, STATE_RASTERIZER);
270 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
271 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, GL_COLOR_BUFFER_BIT, gl_filter);
272 checkGLcall("glBlitFramebuffer()");
274 if (dst_location == WINED3D_LOCATION_DRAWABLE && dst_texture->swapchain->front_buffer == dst_texture)
275 gl_info->gl_ops.gl.p_glFlush();
277 if (restore_texture)
278 context_restore(context, restore_texture, restore_idx);
281 BOOL fbo_blitter_supported(enum wined3d_blit_op blit_op, const struct wined3d_gl_info *gl_info,
282 const struct wined3d_resource *src_resource, DWORD src_location,
283 const struct wined3d_resource *dst_resource, DWORD dst_location)
285 const struct wined3d_format *src_format = src_resource->format;
286 const struct wined3d_format *dst_format = dst_resource->format;
288 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
289 return FALSE;
291 /* Source and/or destination need to be on the GL side */
292 if (!(src_resource->access & dst_resource->access & WINED3D_RESOURCE_ACCESS_GPU))
293 return FALSE;
295 if (src_resource->type != WINED3D_RTYPE_TEXTURE_2D)
296 return FALSE;
298 switch (blit_op)
300 case WINED3D_BLIT_OP_COLOR_BLIT:
301 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
302 || (src_resource->bind_flags & WINED3D_BIND_RENDER_TARGET)))
303 return FALSE;
304 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
305 || (dst_resource->bind_flags & WINED3D_BIND_RENDER_TARGET)))
306 return FALSE;
307 if ((src_format->id != dst_format->id || dst_location == WINED3D_LOCATION_DRAWABLE)
308 && (!is_identity_fixup(src_format->color_fixup) || !is_identity_fixup(dst_format->color_fixup)))
309 return FALSE;
310 break;
312 case WINED3D_BLIT_OP_DEPTH_BLIT:
313 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
314 return FALSE;
315 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
316 return FALSE;
317 /* Accept pure swizzle fixups for depth formats. In general we
318 * ignore the stencil component (if present) at the moment and the
319 * swizzle is not relevant with just the depth component. */
320 if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup)
321 || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup))
322 return FALSE;
323 break;
325 default:
326 return FALSE;
329 return TRUE;
332 /* See also float_16_to_32() in wined3d_private.h */
333 static inline unsigned short float_32_to_16(const float *in)
335 int exp = 0;
336 float tmp = fabsf(*in);
337 unsigned int mantissa;
338 unsigned short ret;
340 /* Deal with special numbers */
341 if (*in == 0.0f)
342 return 0x0000;
343 if (isnan(*in))
344 return 0x7c01;
345 if (isinf(*in))
346 return (*in < 0.0f ? 0xfc00 : 0x7c00);
348 if (tmp < (float)(1u << 10))
352 tmp = tmp * 2.0f;
353 exp--;
354 } while (tmp < (float)(1u << 10));
356 else if (tmp >= (float)(1u << 11))
360 tmp /= 2.0f;
361 exp++;
362 } while (tmp >= (float)(1u << 11));
365 mantissa = (unsigned int)tmp;
366 if (tmp - mantissa >= 0.5f)
367 ++mantissa; /* Round to nearest, away from zero. */
369 exp += 10; /* Normalize the mantissa. */
370 exp += 15; /* Exponent is encoded with excess 15. */
372 if (exp > 30) /* too big */
374 ret = 0x7c00; /* INF */
376 else if (exp <= 0)
378 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
379 while (exp <= 0)
381 mantissa = mantissa >> 1;
382 ++exp;
384 ret = mantissa & 0x3ff;
386 else
388 ret = (exp << 10) | (mantissa & 0x3ff);
391 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
392 return ret;
395 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
396 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
398 unsigned short *dst_s;
399 const float *src_f;
400 unsigned int x, y;
402 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
404 for (y = 0; y < h; ++y)
406 src_f = (const float *)(src + y * pitch_in);
407 dst_s = (unsigned short *) (dst + y * pitch_out);
408 for (x = 0; x < w; ++x)
410 dst_s[x] = float_32_to_16(src_f + x);
415 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
416 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
418 static const unsigned char convert_5to8[] =
420 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
421 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
422 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
423 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
425 static const unsigned char convert_6to8[] =
427 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
428 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
429 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
430 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
431 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
432 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
433 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
434 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
436 unsigned int x, y;
438 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
440 for (y = 0; y < h; ++y)
442 const WORD *src_line = (const WORD *)(src + y * pitch_in);
443 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
444 for (x = 0; x < w; ++x)
446 WORD pixel = src_line[x];
447 dst_line[x] = 0xff000000u
448 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
449 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
450 | convert_5to8[(pixel & 0x001fu)];
455 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
456 * in both cases we're just setting the X / Alpha channel to 0xff. */
457 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
458 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
460 unsigned int x, y;
462 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
464 for (y = 0; y < h; ++y)
466 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
467 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
469 for (x = 0; x < w; ++x)
471 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
476 static inline BYTE cliptobyte(int x)
478 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
481 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
482 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
484 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
485 unsigned int x, y;
487 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
489 for (y = 0; y < h; ++y)
491 const BYTE *src_line = src + y * pitch_in;
492 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
493 for (x = 0; x < w; ++x)
495 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
496 * C = Y - 16; D = U - 128; E = V - 128;
497 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
498 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
499 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
500 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
501 * U and V are shared between the pixels. */
502 if (!(x & 1)) /* For every even pixel, read new U and V. */
504 d = (int) src_line[1] - 128;
505 e = (int) src_line[3] - 128;
506 r2 = 409 * e + 128;
507 g2 = - 100 * d - 208 * e + 128;
508 b2 = 516 * d + 128;
510 c2 = 298 * ((int) src_line[0] - 16);
511 dst_line[x] = 0xff000000
512 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
513 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
514 | cliptobyte((c2 + b2) >> 8); /* blue */
515 /* Scale RGB values to 0..255 range,
516 * then clip them if still not in range (may be negative),
517 * then shift them within DWORD if necessary. */
518 src_line += 2;
523 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
524 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
526 unsigned int x, y;
527 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
529 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
531 for (y = 0; y < h; ++y)
533 const BYTE *src_line = src + y * pitch_in;
534 WORD *dst_line = (WORD *)(dst + y * pitch_out);
535 for (x = 0; x < w; ++x)
537 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
538 * C = Y - 16; D = U - 128; E = V - 128;
539 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
540 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
541 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
542 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
543 * U and V are shared between the pixels. */
544 if (!(x & 1)) /* For every even pixel, read new U and V. */
546 d = (int) src_line[1] - 128;
547 e = (int) src_line[3] - 128;
548 r2 = 409 * e + 128;
549 g2 = - 100 * d - 208 * e + 128;
550 b2 = 516 * d + 128;
552 c2 = 298 * ((int) src_line[0] - 16);
553 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
554 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
555 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
556 /* Scale RGB values to 0..255 range,
557 * then clip them if still not in range (may be negative),
558 * then shift them within DWORD if necessary. */
559 src_line += 2;
564 struct d3dfmt_converter_desc
566 enum wined3d_format_id from, to;
567 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
570 static const struct d3dfmt_converter_desc converters[] =
572 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
573 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
574 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
575 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
576 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
577 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
580 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
581 enum wined3d_format_id to)
583 unsigned int i;
585 for (i = 0; i < ARRAY_SIZE(converters); ++i)
587 if (converters[i].from == from && converters[i].to == to)
588 return &converters[i];
591 return NULL;
594 static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture,
595 unsigned int sub_resource_idx, const struct wined3d_format *dst_format)
597 unsigned int texture_level = sub_resource_idx % src_texture->level_count;
598 const struct wined3d_format *src_format = src_texture->resource.format;
599 struct wined3d_device *device = src_texture->resource.device;
600 const struct d3dfmt_converter_desc *conv = NULL;
601 unsigned int src_row_pitch, src_slice_pitch;
602 struct wined3d_texture *dst_texture;
603 struct wined3d_bo_address src_data;
604 struct wined3d_resource_desc desc;
605 struct wined3d_context *context;
606 DWORD map_binding;
608 if (!(conv = find_converter(src_format->id, dst_format->id)) && (!device->d3d_initialized
609 || !is_identity_fixup(src_format->color_fixup) || src_format->conv_byte_count
610 || !is_identity_fixup(dst_format->color_fixup) || dst_format->conv_byte_count
611 || ((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
612 && !src_format->decompress)))
614 FIXME("Cannot find a conversion function from format %s to %s.\n",
615 debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
616 return NULL;
619 /* FIXME: Multisampled conversion? */
620 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
621 desc.format = dst_format->id;
622 desc.multisample_type = WINED3D_MULTISAMPLE_NONE;
623 desc.multisample_quality = 0;
624 desc.usage = WINED3DUSAGE_SCRATCH | WINED3DUSAGE_PRIVATE;
625 desc.bind_flags = 0;
626 desc.access = WINED3D_RESOURCE_ACCESS_CPU | WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W;
627 desc.width = wined3d_texture_get_level_width(src_texture, texture_level);
628 desc.height = wined3d_texture_get_level_height(src_texture, texture_level);
629 desc.depth = 1;
630 desc.size = 0;
631 if (FAILED(wined3d_texture_create(device, &desc, 1, 1, WINED3D_TEXTURE_CREATE_DISCARD,
632 NULL, NULL, &wined3d_null_parent_ops, &dst_texture)))
634 ERR("Failed to create a destination texture for conversion.\n");
635 return NULL;
638 context = context_acquire(device, NULL, 0);
640 map_binding = src_texture->resource.map_binding;
641 if (!wined3d_texture_load_location(src_texture, sub_resource_idx, context, map_binding))
642 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
643 wined3d_texture_get_pitch(src_texture, texture_level, &src_row_pitch, &src_slice_pitch);
644 wined3d_texture_get_memory(src_texture, sub_resource_idx, &src_data, map_binding);
646 if (conv)
648 unsigned int dst_row_pitch, dst_slice_pitch;
649 struct wined3d_bo_address dst_data;
650 struct wined3d_range range;
651 const BYTE *src;
652 BYTE *dst;
654 map_binding = dst_texture->resource.map_binding;
655 if (!wined3d_texture_load_location(dst_texture, 0, context, map_binding))
656 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
657 wined3d_texture_get_pitch(dst_texture, 0, &dst_row_pitch, &dst_slice_pitch);
658 wined3d_texture_get_memory(dst_texture, 0, &dst_data, map_binding);
660 src = wined3d_context_map_bo_address(context, &src_data,
661 src_texture->sub_resources[sub_resource_idx].size, 0, WINED3D_MAP_READ);
662 dst = wined3d_context_map_bo_address(context, &dst_data,
663 dst_texture->sub_resources[0].size, 0, WINED3D_MAP_WRITE);
665 conv->convert(src, dst, src_row_pitch, dst_row_pitch, desc.width, desc.height);
667 range.offset = 0;
668 range.size = dst_texture->sub_resources[0].size;
669 wined3d_texture_invalidate_location(dst_texture, 0, ~map_binding);
670 wined3d_context_unmap_bo_address(context, &dst_data, 0, 1, &range);
671 wined3d_context_unmap_bo_address(context, &src_data, 0, 0, NULL);
673 else
675 struct wined3d_box src_box = {0, 0, desc.width, desc.height, 0, 1};
677 TRACE("Using upload conversion.\n");
679 wined3d_texture_prepare_location(dst_texture, 0, context, WINED3D_LOCATION_TEXTURE_RGB);
680 dst_texture->texture_ops->texture_upload_data(context, wined3d_const_bo_address(&src_data),
681 src_format, &src_box, src_row_pitch, src_slice_pitch,
682 dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB, 0, 0, 0);
684 wined3d_texture_validate_location(dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB);
685 wined3d_texture_invalidate_location(dst_texture, 0, ~WINED3D_LOCATION_TEXTURE_RGB);
688 context_release(context);
690 return dst_texture;
693 void texture2d_read_from_framebuffer(struct wined3d_texture *texture, unsigned int sub_resource_idx,
694 struct wined3d_context *context, DWORD src_location, DWORD dst_location)
696 struct wined3d_resource *resource = &texture->resource;
697 struct wined3d_device *device = resource->device;
698 const struct wined3d_format_gl *format_gl;
699 struct wined3d_texture *restore_texture;
700 const struct wined3d_gl_info *gl_info;
701 struct wined3d_context_gl *context_gl;
702 unsigned int row_pitch, slice_pitch;
703 unsigned int width, height, level;
704 struct wined3d_bo_address data;
705 unsigned int restore_idx;
706 BYTE *row, *top, *bottom;
707 BOOL src_is_upside_down;
708 unsigned int i;
709 BYTE *mem;
711 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
713 restore_texture = context->current_rt.texture;
714 restore_idx = context->current_rt.sub_resource_idx;
715 if (restore_texture != texture || restore_idx != sub_resource_idx)
716 context = context_acquire(device, texture, sub_resource_idx);
717 else
718 restore_texture = NULL;
719 context_gl = wined3d_context_gl(context);
720 gl_info = context_gl->gl_info;
722 if (src_location != resource->draw_binding)
724 wined3d_context_gl_apply_fbo_state_blit(context_gl, GL_READ_FRAMEBUFFER,
725 resource, sub_resource_idx, NULL, 0, src_location);
726 wined3d_context_gl_check_fbo_status(context_gl, GL_READ_FRAMEBUFFER);
727 context_invalidate_state(context, STATE_FRAMEBUFFER);
729 else
731 wined3d_context_gl_apply_blit_state(context_gl, device);
734 /* Select the correct read buffer, and give some debug output.
735 * There is no need to keep track of the current read buffer or reset it,
736 * every part of the code that reads sets the read buffer as desired.
738 if (src_location != WINED3D_LOCATION_DRAWABLE || wined3d_resource_is_offscreen(resource))
740 /* Mapping the primary render target which is not on a swapchain.
741 * Read from the back buffer. */
742 TRACE("Mapping offscreen render target.\n");
743 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_context_gl_get_offscreen_gl_buffer(context_gl));
744 src_is_upside_down = TRUE;
746 else
748 /* Onscreen surfaces are always part of a swapchain */
749 GLenum buffer = wined3d_texture_get_gl_buffer(texture);
750 TRACE("Mapping %#x buffer.\n", buffer);
751 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
752 src_is_upside_down = FALSE;
754 checkGLcall("glReadBuffer");
756 if (data.buffer_object)
758 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
759 checkGLcall("glBindBuffer");
762 level = sub_resource_idx % texture->level_count;
763 wined3d_texture_get_pitch(texture, level, &row_pitch, &slice_pitch);
764 format_gl = wined3d_format_gl(resource->format);
766 /* Setup pixel store pack state -- to glReadPixels into the correct place */
767 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / format_gl->f.byte_count);
768 checkGLcall("glPixelStorei");
770 width = wined3d_texture_get_level_width(texture, level);
771 height = wined3d_texture_get_level_height(texture, level);
772 gl_info->gl_ops.gl.p_glReadPixels(0, 0, width, height,
773 format_gl->format, format_gl->type, data.addr);
774 checkGLcall("glReadPixels");
776 /* Reset previous pixel store pack state */
777 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
778 checkGLcall("glPixelStorei");
780 if (!src_is_upside_down)
782 /* glReadPixels returns the image upside down, and there is no way to
783 * prevent this. Flip the lines in software. */
785 if (!(row = heap_alloc(row_pitch)))
786 goto error;
788 if (data.buffer_object)
790 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
791 checkGLcall("glMapBuffer");
793 else
794 mem = data.addr;
796 top = mem;
797 bottom = mem + row_pitch * (height - 1);
798 for (i = 0; i < height / 2; i++)
800 memcpy(row, top, row_pitch);
801 memcpy(top, bottom, row_pitch);
802 memcpy(bottom, row, row_pitch);
803 top += row_pitch;
804 bottom -= row_pitch;
806 heap_free(row);
808 if (data.buffer_object)
809 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
812 error:
813 if (data.buffer_object)
815 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
816 checkGLcall("glBindBuffer");
819 if (restore_texture)
820 context_restore(context, restore_texture, restore_idx);
823 /* Read the framebuffer contents into a texture. Note that this function
824 * doesn't do any kind of flipping. Using this on an onscreen surface will
825 * result in a flipped D3D texture.
827 * Context activation is done by the caller. This function may temporarily
828 * switch to a different context and restore the original one before return. */
829 void texture2d_load_fb_texture(struct wined3d_texture_gl *texture_gl,
830 unsigned int sub_resource_idx, BOOL srgb, struct wined3d_context *context)
832 struct wined3d_texture *restore_texture;
833 const struct wined3d_gl_info *gl_info;
834 struct wined3d_context_gl *context_gl;
835 struct wined3d_resource *resource;
836 unsigned int restore_idx, level;
837 struct wined3d_device *device;
838 GLenum target;
840 resource = &texture_gl->t.resource;
841 device = resource->device;
842 restore_texture = context->current_rt.texture;
843 restore_idx = context->current_rt.sub_resource_idx;
844 if (restore_texture != &texture_gl->t || restore_idx != sub_resource_idx)
845 context = context_acquire(device, &texture_gl->t, sub_resource_idx);
846 else
847 restore_texture = NULL;
848 context_gl = wined3d_context_gl(context);
850 gl_info = context_gl->gl_info;
851 device_invalidate_state(device, STATE_FRAMEBUFFER);
853 wined3d_texture_gl_prepare_texture(texture_gl, context_gl, srgb);
854 wined3d_texture_gl_bind_and_dirtify(texture_gl, context_gl, srgb);
856 TRACE("Reading back offscreen render target %p, %u.\n", texture_gl, sub_resource_idx);
858 if (wined3d_resource_is_offscreen(resource))
859 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_context_gl_get_offscreen_gl_buffer(context_gl));
860 else
861 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(&texture_gl->t));
862 checkGLcall("glReadBuffer");
864 level = sub_resource_idx % texture_gl->t.level_count;
865 target = wined3d_texture_gl_get_sub_resource_target(texture_gl, sub_resource_idx);
866 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(target, level, 0, 0, 0, 0,
867 wined3d_texture_get_level_width(&texture_gl->t, level),
868 wined3d_texture_get_level_height(&texture_gl->t, level));
869 checkGLcall("glCopyTexSubImage2D");
871 if (restore_texture)
872 context_restore(context, restore_texture, restore_idx);
875 /* Context activation is done by the caller. */
876 static void fbo_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
878 struct wined3d_blitter *next;
880 if ((next = blitter->next))
881 next->ops->blitter_destroy(next, context);
883 heap_free(blitter);
886 static void fbo_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
887 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
888 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
890 struct wined3d_blitter *next;
892 if ((next = blitter->next))
893 next->ops->blitter_clear(next, device, rt_count, fb, rect_count,
894 clear_rects, draw_rect, flags, colour, depth, stencil);
897 static DWORD fbo_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
898 struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
899 DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture,
900 unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect,
901 const struct wined3d_color_key *colour_key, enum wined3d_texture_filter_type filter)
903 struct wined3d_context_gl *context_gl = wined3d_context_gl(context);
904 struct wined3d_resource *src_resource, *dst_resource;
905 enum wined3d_blit_op blit_op = op;
906 struct wined3d_device *device;
907 struct wined3d_blitter *next;
909 TRACE("blitter %p, op %#x, context %p, src_texture %p, src_sub_resource_idx %u, src_location %s, src_rect %s, "
910 "dst_texture %p, dst_sub_resource_idx %u, dst_location %s, dst_rect %s, colour_key %p, filter %s.\n",
911 blitter, op, context, src_texture, src_sub_resource_idx, wined3d_debug_location(src_location),
912 wine_dbgstr_rect(src_rect), dst_texture, dst_sub_resource_idx, wined3d_debug_location(dst_location),
913 wine_dbgstr_rect(dst_rect), colour_key, debug_d3dtexturefiltertype(filter));
915 src_resource = &src_texture->resource;
916 dst_resource = &dst_texture->resource;
918 device = dst_resource->device;
920 if (blit_op == WINED3D_BLIT_OP_RAW_BLIT && dst_resource->format->id == src_resource->format->id)
922 if (dst_resource->format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
923 blit_op = WINED3D_BLIT_OP_DEPTH_BLIT;
924 else
925 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
928 if (!fbo_blitter_supported(blit_op, context_gl->gl_info,
929 src_resource, src_location, dst_resource, dst_location))
931 if (!(next = blitter->next))
933 ERR("No blitter to handle blit op %#x.\n", op);
934 return dst_location;
937 TRACE("Forwarding to blitter %p.\n", next);
938 return next->ops->blitter_blit(next, op, context, src_texture, src_sub_resource_idx, src_location,
939 src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect, colour_key, filter);
942 if (blit_op == WINED3D_BLIT_OP_COLOR_BLIT)
944 TRACE("Colour blit.\n");
945 texture2d_blt_fbo(device, context, filter, src_texture, src_sub_resource_idx, src_location,
946 src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect);
947 return dst_location;
950 if (blit_op == WINED3D_BLIT_OP_DEPTH_BLIT)
952 TRACE("Depth/stencil blit.\n");
953 texture2d_depth_blt_fbo(device, context, src_texture, src_sub_resource_idx, src_location,
954 src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect);
955 return dst_location;
958 ERR("This blitter does not implement blit op %#x.\n", blit_op);
959 return dst_location;
962 static const struct wined3d_blitter_ops fbo_blitter_ops =
964 fbo_blitter_destroy,
965 fbo_blitter_clear,
966 fbo_blitter_blit,
969 void wined3d_fbo_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
971 struct wined3d_blitter *blitter;
973 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
974 return;
976 if (!(blitter = heap_alloc(sizeof(*blitter))))
977 return;
979 TRACE("Created blitter %p.\n", blitter);
981 blitter->ops = &fbo_blitter_ops;
982 blitter->next = *next;
983 *next = blitter;
986 /* Context activation is done by the caller. */
987 static void raw_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
989 struct wined3d_blitter *next;
991 if ((next = blitter->next))
992 next->ops->blitter_destroy(next, context);
994 heap_free(blitter);
997 /* Context activation is done by the caller. */
998 static void raw_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
999 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
1000 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
1002 struct wined3d_blitter *next;
1004 if (!(next = blitter->next))
1006 ERR("No blitter to handle clear.\n");
1007 return;
1010 TRACE("Forwarding to blitter %p.\n", next);
1011 next->ops->blitter_clear(next, device, rt_count, fb, rect_count,
1012 clear_rects, draw_rect, flags, colour, depth, stencil);
1015 /* Context activation is done by the caller. */
1016 static DWORD raw_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
1017 struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1018 DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture,
1019 unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect,
1020 const struct wined3d_color_key *colour_key, enum wined3d_texture_filter_type filter)
1022 struct wined3d_texture_gl *src_texture_gl = wined3d_texture_gl(src_texture);
1023 struct wined3d_texture_gl *dst_texture_gl = wined3d_texture_gl(dst_texture);
1024 struct wined3d_context_gl *context_gl = wined3d_context_gl(context);
1025 const struct wined3d_gl_info *gl_info = context_gl->gl_info;
1026 unsigned int src_level, src_layer, dst_level, dst_layer;
1027 struct wined3d_blitter *next;
1028 GLuint src_name, dst_name;
1029 DWORD location;
1031 /* If we would need to copy from a renderbuffer or drawable, we'd probably
1032 * be better off using the FBO blitter directly, since we'd need to use it
1033 * to copy the resource contents to the texture anyway. */
1034 if (op != WINED3D_BLIT_OP_RAW_BLIT
1035 || (src_texture->resource.format->id == dst_texture->resource.format->id
1036 && (!(src_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
1037 || !(dst_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB)))))
1039 if (!(next = blitter->next))
1041 ERR("No blitter to handle blit op %#x.\n", op);
1042 return dst_location;
1045 TRACE("Forwarding to blitter %p.\n", next);
1046 return next->ops->blitter_blit(next, op, context, src_texture, src_sub_resource_idx, src_location,
1047 src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect, colour_key, filter);
1050 TRACE("Blit using ARB_copy_image.\n");
1052 src_level = src_sub_resource_idx % src_texture->level_count;
1053 src_layer = src_sub_resource_idx / src_texture->level_count;
1055 dst_level = dst_sub_resource_idx % dst_texture->level_count;
1056 dst_layer = dst_sub_resource_idx / dst_texture->level_count;
1058 location = src_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB);
1059 if (!location)
1060 location = src_texture->flags & WINED3D_TEXTURE_IS_SRGB
1061 ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
1062 if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, location))
1063 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(location));
1064 src_name = wined3d_texture_gl_get_texture_name(src_texture_gl,
1065 context, location == WINED3D_LOCATION_TEXTURE_SRGB);
1067 location = dst_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB);
1068 if (!location)
1069 location = dst_texture->flags & WINED3D_TEXTURE_IS_SRGB
1070 ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
1071 if (texture2d_is_full_rect(dst_texture, dst_level, dst_rect))
1073 if (!wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, location))
1074 ERR("Failed to prepare the destination sub-resource into %s.\n", wined3d_debug_location(location));
1076 else
1078 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, location))
1079 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(location));
1081 dst_name = wined3d_texture_gl_get_texture_name(dst_texture_gl,
1082 context, location == WINED3D_LOCATION_TEXTURE_SRGB);
1084 GL_EXTCALL(glCopyImageSubData(src_name, src_texture_gl->target, src_level,
1085 src_rect->left, src_rect->top, src_layer, dst_name, dst_texture_gl->target, dst_level,
1086 dst_rect->left, dst_rect->top, dst_layer, src_rect->right - src_rect->left,
1087 src_rect->bottom - src_rect->top, 1));
1088 checkGLcall("copy image data");
1090 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, location);
1091 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~location);
1092 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location))
1093 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(dst_location));
1095 return dst_location | location;
1098 static const struct wined3d_blitter_ops raw_blitter_ops =
1100 raw_blitter_destroy,
1101 raw_blitter_clear,
1102 raw_blitter_blit,
1105 void wined3d_raw_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
1107 struct wined3d_blitter *blitter;
1109 if (!gl_info->supported[ARB_COPY_IMAGE])
1110 return;
1112 if (!(blitter = heap_alloc(sizeof(*blitter))))
1113 return;
1115 TRACE("Created blitter %p.\n", blitter);
1117 blitter->ops = &raw_blitter_ops;
1118 blitter->next = *next;
1119 *next = blitter;
1122 /* Context activation is done by the caller. */
1123 static void ffp_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
1125 struct wined3d_blitter *next;
1127 if ((next = blitter->next))
1128 next->ops->blitter_destroy(next, context);
1130 heap_free(blitter);
1133 static BOOL ffp_blit_supported(enum wined3d_blit_op blit_op, const struct wined3d_context *context,
1134 const struct wined3d_resource *src_resource, DWORD src_location,
1135 const struct wined3d_resource *dst_resource, DWORD dst_location)
1137 const struct wined3d_format *src_format = src_resource->format;
1138 const struct wined3d_format *dst_format = dst_resource->format;
1139 BOOL decompress;
1141 if (src_resource->type != WINED3D_RTYPE_TEXTURE_2D)
1142 return FALSE;
1144 decompress = (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
1145 && !(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED);
1146 if (!decompress && !(src_resource->access & dst_resource->access & WINED3D_RESOURCE_ACCESS_GPU))
1148 TRACE("Source or destination resource is not GPU accessible.\n");
1149 return FALSE;
1152 if (blit_op == WINED3D_BLIT_OP_RAW_BLIT && dst_format->id == src_format->id)
1154 if (dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
1155 blit_op = WINED3D_BLIT_OP_DEPTH_BLIT;
1156 else
1157 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
1160 switch (blit_op)
1162 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
1163 if (context->d3d_info->shader_color_key)
1165 TRACE("Color keying requires converted textures.\n");
1166 return FALSE;
1168 case WINED3D_BLIT_OP_COLOR_BLIT:
1169 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
1170 if (!wined3d_context_gl_const(context)->gl_info->supported[WINED3D_GL_LEGACY_CONTEXT])
1171 return FALSE;
1173 if (TRACE_ON(d3d))
1175 TRACE("Checking support for fixup:\n");
1176 dump_color_fixup_desc(src_format->color_fixup);
1179 /* We only support identity conversions. */
1180 if (!is_identity_fixup(src_format->color_fixup)
1181 || !is_identity_fixup(dst_format->color_fixup))
1183 if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER
1184 && dst_format->id == src_format->id && dst_location == WINED3D_LOCATION_DRAWABLE)
1186 WARN("Claiming fixup support because of ORM_BACKBUFFER.\n");
1188 else
1190 TRACE("Fixups are not supported.\n");
1191 return FALSE;
1195 if (!(dst_resource->bind_flags & WINED3D_BIND_RENDER_TARGET))
1197 TRACE("Can only blit to render targets.\n");
1198 return FALSE;
1200 return TRUE;
1202 default:
1203 TRACE("Unsupported blit operation %#x.\n", blit_op);
1204 return FALSE;
1208 static BOOL ffp_blitter_use_cpu_clear(struct wined3d_rendertarget_view *view)
1210 struct wined3d_resource *resource;
1211 struct wined3d_texture *texture;
1212 DWORD locations;
1214 resource = view->resource;
1215 if (resource->type == WINED3D_RTYPE_BUFFER)
1216 return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU);
1218 texture = texture_from_resource(resource);
1219 locations = texture->sub_resources[view->sub_resource_idx].locations;
1220 if (locations & (resource->map_binding | WINED3D_LOCATION_DISCARDED))
1221 return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU)
1222 || (texture->flags & WINED3D_TEXTURE_PIN_SYSMEM);
1224 return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU)
1225 && !(texture->flags & WINED3D_TEXTURE_CONVERTED);
1228 static void ffp_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
1229 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
1230 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
1232 struct wined3d_rendertarget_view *view, *previous = NULL;
1233 BOOL have_identical_size = TRUE;
1234 struct wined3d_fb_state tmp_fb;
1235 unsigned int next_rt_count = 0;
1236 struct wined3d_blitter *next;
1237 DWORD next_flags = 0;
1238 unsigned int i;
1240 if (flags & WINED3DCLEAR_TARGET)
1242 for (i = 0; i < rt_count; ++i)
1244 if (!(view = fb->render_targets[i]))
1245 continue;
1247 if (ffp_blitter_use_cpu_clear(view)
1248 || (!(view->resource->bind_flags & WINED3D_BIND_RENDER_TARGET)
1249 && (wined3d_settings.offscreen_rendering_mode != ORM_FBO
1250 || !(view->format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE))))
1252 next_flags |= WINED3DCLEAR_TARGET;
1253 flags &= ~WINED3DCLEAR_TARGET;
1254 next_rt_count = rt_count;
1255 rt_count = 0;
1256 break;
1259 /* FIXME: We should reject colour fills on formats with fixups,
1260 * but this would break P8 colour fills for example. */
1264 if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil)
1265 && (!view->format->depth_size || (flags & WINED3DCLEAR_ZBUFFER))
1266 && (!view->format->stencil_size || (flags & WINED3DCLEAR_STENCIL))
1267 && ffp_blitter_use_cpu_clear(view))
1269 next_flags |= flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL);
1270 flags &= ~(WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL);
1273 if (flags)
1275 for (i = 0; i < rt_count; ++i)
1277 if (!(view = fb->render_targets[i]))
1278 continue;
1280 if (previous && (previous->width != view->width || previous->height != view->height))
1281 have_identical_size = FALSE;
1282 previous = view;
1284 if (flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL))
1286 view = fb->depth_stencil;
1288 if (previous && (previous->width != view->width || previous->height != view->height))
1289 have_identical_size = FALSE;
1292 if (have_identical_size)
1294 device_clear_render_targets(device, rt_count, fb, rect_count,
1295 clear_rects, draw_rect, flags, colour, depth, stencil);
1297 else
1299 for (i = 0; i < rt_count; ++i)
1301 if (!(view = fb->render_targets[i]))
1302 continue;
1304 tmp_fb.render_targets[0] = view;
1305 tmp_fb.depth_stencil = NULL;
1306 device_clear_render_targets(device, 1, &tmp_fb, rect_count,
1307 clear_rects, draw_rect, WINED3DCLEAR_TARGET, colour, depth, stencil);
1309 if (flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL))
1311 tmp_fb.render_targets[0] = NULL;
1312 tmp_fb.depth_stencil = fb->depth_stencil;
1313 device_clear_render_targets(device, 0, &tmp_fb, rect_count,
1314 clear_rects, draw_rect, flags & ~WINED3DCLEAR_TARGET, colour, depth, stencil);
1319 if (next_flags && (next = blitter->next))
1320 next->ops->blitter_clear(next, device, next_rt_count, fb, rect_count,
1321 clear_rects, draw_rect, next_flags, colour, depth, stencil);
1324 static DWORD ffp_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
1325 struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1326 DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture,
1327 unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect,
1328 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter)
1330 struct wined3d_texture_gl *src_texture_gl = wined3d_texture_gl(src_texture);
1331 struct wined3d_context_gl *context_gl = wined3d_context_gl(context);
1332 const struct wined3d_gl_info *gl_info = context_gl->gl_info;
1333 struct wined3d_resource *src_resource, *dst_resource;
1334 struct wined3d_texture *staging_texture = NULL;
1335 struct wined3d_color_key old_blt_key;
1336 struct wined3d_device *device;
1337 struct wined3d_blitter *next;
1338 DWORD old_color_key_flags;
1339 RECT r;
1341 src_resource = &src_texture->resource;
1342 dst_resource = &dst_texture->resource;
1343 device = dst_resource->device;
1345 if (!ffp_blit_supported(op, context, src_resource, src_location, dst_resource, dst_location))
1347 if ((next = blitter->next))
1348 return next->ops->blitter_blit(next, op, context, src_texture, src_sub_resource_idx, src_location,
1349 src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect, color_key, filter);
1352 TRACE("Blt from texture %p, %u to rendertarget %p, %u.\n",
1353 src_texture, src_sub_resource_idx, dst_texture, dst_sub_resource_idx);
1355 old_blt_key = src_texture->async.src_blt_color_key;
1356 old_color_key_flags = src_texture->async.color_key_flags;
1357 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT, color_key);
1359 if (!(src_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1361 struct wined3d_resource_desc desc;
1362 struct wined3d_box upload_box;
1363 unsigned int src_level;
1364 HRESULT hr;
1366 TRACE("Source texture is not GPU accessible, creating a staging texture.\n");
1368 src_level = src_sub_resource_idx % src_texture->level_count;
1369 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
1370 desc.format = src_texture->resource.format->id;
1371 desc.multisample_type = src_texture->resource.multisample_type;
1372 desc.multisample_quality = src_texture->resource.multisample_quality;
1373 desc.usage = WINED3DUSAGE_PRIVATE;
1374 desc.bind_flags = 0;
1375 desc.access = WINED3D_RESOURCE_ACCESS_GPU;
1376 desc.width = wined3d_texture_get_level_width(src_texture, src_level);
1377 desc.height = wined3d_texture_get_level_height(src_texture, src_level);
1378 desc.depth = 1;
1379 desc.size = 0;
1381 if (FAILED(hr = wined3d_texture_create(device, &desc, 1, 1, 0,
1382 NULL, NULL, &wined3d_null_parent_ops, &staging_texture)))
1384 ERR("Failed to create staging texture, hr %#x.\n", hr);
1385 return dst_location;
1388 wined3d_box_set(&upload_box, 0, 0, desc.width, desc.height, 0, desc.depth);
1389 wined3d_texture_upload_from_texture(staging_texture, 0, 0, 0, 0,
1390 src_texture, src_sub_resource_idx, &upload_box);
1392 src_texture = staging_texture;
1393 src_texture_gl = wined3d_texture_gl(src_texture);
1394 src_sub_resource_idx = 0;
1396 else
1398 /* Make sure the surface is up-to-date. This should probably use
1399 * surface_load_location() and worry about the destination surface
1400 * too, unless we're overwriting it completely. */
1401 wined3d_texture_load(src_texture, context, FALSE);
1404 wined3d_context_gl_apply_ffp_blit_state(context_gl, device);
1406 if (dst_location == WINED3D_LOCATION_DRAWABLE)
1408 r = *dst_rect;
1409 wined3d_texture_translate_drawable_coords(dst_texture, context_gl->window, &r);
1410 dst_rect = &r;
1413 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
1415 GLenum buffer;
1417 if (dst_location == WINED3D_LOCATION_DRAWABLE)
1419 TRACE("Destination texture %p is onscreen.\n", dst_texture);
1420 buffer = wined3d_texture_get_gl_buffer(dst_texture);
1422 else
1424 TRACE("Destination texture %p is offscreen.\n", dst_texture);
1425 buffer = GL_COLOR_ATTACHMENT0;
1427 wined3d_context_gl_apply_fbo_state_blit(context_gl, GL_DRAW_FRAMEBUFFER,
1428 dst_resource, dst_sub_resource_idx, NULL, 0, dst_location);
1429 wined3d_context_gl_set_draw_buffer(context_gl, buffer);
1430 wined3d_context_gl_check_fbo_status(context_gl, GL_DRAW_FRAMEBUFFER);
1431 context_invalidate_state(context, STATE_FRAMEBUFFER);
1434 gl_info->gl_ops.gl.p_glEnable(src_texture_gl->target);
1435 checkGLcall("glEnable(target)");
1437 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST || color_key)
1439 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
1440 checkGLcall("glEnable(GL_ALPHA_TEST)");
1443 if (color_key)
1445 /* For P8 surfaces, the alpha component contains the palette index.
1446 * Which means that the colorkey is one of the palette entries. In
1447 * other cases pixels that should be masked away have alpha set to 0. */
1448 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT)
1449 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
1450 (float)src_texture->async.src_blt_color_key.color_space_low_value / 255.0f);
1451 else
1452 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
1453 checkGLcall("glAlphaFunc");
1456 wined3d_context_gl_draw_textured_quad(context_gl, src_texture_gl,
1457 src_sub_resource_idx, src_rect, dst_rect, filter);
1459 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST || color_key)
1461 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
1462 checkGLcall("glDisable(GL_ALPHA_TEST)");
1465 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
1466 checkGLcall("glDisable(GL_TEXTURE_2D)");
1467 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
1469 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
1470 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
1472 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
1474 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
1475 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
1478 if (dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture)
1479 gl_info->gl_ops.gl.p_glFlush();
1481 /* Restore the color key parameters */
1482 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT,
1483 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
1485 if (staging_texture)
1486 wined3d_texture_decref(staging_texture);
1488 return dst_location;
1491 static const struct wined3d_blitter_ops ffp_blitter_ops =
1493 ffp_blitter_destroy,
1494 ffp_blitter_clear,
1495 ffp_blitter_blit,
1498 void wined3d_ffp_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
1500 struct wined3d_blitter *blitter;
1502 if (!(blitter = heap_alloc(sizeof(*blitter))))
1503 return;
1505 TRACE("Created blitter %p.\n", blitter);
1507 blitter->ops = &ffp_blitter_ops;
1508 blitter->next = *next;
1509 *next = blitter;
1512 /* Context activation is done by the caller. */
1513 static void cpu_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
1515 struct wined3d_blitter *next;
1517 if ((next = blitter->next))
1518 next->ops->blitter_destroy(next, context);
1520 heap_free(blitter);
1523 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
1524 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
1525 const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx)
1527 UINT row_block_count;
1528 const BYTE *src_row;
1529 BYTE *dst_row;
1530 UINT x, y;
1532 src_row = src_data;
1533 dst_row = dst_data;
1535 row_block_count = (update_w + format->block_width - 1) / format->block_width;
1537 if (!flags)
1539 for (y = 0; y < update_h; y += format->block_height)
1541 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
1542 src_row += src_pitch;
1543 dst_row += dst_pitch;
1546 return WINED3D_OK;
1549 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
1551 src_row += (((update_h / format->block_height) - 1) * src_pitch);
1553 switch (format->id)
1555 case WINED3DFMT_DXT1:
1556 for (y = 0; y < update_h; y += format->block_height)
1558 struct block
1560 WORD color[2];
1561 BYTE control_row[4];
1564 const struct block *s = (const struct block *)src_row;
1565 struct block *d = (struct block *)dst_row;
1567 for (x = 0; x < row_block_count; ++x)
1569 d[x].color[0] = s[x].color[0];
1570 d[x].color[1] = s[x].color[1];
1571 d[x].control_row[0] = s[x].control_row[3];
1572 d[x].control_row[1] = s[x].control_row[2];
1573 d[x].control_row[2] = s[x].control_row[1];
1574 d[x].control_row[3] = s[x].control_row[0];
1576 src_row -= src_pitch;
1577 dst_row += dst_pitch;
1579 return WINED3D_OK;
1581 case WINED3DFMT_DXT2:
1582 case WINED3DFMT_DXT3:
1583 for (y = 0; y < update_h; y += format->block_height)
1585 struct block
1587 WORD alpha_row[4];
1588 WORD color[2];
1589 BYTE control_row[4];
1592 const struct block *s = (const struct block *)src_row;
1593 struct block *d = (struct block *)dst_row;
1595 for (x = 0; x < row_block_count; ++x)
1597 d[x].alpha_row[0] = s[x].alpha_row[3];
1598 d[x].alpha_row[1] = s[x].alpha_row[2];
1599 d[x].alpha_row[2] = s[x].alpha_row[1];
1600 d[x].alpha_row[3] = s[x].alpha_row[0];
1601 d[x].color[0] = s[x].color[0];
1602 d[x].color[1] = s[x].color[1];
1603 d[x].control_row[0] = s[x].control_row[3];
1604 d[x].control_row[1] = s[x].control_row[2];
1605 d[x].control_row[2] = s[x].control_row[1];
1606 d[x].control_row[3] = s[x].control_row[0];
1608 src_row -= src_pitch;
1609 dst_row += dst_pitch;
1611 return WINED3D_OK;
1613 default:
1614 FIXME("Compressed flip not implemented for format %s.\n",
1615 debug_d3dformat(format->id));
1616 return E_NOTIMPL;
1620 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
1621 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
1623 return E_NOTIMPL;
1626 static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
1627 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1628 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
1629 enum wined3d_texture_filter_type filter)
1631 unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count;
1632 struct wined3d_device *device = dst_texture->resource.device;
1633 const struct wined3d_format *src_format, *dst_format;
1634 struct wined3d_texture *converted_texture = NULL;
1635 struct wined3d_bo_address src_data, dst_data;
1636 unsigned int src_fmt_flags, dst_fmt_flags;
1637 struct wined3d_map_desc dst_map, src_map;
1638 unsigned int x, sx, xinc, y, sy, yinc;
1639 struct wined3d_context *context;
1640 struct wined3d_range dst_range;
1641 unsigned int texture_level;
1642 HRESULT hr = WINED3D_OK;
1643 BOOL same_sub_resource;
1644 BOOL upload = FALSE;
1645 DWORD map_binding;
1646 const BYTE *sbase;
1647 const BYTE *sbuf;
1648 BYTE *dbuf;
1650 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
1651 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
1652 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture,
1653 src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
1655 context = context_acquire(device, NULL, 0);
1657 src_format = src_texture->resource.format;
1658 dst_format = dst_texture->resource.format;
1660 if (wined3d_format_is_typeless(src_format) && src_format->id == dst_format->typeless_id)
1661 src_format = dst_format;
1662 if (wined3d_format_is_typeless(dst_format) && dst_format->id == src_format->typeless_id)
1663 dst_format = src_format;
1665 src_height = src_box->bottom - src_box->top;
1666 src_width = src_box->right - src_box->left;
1667 dst_height = dst_box->bottom - dst_box->top;
1668 dst_width = dst_box->right - dst_box->left;
1670 dst_range.offset = 0;
1671 dst_range.size = dst_texture->sub_resources[dst_sub_resource_idx].size;
1672 if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx)
1674 same_sub_resource = TRUE;
1676 map_binding = dst_texture->resource.map_binding;
1677 texture_level = dst_sub_resource_idx % dst_texture->level_count;
1678 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
1679 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
1680 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
1681 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
1682 wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
1683 dst_map.data = wined3d_context_map_bo_address(context, &dst_data,
1684 dst_texture->sub_resources[dst_sub_resource_idx].size, 0, WINED3D_MAP_READ | WINED3D_MAP_WRITE);
1686 src_map = dst_map;
1688 else
1690 same_sub_resource = FALSE;
1691 upload = dst_format->flags[dst_texture->resource.gl_type] & WINED3DFMT_FLAG_BLOCKS
1692 && (dst_width != src_width || dst_height != src_height);
1694 if (upload)
1696 dst_format = src_format->flags[dst_texture->resource.gl_type] & WINED3DFMT_FLAG_BLOCKS
1697 ? wined3d_get_format(device->adapter, WINED3DFMT_B8G8R8A8_UNORM, 0) : src_format;
1700 if (!(flags & WINED3D_BLT_RAW) && dst_format->id != src_format->id)
1702 if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format)))
1704 FIXME("Cannot convert %s to %s.\n", debug_d3dformat(src_format->id),
1705 debug_d3dformat(dst_format->id));
1706 context_release(context);
1707 return WINED3DERR_NOTAVAILABLE;
1709 src_texture = converted_texture;
1710 src_sub_resource_idx = 0;
1711 src_format = src_texture->resource.format;
1714 map_binding = src_texture->resource.map_binding;
1715 texture_level = src_sub_resource_idx % src_texture->level_count;
1716 if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, map_binding))
1717 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
1718 wined3d_texture_get_pitch(src_texture, texture_level, &src_map.row_pitch, &src_map.slice_pitch);
1719 wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &src_data, map_binding);
1720 src_map.data = wined3d_context_map_bo_address(context, &src_data,
1721 src_texture->sub_resources[src_sub_resource_idx].size, 0, WINED3D_MAP_READ);
1723 if (upload)
1725 wined3d_format_calculate_pitch(dst_format, 1, dst_box->right, dst_box->bottom,
1726 &dst_map.row_pitch, &dst_map.slice_pitch);
1727 dst_map.data = heap_alloc(dst_map.slice_pitch);
1729 else
1731 map_binding = dst_texture->resource.map_binding;
1732 texture_level = dst_sub_resource_idx % dst_texture->level_count;
1733 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
1734 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
1736 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
1737 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
1738 wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
1739 dst_map.data = wined3d_context_map_bo_address(context, &dst_data,
1740 dst_texture->sub_resources[dst_sub_resource_idx].size, 0, WINED3D_MAP_WRITE);
1743 src_fmt_flags = src_format->flags[src_texture->resource.gl_type];
1744 dst_fmt_flags = dst_format->flags[dst_texture->resource.gl_type];
1745 flags &= ~WINED3D_BLT_RAW;
1747 bpp = dst_format->byte_count;
1748 row_byte_count = dst_width * bpp;
1750 sbase = (BYTE *)src_map.data
1751 + ((src_box->top / src_format->block_height) * src_map.row_pitch)
1752 + ((src_box->left / src_format->block_width) * src_format->block_byte_count);
1753 dbuf = (BYTE *)dst_map.data
1754 + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch)
1755 + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count);
1757 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
1759 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
1761 if (same_sub_resource)
1763 FIXME("Only plain blits supported on compressed surfaces.\n");
1764 hr = E_NOTIMPL;
1765 goto release;
1768 hr = surface_cpu_blt_compressed(sbase, dbuf,
1769 src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height,
1770 src_format, flags, fx);
1771 goto release;
1774 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
1775 && (src_width != dst_width || src_height != dst_height))
1777 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
1778 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
1781 xinc = (src_width << 16) / dst_width;
1782 yinc = (src_height << 16) / dst_height;
1784 if (!flags)
1786 /* No effects, we can cheat here. */
1787 if (dst_width == src_width)
1789 if (dst_height == src_height)
1791 /* No stretching in either direction. This needs to be as fast
1792 * as possible. */
1793 sbuf = sbase;
1795 /* Check for overlapping surfaces. */
1796 if (!same_sub_resource || dst_box->top < src_box->top
1797 || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
1799 /* No overlap, or dst above src, so copy from top downwards. */
1800 for (y = 0; y < dst_height; ++y)
1802 memcpy(dbuf, sbuf, row_byte_count);
1803 sbuf += src_map.row_pitch;
1804 dbuf += dst_map.row_pitch;
1807 else if (dst_box->top > src_box->top)
1809 /* Copy from bottom upwards. */
1810 sbuf += src_map.row_pitch * dst_height;
1811 dbuf += dst_map.row_pitch * dst_height;
1812 for (y = 0; y < dst_height; ++y)
1814 sbuf -= src_map.row_pitch;
1815 dbuf -= dst_map.row_pitch;
1816 memcpy(dbuf, sbuf, row_byte_count);
1819 else
1821 /* Src and dst overlapping on the same line, use memmove. */
1822 for (y = 0; y < dst_height; ++y)
1824 memmove(dbuf, sbuf, row_byte_count);
1825 sbuf += src_map.row_pitch;
1826 dbuf += dst_map.row_pitch;
1830 else
1832 /* Stretching in y direction only. */
1833 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
1835 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
1836 memcpy(dbuf, sbuf, row_byte_count);
1837 dbuf += dst_map.row_pitch;
1841 else
1843 /* Stretching in X direction. */
1844 unsigned int last_sy = ~0u;
1845 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
1847 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
1849 if ((sy >> 16) == (last_sy >> 16))
1851 /* This source row is the same as last source row -
1852 * Copy the already stretched row. */
1853 memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count);
1855 else
1857 #define STRETCH_ROW(type) \
1858 do { \
1859 const type *s = (const type *)sbuf; \
1860 type *d = (type *)dbuf; \
1861 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
1862 d[x] = s[sx >> 16]; \
1863 } while(0)
1865 switch(bpp)
1867 case 1:
1868 STRETCH_ROW(BYTE);
1869 break;
1870 case 2:
1871 STRETCH_ROW(WORD);
1872 break;
1873 case 4:
1874 STRETCH_ROW(DWORD);
1875 break;
1876 case 3:
1878 const BYTE *s;
1879 BYTE *d = dbuf;
1880 for (x = sx = 0; x < dst_width; x++, sx+= xinc)
1882 DWORD pixel;
1884 s = sbuf + 3 * (sx >> 16);
1885 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
1886 d[0] = (pixel ) & 0xff;
1887 d[1] = (pixel >> 8) & 0xff;
1888 d[2] = (pixel >> 16) & 0xff;
1889 d += 3;
1891 break;
1893 default:
1894 FIXME("Stretched blit not implemented for bpp %u.\n", bpp * 8);
1895 hr = WINED3DERR_NOTAVAILABLE;
1896 goto error;
1898 #undef STRETCH_ROW
1900 dbuf += dst_map.row_pitch;
1901 last_sy = sy;
1905 else
1907 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
1908 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
1909 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
1910 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
1911 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
1913 /* The color keying flags are checked for correctness in ddraw. */
1914 if (flags & WINED3D_BLT_SRC_CKEY)
1916 keylow = src_texture->async.src_blt_color_key.color_space_low_value;
1917 keyhigh = src_texture->async.src_blt_color_key.color_space_high_value;
1919 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
1921 keylow = fx->src_color_key.color_space_low_value;
1922 keyhigh = fx->src_color_key.color_space_high_value;
1925 if (flags & WINED3D_BLT_DST_CKEY)
1927 /* Destination color keys are taken from the source surface! */
1928 destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value;
1929 destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value;
1931 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
1933 destkeylow = fx->dst_color_key.color_space_low_value;
1934 destkeyhigh = fx->dst_color_key.color_space_high_value;
1937 if (bpp == 1)
1939 keymask = 0xff;
1941 else
1943 DWORD masks[3];
1944 get_color_masks(src_format, masks);
1945 keymask = masks[0] | masks[1] | masks[2];
1947 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
1948 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
1951 if (flags & WINED3D_BLT_FX)
1953 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
1954 LONG tmpxy;
1955 dTopLeft = dbuf;
1956 dTopRight = dbuf + ((dst_width - 1) * bpp);
1957 dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
1958 dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
1960 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
1962 /* I don't think we need to do anything about this flag. */
1963 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
1965 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
1967 tmp = dTopRight;
1968 dTopRight = dTopLeft;
1969 dTopLeft = tmp;
1970 tmp = dBottomRight;
1971 dBottomRight = dBottomLeft;
1972 dBottomLeft = tmp;
1973 dstxinc = dstxinc * -1;
1975 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
1977 tmp = dTopLeft;
1978 dTopLeft = dBottomLeft;
1979 dBottomLeft = tmp;
1980 tmp = dTopRight;
1981 dTopRight = dBottomRight;
1982 dBottomRight = tmp;
1983 dstyinc = dstyinc * -1;
1985 if (fx->fx & WINEDDBLTFX_NOTEARING)
1987 /* I don't think we need to do anything about this flag. */
1988 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
1990 if (fx->fx & WINEDDBLTFX_ROTATE180)
1992 tmp = dBottomRight;
1993 dBottomRight = dTopLeft;
1994 dTopLeft = tmp;
1995 tmp = dBottomLeft;
1996 dBottomLeft = dTopRight;
1997 dTopRight = tmp;
1998 dstxinc = dstxinc * -1;
1999 dstyinc = dstyinc * -1;
2001 if (fx->fx & WINEDDBLTFX_ROTATE270)
2003 tmp = dTopLeft;
2004 dTopLeft = dBottomLeft;
2005 dBottomLeft = dBottomRight;
2006 dBottomRight = dTopRight;
2007 dTopRight = tmp;
2008 tmpxy = dstxinc;
2009 dstxinc = dstyinc;
2010 dstyinc = tmpxy;
2011 dstxinc = dstxinc * -1;
2013 if (fx->fx & WINEDDBLTFX_ROTATE90)
2015 tmp = dTopLeft;
2016 dTopLeft = dTopRight;
2017 dTopRight = dBottomRight;
2018 dBottomRight = dBottomLeft;
2019 dBottomLeft = tmp;
2020 tmpxy = dstxinc;
2021 dstxinc = dstyinc;
2022 dstyinc = tmpxy;
2023 dstyinc = dstyinc * -1;
2025 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
2027 /* I don't think we need to do anything about this flag. */
2028 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
2030 dbuf = dTopLeft;
2031 flags &= ~(WINED3D_BLT_FX);
2034 #define COPY_COLORKEY_FX(type) \
2035 do { \
2036 const type *s; \
2037 type *d = (type *)dbuf, *dx, tmp; \
2038 for (y = sy = 0; y < dst_height; ++y, sy += yinc) \
2040 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
2041 dx = d; \
2042 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
2044 tmp = s[sx >> 16]; \
2045 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
2046 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
2048 dx[0] = tmp; \
2050 dx = (type *)(((BYTE *)dx) + dstxinc); \
2052 d = (type *)(((BYTE *)d) + dstyinc); \
2054 } while(0)
2056 switch (bpp)
2058 case 1:
2059 COPY_COLORKEY_FX(BYTE);
2060 break;
2061 case 2:
2062 COPY_COLORKEY_FX(WORD);
2063 break;
2064 case 4:
2065 COPY_COLORKEY_FX(DWORD);
2066 break;
2067 case 3:
2069 const BYTE *s;
2070 BYTE *d = dbuf, *dx;
2071 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
2073 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
2074 dx = d;
2075 for (x = sx = 0; x < dst_width; ++x, sx+= xinc)
2077 DWORD pixel, dpixel = 0;
2078 s = sbuf + 3 * (sx>>16);
2079 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
2080 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
2081 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
2082 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
2084 dx[0] = (pixel ) & 0xff;
2085 dx[1] = (pixel >> 8) & 0xff;
2086 dx[2] = (pixel >> 16) & 0xff;
2088 dx += dstxinc;
2090 d += dstyinc;
2092 break;
2094 default:
2095 FIXME("%s color-keyed blit not implemented for bpp %u.\n",
2096 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
2097 hr = WINED3DERR_NOTAVAILABLE;
2098 goto error;
2099 #undef COPY_COLORKEY_FX
2103 error:
2104 if (flags)
2105 FIXME(" Unsupported flags %#x.\n", flags);
2107 release:
2108 if (upload && hr == WINED3D_OK)
2110 struct wined3d_bo_address data;
2112 data.buffer_object = 0;
2113 data.addr = dst_map.data;
2115 texture_level = dst_sub_resource_idx % dst_texture->level_count;
2117 wined3d_texture_prepare_location(dst_texture, texture_level, context, WINED3D_LOCATION_TEXTURE_RGB);
2118 dst_texture->texture_ops->texture_upload_data(context, wined3d_const_bo_address(&data), dst_format,
2119 dst_box, dst_map.row_pitch, dst_map.slice_pitch, dst_texture, texture_level,
2120 WINED3D_LOCATION_TEXTURE_RGB, dst_box->left, dst_box->top, 0);
2122 wined3d_texture_validate_location(dst_texture, texture_level, WINED3D_LOCATION_TEXTURE_RGB);
2123 wined3d_texture_invalidate_location(dst_texture, texture_level, ~WINED3D_LOCATION_TEXTURE_RGB);
2126 if (upload)
2128 heap_free(dst_map.data);
2130 else
2132 wined3d_context_unmap_bo_address(context, &dst_data, 0, 1, &dst_range);
2135 if (!same_sub_resource)
2136 wined3d_context_unmap_bo_address(context, &src_data, 0, 0, NULL);
2137 if (SUCCEEDED(hr) && dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture)
2139 SetRect(&dst_texture->swapchain->front_buffer_update,
2140 dst_box->left, dst_box->top, dst_box->right, dst_box->bottom);
2141 dst_texture->swapchain->swapchain_ops->swapchain_frontbuffer_updated(dst_texture->swapchain);
2143 if (converted_texture)
2144 wined3d_texture_decref(converted_texture);
2145 context_release(context);
2147 return hr;
2150 static void surface_cpu_blt_colour_fill(struct wined3d_rendertarget_view *view,
2151 const struct wined3d_box *box, const struct wined3d_color *colour)
2153 struct wined3d_device *device = view->resource->device;
2154 unsigned int x, y, z, w, h, d, bpp, level;
2155 struct wined3d_context *context;
2156 struct wined3d_texture *texture;
2157 struct wined3d_bo_address data;
2158 struct wined3d_map_desc map;
2159 struct wined3d_range range;
2160 DWORD map_binding;
2161 uint8_t *dst;
2162 DWORD c;
2164 TRACE("view %p, box %s, colour %s.\n", view, debug_box(box), debug_color(colour));
2166 if (view->format_flags & WINED3DFMT_FLAG_BLOCKS)
2168 FIXME("Not implemented for format %s.\n", debug_d3dformat(view->format->id));
2169 return;
2172 if (view->format->id != view->resource->format->id)
2173 FIXME("View format %s doesn't match resource format %s.\n",
2174 debug_d3dformat(view->format->id), debug_d3dformat(view->resource->format->id));
2176 if (view->resource->type == WINED3D_RTYPE_BUFFER)
2178 FIXME("Not implemented for buffers.\n");
2179 return;
2182 context = context_acquire(device, NULL, 0);
2184 texture = texture_from_resource(view->resource);
2185 level = view->sub_resource_idx % texture->level_count;
2187 c = wined3d_format_convert_from_float(view->format, colour);
2188 bpp = view->format->byte_count;
2189 w = min(box->right, view->width) - min(box->left, view->width);
2190 h = min(box->bottom, view->height) - min(box->top, view->height);
2191 if (view->resource->type != WINED3D_RTYPE_TEXTURE_3D)
2193 d = 1;
2195 else
2197 d = wined3d_texture_get_level_depth(texture, level);
2198 d = min(box->back, d) - min(box->front, d);
2201 map_binding = texture->resource.map_binding;
2202 if (!wined3d_texture_load_location(texture, view->sub_resource_idx, context, map_binding))
2203 ERR("Failed to load the sub-resource into %s.\n", wined3d_debug_location(map_binding));
2204 wined3d_texture_invalidate_location(texture, view->sub_resource_idx, ~map_binding);
2205 wined3d_texture_get_pitch(texture, level, &map.row_pitch, &map.slice_pitch);
2206 wined3d_texture_get_memory(texture, view->sub_resource_idx, &data, map_binding);
2207 map.data = wined3d_context_map_bo_address(context, &data,
2208 texture->sub_resources[view->sub_resource_idx].size, 0, WINED3D_MAP_WRITE);
2209 map.data = (BYTE *)map.data
2210 + (box->front * map.slice_pitch)
2211 + ((box->top / view->format->block_height) * map.row_pitch)
2212 + ((box->left / view->format->block_width) * view->format->block_byte_count);
2213 range.offset = 0;
2214 range.size = texture->sub_resources[view->sub_resource_idx].size;
2216 switch (bpp)
2218 case 1:
2219 for (x = 0; x < w; ++x)
2221 ((BYTE *)map.data)[x] = c;
2223 break;
2225 case 2:
2226 for (x = 0; x < w; ++x)
2228 ((WORD *)map.data)[x] = c;
2230 break;
2232 case 3:
2234 dst = map.data;
2235 for (x = 0; x < w; ++x, dst += 3)
2237 dst[0] = (c ) & 0xff;
2238 dst[1] = (c >> 8) & 0xff;
2239 dst[2] = (c >> 16) & 0xff;
2241 break;
2243 case 4:
2244 for (x = 0; x < w; ++x)
2246 ((DWORD *)map.data)[x] = c;
2248 break;
2250 default:
2251 FIXME("Not implemented for bpp %u.\n", bpp);
2252 wined3d_resource_unmap(view->resource, view->sub_resource_idx);
2253 return;
2256 dst = map.data;
2257 for (y = 1; y < h; ++y)
2259 dst += map.row_pitch;
2260 memcpy(dst, map.data, w * bpp);
2263 dst = map.data;
2264 for (z = 1; z < d; ++z)
2266 dst += map.slice_pitch;
2267 memcpy(dst, map.data, w * h * bpp);
2270 wined3d_context_unmap_bo_address(context, &data, 0, 1, &range);
2271 context_release(context);
2274 static void cpu_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
2275 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
2276 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
2278 struct wined3d_color c = {depth, 0.0f, 0.0f, 0.0f};
2279 struct wined3d_rendertarget_view *view;
2280 struct wined3d_box box;
2281 unsigned int i, j;
2283 if (!rect_count)
2285 rect_count = 1;
2286 clear_rects = draw_rect;
2289 for (i = 0; i < rect_count; ++i)
2291 box.left = max(clear_rects[i].left, draw_rect->left);
2292 box.top = max(clear_rects[i].top, draw_rect->top);
2293 box.right = min(clear_rects[i].right, draw_rect->right);
2294 box.bottom = min(clear_rects[i].bottom, draw_rect->bottom);
2295 box.front = 0;
2296 box.back = ~0u;
2298 if (box.left >= box.right || box.top >= box.bottom)
2299 continue;
2301 if (flags & WINED3DCLEAR_TARGET)
2303 for (j = 0; j < rt_count; ++j)
2305 if ((view = fb->render_targets[j]))
2306 surface_cpu_blt_colour_fill(view, &box, colour);
2310 if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil))
2312 if ((view->format->depth_size && !(flags & WINED3DCLEAR_ZBUFFER))
2313 || (view->format->stencil_size && !(flags & WINED3DCLEAR_STENCIL)))
2314 FIXME("Clearing %#x on %s.\n", flags, debug_d3dformat(view->format->id));
2316 surface_cpu_blt_colour_fill(view, &box, &c);
2321 static DWORD cpu_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
2322 struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
2323 DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture,
2324 unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect,
2325 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter)
2327 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
2328 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
2329 struct wined3d_blt_fx fx;
2330 DWORD flags = 0;
2332 memset(&fx, 0, sizeof(fx));
2333 switch (op)
2335 case WINED3D_BLIT_OP_COLOR_BLIT:
2336 case WINED3D_BLIT_OP_DEPTH_BLIT:
2337 break;
2338 case WINED3D_BLIT_OP_RAW_BLIT:
2339 flags |= WINED3D_BLT_RAW;
2340 break;
2341 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
2342 flags |= WINED3D_BLT_ALPHA_TEST;
2343 break;
2344 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
2345 flags |= WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_FX;
2346 fx.src_color_key = *color_key;
2347 break;
2348 default:
2349 FIXME("Unhandled op %#x.\n", op);
2350 break;
2353 if (FAILED(surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
2354 src_texture, src_sub_resource_idx, &src_box, flags, &fx, filter)))
2355 ERR("Failed to blit.\n");
2356 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
2358 return dst_location | (dst_texture->sub_resources[dst_sub_resource_idx].locations
2359 & dst_texture->resource.map_binding);
2362 static const struct wined3d_blitter_ops cpu_blitter_ops =
2364 cpu_blitter_destroy,
2365 cpu_blitter_clear,
2366 cpu_blitter_blit,
2369 struct wined3d_blitter *wined3d_cpu_blitter_create(void)
2371 struct wined3d_blitter *blitter;
2373 if (!(blitter = heap_alloc(sizeof(*blitter))))
2374 return NULL;
2376 TRACE("Created blitter %p.\n", blitter);
2378 blitter->ops = &cpu_blitter_ops;
2379 blitter->next = NULL;
2381 return blitter;
2384 HRESULT texture2d_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
2385 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
2386 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
2387 enum wined3d_texture_filter_type filter)
2389 struct wined3d_texture_sub_resource *src_sub_resource, *dst_sub_resource;
2390 struct wined3d_device *device = dst_texture->resource.device;
2391 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
2392 const struct wined3d_color_key *colour_key = NULL;
2393 DWORD src_location, dst_location, valid_locations;
2394 DWORD src_ds_flags, dst_ds_flags;
2395 struct wined3d_context *context;
2396 enum wined3d_blit_op blit_op;
2397 BOOL scale, convert, resolve;
2398 RECT src_rect, dst_rect;
2400 static const DWORD simple_blit = WINED3D_BLT_SRC_CKEY
2401 | WINED3D_BLT_SRC_CKEY_OVERRIDE
2402 | WINED3D_BLT_ALPHA_TEST
2403 | WINED3D_BLT_RAW;
2405 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
2406 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
2407 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture, src_sub_resource_idx,
2408 debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
2409 TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage));
2411 if (fx)
2413 TRACE("fx %#x.\n", fx->fx);
2414 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
2415 fx->dst_color_key.color_space_low_value,
2416 fx->dst_color_key.color_space_high_value);
2417 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
2418 fx->src_color_key.color_space_low_value,
2419 fx->src_color_key.color_space_high_value);
2422 dst_sub_resource = &dst_texture->sub_resources[dst_sub_resource_idx];
2423 src_sub_resource = &src_texture->sub_resources[src_sub_resource_idx];
2425 if (src_sub_resource->locations & WINED3D_LOCATION_DISCARDED)
2427 WARN("Source sub-resource is discarded, nothing to do.\n");
2428 return WINED3D_OK;
2431 SetRect(&src_rect, src_box->left, src_box->top, src_box->right, src_box->bottom);
2432 SetRect(&dst_rect, dst_box->left, dst_box->top, dst_box->right, dst_box->bottom);
2434 if (!fx || !(fx->fx))
2435 flags &= ~WINED3D_BLT_FX;
2437 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
2438 if (flags & WINED3D_BLT_DO_NOT_WAIT)
2440 static unsigned int once;
2442 if (!once++)
2443 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
2446 flags &= ~(WINED3D_BLT_SYNCHRONOUS | WINED3D_BLT_DO_NOT_WAIT | WINED3D_BLT_WAIT);
2448 if (!device->d3d_initialized)
2450 WARN("D3D not initialized, using CPU blit fallback.\n");
2451 goto cpu;
2454 if (flags & ~simple_blit)
2456 WARN_(d3d_perf)("Using CPU fallback for complex blit (%#x).\n", flags);
2457 goto cpu;
2460 src_swapchain = src_texture->swapchain;
2461 dst_swapchain = dst_texture->swapchain;
2463 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain
2464 && (wined3d_settings.offscreen_rendering_mode != ORM_FBO
2465 || src_texture == src_swapchain->front_buffer))
2467 /* TODO: We could support cross-swapchain blits by first downloading
2468 * the source to a texture. */
2469 FIXME("Cross-swapchain blit not supported.\n");
2470 return WINED3DERR_INVALIDCALL;
2473 scale = src_box->right - src_box->left != dst_box->right - dst_box->left
2474 || src_box->bottom - src_box->top != dst_box->bottom - dst_box->top;
2475 convert = src_texture->resource.format->id != dst_texture->resource.format->id;
2476 resolve = src_texture->resource.multisample_type != dst_texture->resource.multisample_type;
2478 dst_ds_flags = dst_texture->resource.format_flags
2479 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
2480 src_ds_flags = src_texture->resource.format_flags
2481 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
2483 if (src_ds_flags || dst_ds_flags)
2485 TRACE("Depth/stencil blit.\n");
2487 if (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)
2488 dst_location = dst_texture->resource.draw_binding;
2489 else
2490 dst_location = dst_texture->resource.map_binding;
2492 if ((flags & WINED3D_BLT_RAW) || (!scale && !convert && !resolve))
2493 blit_op = WINED3D_BLIT_OP_RAW_BLIT;
2494 else
2495 blit_op = WINED3D_BLIT_OP_DEPTH_BLIT;
2497 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
2498 valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context,
2499 src_texture, src_sub_resource_idx, src_texture->resource.draw_binding, &src_rect,
2500 dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, NULL, filter);
2501 context_release(context);
2503 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
2504 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
2506 return WINED3D_OK;
2509 TRACE("Colour blit.\n");
2511 /* In principle this would apply to depth blits as well, but we don't
2512 * implement those in the CPU blitter at the moment. */
2513 if ((dst_sub_resource->locations & dst_texture->resource.map_binding)
2514 && (src_sub_resource->locations & src_texture->resource.map_binding))
2516 if (scale)
2517 TRACE("Not doing sysmem blit because of scaling.\n");
2518 else if (convert)
2519 TRACE("Not doing sysmem blit because of format conversion.\n");
2520 else
2521 goto cpu;
2524 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
2525 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
2527 colour_key = &fx->src_color_key;
2528 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
2530 else if (flags & WINED3D_BLT_SRC_CKEY)
2532 colour_key = &src_texture->async.src_blt_color_key;
2533 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
2535 else if (flags & WINED3D_BLT_ALPHA_TEST)
2537 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
2539 else if ((src_sub_resource->locations & surface_simple_locations)
2540 && !(dst_sub_resource->locations & surface_simple_locations))
2542 /* Upload */
2543 if (scale)
2544 TRACE("Not doing upload because of scaling.\n");
2545 else if (convert)
2546 TRACE("Not doing upload because of format conversion.\n");
2547 else if (dst_texture->resource.format->conv_byte_count)
2548 TRACE("Not doing upload because the destination format needs conversion.\n");
2549 else
2551 wined3d_texture_upload_from_texture(dst_texture, dst_sub_resource_idx, dst_box->left,
2552 dst_box->top, dst_box->front, src_texture, src_sub_resource_idx, src_box);
2553 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
2555 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
2556 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx,
2557 context, dst_texture->resource.draw_binding);
2558 context_release(context);
2560 return WINED3D_OK;
2563 else if (!(src_sub_resource->locations & surface_simple_locations)
2564 && (dst_sub_resource->locations & dst_texture->resource.map_binding)
2565 && !(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
2567 /* Download */
2568 if (scale)
2569 TRACE("Not doing download because of scaling.\n");
2570 else if (convert)
2571 TRACE("Not doing download because of format conversion.\n");
2572 else if (src_texture->resource.format->conv_byte_count)
2573 TRACE("Not doing download because the source format needs conversion.\n");
2574 else if (!(src_texture->flags & WINED3D_TEXTURE_DOWNLOADABLE))
2575 TRACE("Not doing download because texture is not downloadable.\n");
2576 else if (!texture2d_is_full_rect(src_texture, src_sub_resource_idx % src_texture->level_count, &src_rect))
2577 TRACE("Not doing download because of partial download (src).\n");
2578 else if (!texture2d_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, &dst_rect))
2579 TRACE("Not doing download because of partial download (dst).\n");
2580 else
2582 wined3d_texture_download_from_texture(dst_texture, dst_sub_resource_idx, src_texture,
2583 src_sub_resource_idx);
2584 return WINED3D_OK;
2587 else if (dst_swapchain && dst_swapchain->back_buffers
2588 && dst_texture == dst_swapchain->front_buffer
2589 && src_texture == dst_swapchain->back_buffers[0])
2591 /* Use present for back -> front blits. The idea behind this is that
2592 * present is potentially faster than a blit, in particular when FBO
2593 * blits aren't available. Some ddraw applications like Half-Life and
2594 * Prince of Persia 3D use Blt() from the backbuffer to the
2595 * frontbuffer instead of doing a Flip(). D3d8 and d3d9 applications
2596 * can't blit directly to the frontbuffer. */
2597 enum wined3d_swap_effect swap_effect = dst_swapchain->state.desc.swap_effect;
2599 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
2601 /* Set the swap effect to COPY, we don't want the backbuffer to become
2602 * undefined. */
2603 dst_swapchain->state.desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
2604 wined3d_swapchain_present(dst_swapchain, NULL, NULL,
2605 dst_swapchain->win_handle, dst_swapchain->swap_interval, 0);
2606 dst_swapchain->state.desc.swap_effect = swap_effect;
2608 return WINED3D_OK;
2611 if ((flags & WINED3D_BLT_RAW) || (blit_op == WINED3D_BLIT_OP_COLOR_BLIT && !scale && !convert && !resolve))
2612 blit_op = WINED3D_BLIT_OP_RAW_BLIT;
2614 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
2616 if (src_texture->resource.multisample_type != WINED3D_MULTISAMPLE_NONE
2617 && ((scale && !context->d3d_info->scaled_resolve)
2618 || convert || blit_op != WINED3D_BLIT_OP_COLOR_BLIT))
2619 src_location = WINED3D_LOCATION_RB_RESOLVED;
2620 else
2621 src_location = src_texture->resource.draw_binding;
2623 if (!(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
2624 dst_location = dst_texture->resource.map_binding;
2625 else if (dst_texture->resource.multisample_type != WINED3D_MULTISAMPLE_NONE
2626 && (scale || convert || blit_op != WINED3D_BLIT_OP_COLOR_BLIT))
2627 dst_location = WINED3D_LOCATION_RB_RESOLVED;
2628 else
2629 dst_location = dst_texture->resource.draw_binding;
2631 valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context,
2632 src_texture, src_sub_resource_idx, src_location, &src_rect,
2633 dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, colour_key, filter);
2635 context_release(context);
2637 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
2638 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
2640 return WINED3D_OK;
2642 cpu:
2643 return surface_cpu_blt(dst_texture, dst_sub_resource_idx, dst_box,
2644 src_texture, src_sub_resource_idx, src_box, flags, fx, filter);