d2d1: Partially implement d2d_device_context_DrawImage().
[wine.git] / dlls / wined3d / surface.c
blobd5d07d8c4010a33aefaf67b32a87968c4114d5f3
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 | WINED3D_LOCATION_BUFFER;
38 /* Works correctly only for <= 4 bpp formats. */
39 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
41 masks[0] = ((1u << format->red_size) - 1) << format->red_offset;
42 masks[1] = ((1u << format->green_size) - 1) << format->green_offset;
43 masks[2] = ((1u << format->blue_size) - 1) << format->blue_offset;
46 /* See also float_16_to_32() in wined3d_private.h */
47 static inline unsigned short float_32_to_16(const float *in)
49 int exp = 0;
50 float tmp = fabsf(*in);
51 unsigned int mantissa;
52 unsigned short ret;
54 /* Deal with special numbers */
55 if (*in == 0.0f)
56 return 0x0000;
57 if (isnan(*in))
58 return 0x7c01;
59 if (isinf(*in))
60 return (*in < 0.0f ? 0xfc00 : 0x7c00);
62 if (tmp < (float)(1u << 10))
66 tmp = tmp * 2.0f;
67 exp--;
68 } while (tmp < (float)(1u << 10));
70 else if (tmp >= (float)(1u << 11))
74 tmp /= 2.0f;
75 exp++;
76 } while (tmp >= (float)(1u << 11));
79 mantissa = (unsigned int)tmp;
80 if (tmp - mantissa >= 0.5f)
81 ++mantissa; /* Round to nearest, away from zero. */
83 exp += 10; /* Normalize the mantissa. */
84 exp += 15; /* Exponent is encoded with excess 15. */
86 if (exp > 30) /* too big */
88 ret = 0x7c00; /* INF */
90 else if (exp <= 0)
92 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
93 while (exp <= 0)
95 mantissa = mantissa >> 1;
96 ++exp;
98 ret = mantissa & 0x3ff;
100 else
102 ret = (exp << 10) | (mantissa & 0x3ff);
105 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
106 return ret;
109 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
110 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
112 unsigned short *dst_s;
113 const float *src_f;
114 unsigned int x, y;
116 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
118 for (y = 0; y < h; ++y)
120 src_f = (const float *)(src + y * pitch_in);
121 dst_s = (unsigned short *) (dst + y * pitch_out);
122 for (x = 0; x < w; ++x)
124 dst_s[x] = float_32_to_16(src_f + x);
129 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
130 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
132 static const unsigned char convert_5to8[] =
134 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
135 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
136 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
137 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
139 static const unsigned char convert_6to8[] =
141 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
142 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
143 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
144 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
145 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
146 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
147 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
148 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
150 unsigned int x, y;
152 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
154 for (y = 0; y < h; ++y)
156 const WORD *src_line = (const WORD *)(src + y * pitch_in);
157 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
158 for (x = 0; x < w; ++x)
160 WORD pixel = src_line[x];
161 dst_line[x] = 0xff000000u
162 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
163 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
164 | convert_5to8[(pixel & 0x001fu)];
169 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
170 * in both cases we're just setting the X / Alpha channel to 0xff. */
171 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
172 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
174 unsigned int x, y;
176 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
178 for (y = 0; y < h; ++y)
180 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
181 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
183 for (x = 0; x < w; ++x)
185 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
190 static inline BYTE cliptobyte(int x)
192 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
195 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
196 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
198 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
199 unsigned int x, y;
201 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
203 for (y = 0; y < h; ++y)
205 const BYTE *src_line = src + y * pitch_in;
206 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
207 for (x = 0; x < w; ++x)
209 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
210 * C = Y - 16; D = U - 128; E = V - 128;
211 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
212 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
213 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
214 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
215 * U and V are shared between the pixels. */
216 if (!(x & 1)) /* For every even pixel, read new U and V. */
218 d = (int) src_line[1] - 128;
219 e = (int) src_line[3] - 128;
220 r2 = 409 * e + 128;
221 g2 = - 100 * d - 208 * e + 128;
222 b2 = 516 * d + 128;
224 c2 = 298 * ((int) src_line[0] - 16);
225 dst_line[x] = 0xff000000
226 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
227 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
228 | cliptobyte((c2 + b2) >> 8); /* blue */
229 /* Scale RGB values to 0..255 range,
230 * then clip them if still not in range (may be negative),
231 * then shift them within DWORD if necessary. */
232 src_line += 2;
237 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
238 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
240 unsigned int x, y;
241 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
243 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
245 for (y = 0; y < h; ++y)
247 const BYTE *src_line = src + y * pitch_in;
248 WORD *dst_line = (WORD *)(dst + y * pitch_out);
249 for (x = 0; x < w; ++x)
251 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
252 * C = Y - 16; D = U - 128; E = V - 128;
253 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
254 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
255 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
256 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
257 * U and V are shared between the pixels. */
258 if (!(x & 1)) /* For every even pixel, read new U and V. */
260 d = (int) src_line[1] - 128;
261 e = (int) src_line[3] - 128;
262 r2 = 409 * e + 128;
263 g2 = - 100 * d - 208 * e + 128;
264 b2 = 516 * d + 128;
266 c2 = 298 * ((int) src_line[0] - 16);
267 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
268 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
269 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
270 /* Scale RGB values to 0..255 range,
271 * then clip them if still not in range (may be negative),
272 * then shift them within DWORD if necessary. */
273 src_line += 2;
278 struct d3dfmt_converter_desc
280 enum wined3d_format_id from, to;
281 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
284 static const struct d3dfmt_converter_desc converters[] =
286 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
287 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
288 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
289 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
290 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
291 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
294 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
295 enum wined3d_format_id to)
297 unsigned int i;
299 for (i = 0; i < ARRAY_SIZE(converters); ++i)
301 if (converters[i].from == from && converters[i].to == to)
302 return &converters[i];
305 return NULL;
308 static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture,
309 unsigned int sub_resource_idx, const struct wined3d_format *dst_format)
311 unsigned int texture_level = sub_resource_idx % src_texture->level_count;
312 const struct wined3d_format *src_format = src_texture->resource.format;
313 struct wined3d_device *device = src_texture->resource.device;
314 const struct d3dfmt_converter_desc *conv = NULL;
315 unsigned int src_row_pitch, src_slice_pitch;
316 struct wined3d_texture *dst_texture;
317 struct wined3d_bo_address src_data;
318 struct wined3d_resource_desc desc;
319 struct wined3d_context *context;
320 DWORD map_binding;
322 if (!(conv = find_converter(src_format->id, dst_format->id)) && ((device->wined3d->flags & WINED3D_NO3D)
323 || !is_identity_fixup(src_format->color_fixup) || src_format->conv_byte_count
324 || !is_identity_fixup(dst_format->color_fixup) || dst_format->conv_byte_count
325 || ((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
326 && !src_format->decompress)))
328 FIXME("Cannot find a conversion function from format %s to %s.\n",
329 debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
330 return NULL;
333 /* FIXME: Multisampled conversion? */
334 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
335 desc.format = dst_format->id;
336 desc.multisample_type = WINED3D_MULTISAMPLE_NONE;
337 desc.multisample_quality = 0;
338 desc.usage = WINED3DUSAGE_SCRATCH | WINED3DUSAGE_PRIVATE;
339 desc.bind_flags = 0;
340 desc.access = WINED3D_RESOURCE_ACCESS_CPU | WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W;
341 desc.width = wined3d_texture_get_level_width(src_texture, texture_level);
342 desc.height = wined3d_texture_get_level_height(src_texture, texture_level);
343 desc.depth = 1;
344 desc.size = 0;
345 if (FAILED(wined3d_texture_create(device, &desc, 1, 1, WINED3D_TEXTURE_CREATE_DISCARD,
346 NULL, NULL, &wined3d_null_parent_ops, &dst_texture)))
348 ERR("Failed to create a destination texture for conversion.\n");
349 return NULL;
352 context = context_acquire(device, NULL, 0);
354 map_binding = src_texture->resource.map_binding;
355 if (!wined3d_texture_load_location(src_texture, sub_resource_idx, context, map_binding))
356 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
357 wined3d_texture_get_pitch(src_texture, texture_level, &src_row_pitch, &src_slice_pitch);
358 wined3d_texture_get_memory(src_texture, sub_resource_idx, &src_data, map_binding);
360 if (conv)
362 unsigned int dst_row_pitch, dst_slice_pitch;
363 struct wined3d_bo_address dst_data;
364 struct wined3d_range range;
365 const BYTE *src;
366 BYTE *dst;
368 map_binding = dst_texture->resource.map_binding;
369 if (!wined3d_texture_load_location(dst_texture, 0, context, map_binding))
370 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
371 wined3d_texture_get_pitch(dst_texture, 0, &dst_row_pitch, &dst_slice_pitch);
372 wined3d_texture_get_memory(dst_texture, 0, &dst_data, map_binding);
374 src = wined3d_context_map_bo_address(context, &src_data,
375 src_texture->sub_resources[sub_resource_idx].size, WINED3D_MAP_READ);
376 dst = wined3d_context_map_bo_address(context, &dst_data,
377 dst_texture->sub_resources[0].size, WINED3D_MAP_WRITE);
379 conv->convert(src, dst, src_row_pitch, dst_row_pitch, desc.width, desc.height);
381 range.offset = 0;
382 range.size = dst_texture->sub_resources[0].size;
383 wined3d_texture_invalidate_location(dst_texture, 0, ~map_binding);
384 wined3d_context_unmap_bo_address(context, &dst_data, 1, &range);
385 wined3d_context_unmap_bo_address(context, &src_data, 0, NULL);
387 else
389 struct wined3d_box src_box = {0, 0, desc.width, desc.height, 0, 1};
391 TRACE("Using upload conversion.\n");
393 wined3d_texture_prepare_location(dst_texture, 0, context, WINED3D_LOCATION_TEXTURE_RGB);
394 dst_texture->texture_ops->texture_upload_data(context, wined3d_const_bo_address(&src_data),
395 src_format, &src_box, src_row_pitch, src_slice_pitch,
396 dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB, 0, 0, 0);
398 wined3d_texture_validate_location(dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB);
399 wined3d_texture_invalidate_location(dst_texture, 0, ~WINED3D_LOCATION_TEXTURE_RGB);
402 context_release(context);
404 return dst_texture;
407 void texture2d_read_from_framebuffer(struct wined3d_texture *texture, unsigned int sub_resource_idx,
408 struct wined3d_context *context, DWORD src_location, DWORD dst_location)
410 struct wined3d_resource *resource = &texture->resource;
411 struct wined3d_device *device = resource->device;
412 const struct wined3d_format_gl *format_gl;
413 struct wined3d_texture *restore_texture;
414 const struct wined3d_gl_info *gl_info;
415 struct wined3d_context_gl *context_gl;
416 unsigned int row_pitch, slice_pitch;
417 unsigned int width, height, level;
418 struct wined3d_bo_address data;
419 unsigned int restore_idx;
420 BYTE *row, *top, *bottom;
421 BOOL src_is_upside_down;
422 unsigned int i;
423 BYTE *mem;
425 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
427 restore_texture = context->current_rt.texture;
428 restore_idx = context->current_rt.sub_resource_idx;
429 if (restore_texture != texture || restore_idx != sub_resource_idx)
430 context = context_acquire(device, texture, sub_resource_idx);
431 else
432 restore_texture = NULL;
433 context_gl = wined3d_context_gl(context);
434 gl_info = context_gl->gl_info;
436 if (src_location != resource->draw_binding)
438 wined3d_context_gl_apply_fbo_state_blit(context_gl, GL_READ_FRAMEBUFFER,
439 resource, sub_resource_idx, NULL, 0, src_location);
440 wined3d_context_gl_check_fbo_status(context_gl, GL_READ_FRAMEBUFFER);
441 context_invalidate_state(context, STATE_FRAMEBUFFER);
443 else
445 wined3d_context_gl_apply_blit_state(context_gl, device);
448 /* Select the correct read buffer, and give some debug output.
449 * There is no need to keep track of the current read buffer or reset it,
450 * every part of the code that reads sets the read buffer as desired.
452 if (src_location != WINED3D_LOCATION_DRAWABLE || wined3d_resource_is_offscreen(resource))
454 /* Mapping the primary render target which is not on a swapchain.
455 * Read from the back buffer. */
456 TRACE("Mapping offscreen render target.\n");
457 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_context_gl_get_offscreen_gl_buffer(context_gl));
458 src_is_upside_down = TRUE;
460 else
462 /* Onscreen surfaces are always part of a swapchain */
463 GLenum buffer = wined3d_texture_get_gl_buffer(texture);
464 TRACE("Mapping %#x buffer.\n", buffer);
465 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
466 src_is_upside_down = FALSE;
468 checkGLcall("glReadBuffer");
470 if (data.buffer_object)
472 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, ((struct wined3d_bo_gl *)data.buffer_object)->id));
473 checkGLcall("glBindBuffer");
476 level = sub_resource_idx % texture->level_count;
477 wined3d_texture_get_pitch(texture, level, &row_pitch, &slice_pitch);
478 format_gl = wined3d_format_gl(resource->format);
480 /* Setup pixel store pack state -- to glReadPixels into the correct place */
481 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / format_gl->f.byte_count);
482 checkGLcall("glPixelStorei");
484 width = wined3d_texture_get_level_width(texture, level);
485 height = wined3d_texture_get_level_height(texture, level);
486 gl_info->gl_ops.gl.p_glReadPixels(0, 0, width, height,
487 format_gl->format, format_gl->type, data.addr);
488 checkGLcall("glReadPixels");
490 /* Reset previous pixel store pack state */
491 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
492 checkGLcall("glPixelStorei");
494 if (!src_is_upside_down)
496 /* glReadPixels returns the image upside down, and there is no way to
497 * prevent this. Flip the lines in software. */
499 if (!(row = heap_alloc(row_pitch)))
500 goto error;
502 if (data.buffer_object)
504 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
505 checkGLcall("glMapBuffer");
507 else
508 mem = data.addr;
510 top = mem;
511 bottom = mem + row_pitch * (height - 1);
512 for (i = 0; i < height / 2; i++)
514 memcpy(row, top, row_pitch);
515 memcpy(top, bottom, row_pitch);
516 memcpy(bottom, row, row_pitch);
517 top += row_pitch;
518 bottom -= row_pitch;
520 heap_free(row);
522 if (data.buffer_object)
523 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
526 error:
527 if (data.buffer_object)
529 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
530 wined3d_context_gl_reference_bo(context_gl, (struct wined3d_bo_gl *)data.buffer_object);
531 checkGLcall("glBindBuffer");
534 if (restore_texture)
535 context_restore(context, restore_texture, restore_idx);
538 /* Read the framebuffer contents into a texture. Note that this function
539 * doesn't do any kind of flipping. Using this on an onscreen surface will
540 * result in a flipped D3D texture.
542 * Context activation is done by the caller. This function may temporarily
543 * switch to a different context and restore the original one before return. */
544 void texture2d_load_fb_texture(struct wined3d_texture_gl *texture_gl,
545 unsigned int sub_resource_idx, BOOL srgb, struct wined3d_context *context)
547 struct wined3d_texture *restore_texture;
548 const struct wined3d_gl_info *gl_info;
549 struct wined3d_context_gl *context_gl;
550 struct wined3d_resource *resource;
551 unsigned int restore_idx, level;
552 struct wined3d_device *device;
553 GLenum target;
555 resource = &texture_gl->t.resource;
556 device = resource->device;
557 restore_texture = context->current_rt.texture;
558 restore_idx = context->current_rt.sub_resource_idx;
559 if (restore_texture != &texture_gl->t || restore_idx != sub_resource_idx)
560 context = context_acquire(device, &texture_gl->t, sub_resource_idx);
561 else
562 restore_texture = NULL;
563 context_gl = wined3d_context_gl(context);
565 gl_info = context_gl->gl_info;
566 device_invalidate_state(device, STATE_FRAMEBUFFER);
568 wined3d_texture_gl_prepare_texture(texture_gl, context_gl, srgb);
569 wined3d_texture_gl_bind_and_dirtify(texture_gl, context_gl, srgb);
571 TRACE("Reading back offscreen render target %p, %u.\n", texture_gl, sub_resource_idx);
573 if (wined3d_resource_is_offscreen(resource))
574 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_context_gl_get_offscreen_gl_buffer(context_gl));
575 else
576 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(&texture_gl->t));
577 checkGLcall("glReadBuffer");
579 level = sub_resource_idx % texture_gl->t.level_count;
580 target = wined3d_texture_gl_get_sub_resource_target(texture_gl, sub_resource_idx);
581 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(target, level, 0, 0, 0, 0,
582 wined3d_texture_get_level_width(&texture_gl->t, level),
583 wined3d_texture_get_level_height(&texture_gl->t, level));
584 checkGLcall("glCopyTexSubImage2D");
586 if (restore_texture)
587 context_restore(context, restore_texture, restore_idx);
590 /* Context activation is done by the caller. */
591 static void cpu_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
593 struct wined3d_blitter *next;
595 if ((next = blitter->next))
596 next->ops->blitter_destroy(next, context);
598 heap_free(blitter);
601 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
602 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
603 const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx)
605 UINT row_block_count;
606 const BYTE *src_row;
607 BYTE *dst_row;
608 UINT x, y;
610 src_row = src_data;
611 dst_row = dst_data;
613 row_block_count = (update_w + format->block_width - 1) / format->block_width;
615 if (!flags)
617 for (y = 0; y < update_h; y += format->block_height)
619 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
620 src_row += src_pitch;
621 dst_row += dst_pitch;
624 return WINED3D_OK;
627 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
629 src_row += (((update_h / format->block_height) - 1) * src_pitch);
631 switch (format->id)
633 case WINED3DFMT_DXT1:
634 for (y = 0; y < update_h; y += format->block_height)
636 struct block
638 WORD color[2];
639 BYTE control_row[4];
642 const struct block *s = (const struct block *)src_row;
643 struct block *d = (struct block *)dst_row;
645 for (x = 0; x < row_block_count; ++x)
647 d[x].color[0] = s[x].color[0];
648 d[x].color[1] = s[x].color[1];
649 d[x].control_row[0] = s[x].control_row[3];
650 d[x].control_row[1] = s[x].control_row[2];
651 d[x].control_row[2] = s[x].control_row[1];
652 d[x].control_row[3] = s[x].control_row[0];
654 src_row -= src_pitch;
655 dst_row += dst_pitch;
657 return WINED3D_OK;
659 case WINED3DFMT_DXT2:
660 case WINED3DFMT_DXT3:
661 for (y = 0; y < update_h; y += format->block_height)
663 struct block
665 WORD alpha_row[4];
666 WORD color[2];
667 BYTE control_row[4];
670 const struct block *s = (const struct block *)src_row;
671 struct block *d = (struct block *)dst_row;
673 for (x = 0; x < row_block_count; ++x)
675 d[x].alpha_row[0] = s[x].alpha_row[3];
676 d[x].alpha_row[1] = s[x].alpha_row[2];
677 d[x].alpha_row[2] = s[x].alpha_row[1];
678 d[x].alpha_row[3] = s[x].alpha_row[0];
679 d[x].color[0] = s[x].color[0];
680 d[x].color[1] = s[x].color[1];
681 d[x].control_row[0] = s[x].control_row[3];
682 d[x].control_row[1] = s[x].control_row[2];
683 d[x].control_row[2] = s[x].control_row[1];
684 d[x].control_row[3] = s[x].control_row[0];
686 src_row -= src_pitch;
687 dst_row += dst_pitch;
689 return WINED3D_OK;
691 default:
692 FIXME("Compressed flip not implemented for format %s.\n",
693 debug_d3dformat(format->id));
694 return E_NOTIMPL;
698 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
699 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
701 return E_NOTIMPL;
704 static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
705 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
706 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
707 enum wined3d_texture_filter_type filter)
709 unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count;
710 struct wined3d_device *device = dst_texture->resource.device;
711 const struct wined3d_format *src_format, *dst_format;
712 struct wined3d_texture *converted_texture = NULL;
713 struct wined3d_bo_address src_data, dst_data;
714 unsigned int src_fmt_flags, dst_fmt_flags;
715 struct wined3d_map_desc dst_map, src_map;
716 unsigned int x, sx, xinc, y, sy, yinc;
717 struct wined3d_context *context;
718 struct wined3d_range dst_range;
719 unsigned int texture_level;
720 HRESULT hr = WINED3D_OK;
721 BOOL same_sub_resource;
722 BOOL upload = FALSE;
723 DWORD map_binding;
724 const BYTE *sbase;
725 const BYTE *sbuf;
726 BYTE *dbuf;
728 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
729 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
730 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture,
731 src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
733 context = context_acquire(device, NULL, 0);
735 src_format = src_texture->resource.format;
736 dst_format = dst_texture->resource.format;
738 if (wined3d_format_is_typeless(src_format) && src_format->id == dst_format->typeless_id)
739 src_format = dst_format;
740 if (wined3d_format_is_typeless(dst_format) && dst_format->id == src_format->typeless_id)
741 dst_format = src_format;
743 src_height = src_box->bottom - src_box->top;
744 src_width = src_box->right - src_box->left;
745 dst_height = dst_box->bottom - dst_box->top;
746 dst_width = dst_box->right - dst_box->left;
748 dst_range.offset = 0;
749 dst_range.size = dst_texture->sub_resources[dst_sub_resource_idx].size;
750 if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx)
752 same_sub_resource = TRUE;
754 map_binding = dst_texture->resource.map_binding;
755 texture_level = dst_sub_resource_idx % dst_texture->level_count;
756 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
757 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
758 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
759 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
760 wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
761 dst_map.data = wined3d_context_map_bo_address(context, &dst_data,
762 dst_texture->sub_resources[dst_sub_resource_idx].size, WINED3D_MAP_READ | WINED3D_MAP_WRITE);
764 src_map = dst_map;
766 else
768 same_sub_resource = FALSE;
769 upload = dst_format->flags[dst_texture->resource.gl_type] & WINED3DFMT_FLAG_BLOCKS
770 && (dst_width != src_width || dst_height != src_height);
772 if (upload)
774 dst_format = src_format->flags[dst_texture->resource.gl_type] & WINED3DFMT_FLAG_BLOCKS
775 ? wined3d_get_format(device->adapter, WINED3DFMT_B8G8R8A8_UNORM, 0) : src_format;
778 if (!(flags & WINED3D_BLT_RAW) && dst_format->id != src_format->id)
780 if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format)))
782 FIXME("Cannot convert %s to %s.\n", debug_d3dformat(src_format->id),
783 debug_d3dformat(dst_format->id));
784 context_release(context);
785 return WINED3DERR_NOTAVAILABLE;
787 src_texture = converted_texture;
788 src_sub_resource_idx = 0;
789 src_format = src_texture->resource.format;
792 map_binding = src_texture->resource.map_binding;
793 texture_level = src_sub_resource_idx % src_texture->level_count;
794 if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, map_binding))
795 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
796 wined3d_texture_get_pitch(src_texture, texture_level, &src_map.row_pitch, &src_map.slice_pitch);
797 wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &src_data, map_binding);
798 src_map.data = wined3d_context_map_bo_address(context, &src_data,
799 src_texture->sub_resources[src_sub_resource_idx].size, WINED3D_MAP_READ);
801 if (upload)
803 wined3d_format_calculate_pitch(dst_format, 1, dst_box->right, dst_box->bottom,
804 &dst_map.row_pitch, &dst_map.slice_pitch);
805 dst_map.data = heap_alloc(dst_map.slice_pitch);
807 else
809 map_binding = dst_texture->resource.map_binding;
810 texture_level = dst_sub_resource_idx % dst_texture->level_count;
811 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
812 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
814 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
815 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
816 wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
817 dst_map.data = wined3d_context_map_bo_address(context, &dst_data,
818 dst_texture->sub_resources[dst_sub_resource_idx].size, WINED3D_MAP_WRITE);
821 src_fmt_flags = src_format->flags[src_texture->resource.gl_type];
822 dst_fmt_flags = dst_format->flags[dst_texture->resource.gl_type];
823 flags &= ~WINED3D_BLT_RAW;
825 bpp = dst_format->byte_count;
826 row_byte_count = dst_width * bpp;
828 sbase = (BYTE *)src_map.data
829 + ((src_box->top / src_format->block_height) * src_map.row_pitch)
830 + ((src_box->left / src_format->block_width) * src_format->block_byte_count);
831 dbuf = (BYTE *)dst_map.data
832 + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch)
833 + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count);
835 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
837 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
839 if (same_sub_resource)
841 FIXME("Only plain blits supported on compressed surfaces.\n");
842 hr = E_NOTIMPL;
843 goto release;
846 hr = surface_cpu_blt_compressed(sbase, dbuf,
847 src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height,
848 src_format, flags, fx);
849 goto release;
852 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
853 && (src_width != dst_width || src_height != dst_height))
855 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
856 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
859 xinc = (src_width << 16) / dst_width;
860 yinc = (src_height << 16) / dst_height;
862 if (!flags)
864 /* No effects, we can cheat here. */
865 if (dst_width == src_width)
867 if (dst_height == src_height)
869 /* No stretching in either direction. This needs to be as fast
870 * as possible. */
871 sbuf = sbase;
873 /* Check for overlapping surfaces. */
874 if (!same_sub_resource || dst_box->top < src_box->top
875 || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
877 /* No overlap, or dst above src, so copy from top downwards. */
878 for (y = 0; y < dst_height; ++y)
880 memcpy(dbuf, sbuf, row_byte_count);
881 sbuf += src_map.row_pitch;
882 dbuf += dst_map.row_pitch;
885 else if (dst_box->top > src_box->top)
887 /* Copy from bottom upwards. */
888 sbuf += src_map.row_pitch * dst_height;
889 dbuf += dst_map.row_pitch * dst_height;
890 for (y = 0; y < dst_height; ++y)
892 sbuf -= src_map.row_pitch;
893 dbuf -= dst_map.row_pitch;
894 memcpy(dbuf, sbuf, row_byte_count);
897 else
899 /* Src and dst overlapping on the same line, use memmove. */
900 for (y = 0; y < dst_height; ++y)
902 memmove(dbuf, sbuf, row_byte_count);
903 sbuf += src_map.row_pitch;
904 dbuf += dst_map.row_pitch;
908 else
910 /* Stretching in y direction only. */
911 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
913 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
914 memcpy(dbuf, sbuf, row_byte_count);
915 dbuf += dst_map.row_pitch;
919 else
921 /* Stretching in X direction. */
922 unsigned int last_sy = ~0u;
923 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
925 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
927 if ((sy >> 16) == (last_sy >> 16))
929 /* This source row is the same as last source row -
930 * Copy the already stretched row. */
931 memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count);
933 else
935 #define STRETCH_ROW(type) \
936 do { \
937 const type *s = (const type *)sbuf; \
938 type *d = (type *)dbuf; \
939 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
940 d[x] = s[sx >> 16]; \
941 } while(0)
943 switch(bpp)
945 case 1:
946 STRETCH_ROW(BYTE);
947 break;
948 case 2:
949 STRETCH_ROW(WORD);
950 break;
951 case 4:
952 STRETCH_ROW(DWORD);
953 break;
954 case 3:
956 const BYTE *s;
957 BYTE *d = dbuf;
958 for (x = sx = 0; x < dst_width; x++, sx+= xinc)
960 DWORD pixel;
962 s = sbuf + 3 * (sx >> 16);
963 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
964 d[0] = (pixel ) & 0xff;
965 d[1] = (pixel >> 8) & 0xff;
966 d[2] = (pixel >> 16) & 0xff;
967 d += 3;
969 break;
971 default:
972 FIXME("Stretched blit not implemented for bpp %u.\n", bpp * 8);
973 hr = WINED3DERR_NOTAVAILABLE;
974 goto error;
976 #undef STRETCH_ROW
978 dbuf += dst_map.row_pitch;
979 last_sy = sy;
983 else
985 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
986 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
987 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
988 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
989 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
991 /* The color keying flags are checked for correctness in ddraw. */
992 if (flags & WINED3D_BLT_SRC_CKEY)
994 keylow = src_texture->async.src_blt_color_key.color_space_low_value;
995 keyhigh = src_texture->async.src_blt_color_key.color_space_high_value;
997 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
999 keylow = fx->src_color_key.color_space_low_value;
1000 keyhigh = fx->src_color_key.color_space_high_value;
1003 if (flags & WINED3D_BLT_DST_CKEY)
1005 /* Destination color keys are taken from the source surface! */
1006 destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value;
1007 destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value;
1009 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
1011 destkeylow = fx->dst_color_key.color_space_low_value;
1012 destkeyhigh = fx->dst_color_key.color_space_high_value;
1015 if (bpp == 1)
1017 keymask = 0xff;
1019 else
1021 DWORD masks[3];
1022 get_color_masks(src_format, masks);
1023 keymask = masks[0] | masks[1] | masks[2];
1025 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
1026 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
1029 if (flags & WINED3D_BLT_FX)
1031 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
1032 LONG tmpxy;
1033 dTopLeft = dbuf;
1034 dTopRight = dbuf + ((dst_width - 1) * bpp);
1035 dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
1036 dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
1038 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
1040 /* I don't think we need to do anything about this flag. */
1041 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
1043 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
1045 tmp = dTopRight;
1046 dTopRight = dTopLeft;
1047 dTopLeft = tmp;
1048 tmp = dBottomRight;
1049 dBottomRight = dBottomLeft;
1050 dBottomLeft = tmp;
1051 dstxinc = dstxinc * -1;
1053 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
1055 tmp = dTopLeft;
1056 dTopLeft = dBottomLeft;
1057 dBottomLeft = tmp;
1058 tmp = dTopRight;
1059 dTopRight = dBottomRight;
1060 dBottomRight = tmp;
1061 dstyinc = dstyinc * -1;
1063 if (fx->fx & WINEDDBLTFX_NOTEARING)
1065 /* I don't think we need to do anything about this flag. */
1066 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
1068 if (fx->fx & WINEDDBLTFX_ROTATE180)
1070 tmp = dBottomRight;
1071 dBottomRight = dTopLeft;
1072 dTopLeft = tmp;
1073 tmp = dBottomLeft;
1074 dBottomLeft = dTopRight;
1075 dTopRight = tmp;
1076 dstxinc = dstxinc * -1;
1077 dstyinc = dstyinc * -1;
1079 if (fx->fx & WINEDDBLTFX_ROTATE270)
1081 tmp = dTopLeft;
1082 dTopLeft = dBottomLeft;
1083 dBottomLeft = dBottomRight;
1084 dBottomRight = dTopRight;
1085 dTopRight = tmp;
1086 tmpxy = dstxinc;
1087 dstxinc = dstyinc;
1088 dstyinc = tmpxy;
1089 dstxinc = dstxinc * -1;
1091 if (fx->fx & WINEDDBLTFX_ROTATE90)
1093 tmp = dTopLeft;
1094 dTopLeft = dTopRight;
1095 dTopRight = dBottomRight;
1096 dBottomRight = dBottomLeft;
1097 dBottomLeft = tmp;
1098 tmpxy = dstxinc;
1099 dstxinc = dstyinc;
1100 dstyinc = tmpxy;
1101 dstyinc = dstyinc * -1;
1103 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
1105 /* I don't think we need to do anything about this flag. */
1106 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
1108 dbuf = dTopLeft;
1109 flags &= ~(WINED3D_BLT_FX);
1112 #define COPY_COLORKEY_FX(type) \
1113 do { \
1114 const type *s; \
1115 type *d = (type *)dbuf, *dx, tmp; \
1116 for (y = sy = 0; y < dst_height; ++y, sy += yinc) \
1118 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
1119 dx = d; \
1120 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
1122 tmp = s[sx >> 16]; \
1123 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
1124 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
1126 dx[0] = tmp; \
1128 dx = (type *)(((BYTE *)dx) + dstxinc); \
1130 d = (type *)(((BYTE *)d) + dstyinc); \
1132 } while(0)
1134 switch (bpp)
1136 case 1:
1137 COPY_COLORKEY_FX(BYTE);
1138 break;
1139 case 2:
1140 COPY_COLORKEY_FX(WORD);
1141 break;
1142 case 4:
1143 COPY_COLORKEY_FX(DWORD);
1144 break;
1145 case 3:
1147 const BYTE *s;
1148 BYTE *d = dbuf, *dx;
1149 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
1151 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
1152 dx = d;
1153 for (x = sx = 0; x < dst_width; ++x, sx+= xinc)
1155 DWORD pixel, dpixel = 0;
1156 s = sbuf + 3 * (sx>>16);
1157 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
1158 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
1159 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
1160 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
1162 dx[0] = (pixel ) & 0xff;
1163 dx[1] = (pixel >> 8) & 0xff;
1164 dx[2] = (pixel >> 16) & 0xff;
1166 dx += dstxinc;
1168 d += dstyinc;
1170 break;
1172 default:
1173 FIXME("%s color-keyed blit not implemented for bpp %u.\n",
1174 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
1175 hr = WINED3DERR_NOTAVAILABLE;
1176 goto error;
1177 #undef COPY_COLORKEY_FX
1181 error:
1182 if (flags)
1183 FIXME(" Unsupported flags %#x.\n", flags);
1185 release:
1186 if (upload && hr == WINED3D_OK)
1188 struct wined3d_bo_address data;
1190 data.buffer_object = 0;
1191 data.addr = dst_map.data;
1193 texture_level = dst_sub_resource_idx % dst_texture->level_count;
1195 wined3d_texture_prepare_location(dst_texture, texture_level, context, WINED3D_LOCATION_TEXTURE_RGB);
1196 dst_texture->texture_ops->texture_upload_data(context, wined3d_const_bo_address(&data), dst_format,
1197 dst_box, dst_map.row_pitch, dst_map.slice_pitch, dst_texture, texture_level,
1198 WINED3D_LOCATION_TEXTURE_RGB, dst_box->left, dst_box->top, 0);
1200 wined3d_texture_validate_location(dst_texture, texture_level, WINED3D_LOCATION_TEXTURE_RGB);
1201 wined3d_texture_invalidate_location(dst_texture, texture_level, ~WINED3D_LOCATION_TEXTURE_RGB);
1204 if (upload)
1206 heap_free(dst_map.data);
1208 else
1210 wined3d_context_unmap_bo_address(context, &dst_data, 1, &dst_range);
1213 if (!same_sub_resource)
1214 wined3d_context_unmap_bo_address(context, &src_data, 0, NULL);
1215 if (SUCCEEDED(hr) && dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture)
1217 SetRect(&dst_texture->swapchain->front_buffer_update,
1218 dst_box->left, dst_box->top, dst_box->right, dst_box->bottom);
1219 dst_texture->swapchain->swapchain_ops->swapchain_frontbuffer_updated(dst_texture->swapchain);
1221 if (converted_texture)
1222 wined3d_texture_decref(converted_texture);
1223 context_release(context);
1225 return hr;
1228 static void surface_cpu_blt_colour_fill(struct wined3d_rendertarget_view *view,
1229 const struct wined3d_box *box, const struct wined3d_color *colour)
1231 struct wined3d_device *device = view->resource->device;
1232 unsigned int x, y, z, w, h, d, bpp, level;
1233 struct wined3d_context *context;
1234 struct wined3d_texture *texture;
1235 struct wined3d_bo_address data;
1236 struct wined3d_map_desc map;
1237 struct wined3d_range range;
1238 DWORD map_binding;
1239 uint8_t *dst;
1240 DWORD c;
1242 TRACE("view %p, box %s, colour %s.\n", view, debug_box(box), debug_color(colour));
1244 if (view->format_flags & WINED3DFMT_FLAG_BLOCKS)
1246 FIXME("Not implemented for format %s.\n", debug_d3dformat(view->format->id));
1247 return;
1250 if (view->format->id != view->resource->format->id)
1251 FIXME("View format %s doesn't match resource format %s.\n",
1252 debug_d3dformat(view->format->id), debug_d3dformat(view->resource->format->id));
1254 if (view->resource->type == WINED3D_RTYPE_BUFFER)
1256 FIXME("Not implemented for buffers.\n");
1257 return;
1260 context = context_acquire(device, NULL, 0);
1262 texture = texture_from_resource(view->resource);
1263 level = view->sub_resource_idx % texture->level_count;
1265 c = wined3d_format_convert_from_float(view->format, colour);
1266 bpp = view->format->byte_count;
1267 w = min(box->right, view->width) - min(box->left, view->width);
1268 h = min(box->bottom, view->height) - min(box->top, view->height);
1269 if (view->resource->type != WINED3D_RTYPE_TEXTURE_3D)
1271 d = 1;
1273 else
1275 d = wined3d_texture_get_level_depth(texture, level);
1276 d = min(box->back, d) - min(box->front, d);
1279 map_binding = texture->resource.map_binding;
1280 if (!wined3d_texture_load_location(texture, view->sub_resource_idx, context, map_binding))
1281 ERR("Failed to load the sub-resource into %s.\n", wined3d_debug_location(map_binding));
1282 wined3d_texture_invalidate_location(texture, view->sub_resource_idx, ~map_binding);
1283 wined3d_texture_get_pitch(texture, level, &map.row_pitch, &map.slice_pitch);
1284 wined3d_texture_get_memory(texture, view->sub_resource_idx, &data, map_binding);
1285 map.data = wined3d_context_map_bo_address(context, &data,
1286 texture->sub_resources[view->sub_resource_idx].size, WINED3D_MAP_WRITE);
1287 map.data = (BYTE *)map.data
1288 + (box->front * map.slice_pitch)
1289 + ((box->top / view->format->block_height) * map.row_pitch)
1290 + ((box->left / view->format->block_width) * view->format->block_byte_count);
1291 range.offset = 0;
1292 range.size = texture->sub_resources[view->sub_resource_idx].size;
1294 switch (bpp)
1296 case 1:
1297 for (x = 0; x < w; ++x)
1299 ((BYTE *)map.data)[x] = c;
1301 break;
1303 case 2:
1304 for (x = 0; x < w; ++x)
1306 ((WORD *)map.data)[x] = c;
1308 break;
1310 case 3:
1312 dst = map.data;
1313 for (x = 0; x < w; ++x, dst += 3)
1315 dst[0] = (c ) & 0xff;
1316 dst[1] = (c >> 8) & 0xff;
1317 dst[2] = (c >> 16) & 0xff;
1319 break;
1321 case 4:
1322 for (x = 0; x < w; ++x)
1324 ((DWORD *)map.data)[x] = c;
1326 break;
1328 default:
1329 FIXME("Not implemented for bpp %u.\n", bpp);
1330 wined3d_resource_unmap(view->resource, view->sub_resource_idx);
1331 return;
1334 dst = map.data;
1335 for (y = 1; y < h; ++y)
1337 dst += map.row_pitch;
1338 memcpy(dst, map.data, w * bpp);
1341 dst = map.data;
1342 for (z = 1; z < d; ++z)
1344 dst += map.slice_pitch;
1345 memcpy(dst, map.data, w * h * bpp);
1348 wined3d_context_unmap_bo_address(context, &data, 1, &range);
1349 context_release(context);
1352 static void cpu_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
1353 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
1354 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
1356 struct wined3d_color c = {depth, 0.0f, 0.0f, 0.0f};
1357 struct wined3d_rendertarget_view *view;
1358 struct wined3d_box box;
1359 unsigned int i, j;
1361 if (!rect_count)
1363 rect_count = 1;
1364 clear_rects = draw_rect;
1367 for (i = 0; i < rect_count; ++i)
1369 box.left = max(clear_rects[i].left, draw_rect->left);
1370 box.top = max(clear_rects[i].top, draw_rect->top);
1371 box.right = min(clear_rects[i].right, draw_rect->right);
1372 box.bottom = min(clear_rects[i].bottom, draw_rect->bottom);
1373 box.front = 0;
1374 box.back = ~0u;
1376 if (box.left >= box.right || box.top >= box.bottom)
1377 continue;
1379 if (flags & WINED3DCLEAR_TARGET)
1381 for (j = 0; j < rt_count; ++j)
1383 if ((view = fb->render_targets[j]))
1384 surface_cpu_blt_colour_fill(view, &box, colour);
1388 if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil))
1390 if ((view->format->depth_size && !(flags & WINED3DCLEAR_ZBUFFER))
1391 || (view->format->stencil_size && !(flags & WINED3DCLEAR_STENCIL)))
1392 FIXME("Clearing %#x on %s.\n", flags, debug_d3dformat(view->format->id));
1394 surface_cpu_blt_colour_fill(view, &box, &c);
1399 static DWORD cpu_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
1400 struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1401 DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture,
1402 unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect,
1403 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter)
1405 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
1406 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
1407 struct wined3d_blt_fx fx;
1408 DWORD flags = 0;
1410 memset(&fx, 0, sizeof(fx));
1411 switch (op)
1413 case WINED3D_BLIT_OP_COLOR_BLIT:
1414 case WINED3D_BLIT_OP_DEPTH_BLIT:
1415 break;
1416 case WINED3D_BLIT_OP_RAW_BLIT:
1417 flags |= WINED3D_BLT_RAW;
1418 break;
1419 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
1420 flags |= WINED3D_BLT_ALPHA_TEST;
1421 break;
1422 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
1423 flags |= WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_FX;
1424 fx.src_color_key = *color_key;
1425 break;
1426 default:
1427 FIXME("Unhandled op %#x.\n", op);
1428 break;
1431 if (FAILED(surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
1432 src_texture, src_sub_resource_idx, &src_box, flags, &fx, filter)))
1433 ERR("Failed to blit.\n");
1434 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
1436 return dst_location | (dst_texture->sub_resources[dst_sub_resource_idx].locations
1437 & dst_texture->resource.map_binding);
1440 static const struct wined3d_blitter_ops cpu_blitter_ops =
1442 cpu_blitter_destroy,
1443 cpu_blitter_clear,
1444 cpu_blitter_blit,
1447 struct wined3d_blitter *wined3d_cpu_blitter_create(void)
1449 struct wined3d_blitter *blitter;
1451 if (!(blitter = heap_alloc(sizeof(*blitter))))
1452 return NULL;
1454 TRACE("Created blitter %p.\n", blitter);
1456 blitter->ops = &cpu_blitter_ops;
1457 blitter->next = NULL;
1459 return blitter;
1462 static bool wined3d_is_colour_blit(enum wined3d_blit_op blit_op)
1464 switch (blit_op)
1466 case WINED3D_BLIT_OP_COLOR_BLIT:
1467 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
1468 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
1469 return true;
1471 default:
1472 return false;
1476 HRESULT texture2d_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
1477 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1478 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
1479 enum wined3d_texture_filter_type filter)
1481 struct wined3d_texture_sub_resource *src_sub_resource, *dst_sub_resource;
1482 struct wined3d_device *device = dst_texture->resource.device;
1483 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1484 const struct wined3d_color_key *colour_key = NULL;
1485 DWORD src_location, dst_location, valid_locations;
1486 struct wined3d_context *context;
1487 enum wined3d_blit_op blit_op;
1488 BOOL scale, convert, resolve;
1489 RECT src_rect, dst_rect;
1490 bool src_ds, dst_ds;
1492 static const DWORD simple_blit = WINED3D_BLT_SRC_CKEY
1493 | WINED3D_BLT_SRC_CKEY_OVERRIDE
1494 | WINED3D_BLT_ALPHA_TEST
1495 | WINED3D_BLT_RAW;
1497 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
1498 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
1499 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture, src_sub_resource_idx,
1500 debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
1501 TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage));
1503 if (fx)
1505 TRACE("fx %#x.\n", fx->fx);
1506 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
1507 fx->dst_color_key.color_space_low_value,
1508 fx->dst_color_key.color_space_high_value);
1509 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
1510 fx->src_color_key.color_space_low_value,
1511 fx->src_color_key.color_space_high_value);
1514 dst_sub_resource = &dst_texture->sub_resources[dst_sub_resource_idx];
1515 src_sub_resource = &src_texture->sub_resources[src_sub_resource_idx];
1517 if (src_sub_resource->locations & WINED3D_LOCATION_DISCARDED)
1519 WARN("Source sub-resource is discarded, nothing to do.\n");
1520 return WINED3D_OK;
1523 SetRect(&src_rect, src_box->left, src_box->top, src_box->right, src_box->bottom);
1524 SetRect(&dst_rect, dst_box->left, dst_box->top, dst_box->right, dst_box->bottom);
1526 if (!fx || !(fx->fx))
1527 flags &= ~WINED3D_BLT_FX;
1529 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
1530 if (flags & WINED3D_BLT_DO_NOT_WAIT)
1532 static unsigned int once;
1534 if (!once++)
1535 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
1538 flags &= ~(WINED3D_BLT_SYNCHRONOUS | WINED3D_BLT_DO_NOT_WAIT | WINED3D_BLT_WAIT);
1540 if (flags & ~simple_blit)
1542 WARN_(d3d_perf)("Using CPU fallback for complex blit (%#x).\n", flags);
1543 goto cpu;
1546 src_swapchain = src_texture->swapchain;
1547 dst_swapchain = dst_texture->swapchain;
1549 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain
1550 && (wined3d_settings.offscreen_rendering_mode != ORM_FBO
1551 || src_texture == src_swapchain->front_buffer))
1553 /* TODO: We could support cross-swapchain blits by first downloading
1554 * the source to a texture. */
1555 FIXME("Cross-swapchain blit not supported.\n");
1556 return WINED3DERR_INVALIDCALL;
1559 scale = src_box->right - src_box->left != dst_box->right - dst_box->left
1560 || src_box->bottom - src_box->top != dst_box->bottom - dst_box->top;
1561 convert = src_texture->resource.format->id != dst_texture->resource.format->id;
1562 resolve = src_texture->resource.multisample_type != dst_texture->resource.multisample_type;
1564 dst_ds = dst_texture->resource.format->depth_size || dst_texture->resource.format->stencil_size;
1565 src_ds = src_texture->resource.format->depth_size || src_texture->resource.format->stencil_size;
1567 if (src_ds || dst_ds)
1569 TRACE("Depth/stencil blit.\n");
1571 if (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)
1572 dst_location = dst_texture->resource.draw_binding;
1573 else
1574 dst_location = dst_texture->resource.map_binding;
1576 if ((flags & WINED3D_BLT_RAW) || (!scale && !convert && !resolve))
1577 blit_op = WINED3D_BLIT_OP_RAW_BLIT;
1578 else
1579 blit_op = WINED3D_BLIT_OP_DEPTH_BLIT;
1581 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
1582 valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context,
1583 src_texture, src_sub_resource_idx, src_texture->resource.draw_binding, &src_rect,
1584 dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, NULL, filter);
1585 context_release(context);
1587 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
1588 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
1590 return WINED3D_OK;
1593 TRACE("Colour blit.\n");
1595 /* In principle this would apply to depth blits as well, but we don't
1596 * implement those in the CPU blitter at the moment. */
1597 if ((dst_sub_resource->locations & dst_texture->resource.map_binding)
1598 && (src_sub_resource->locations & src_texture->resource.map_binding))
1600 if (scale)
1601 TRACE("Not doing sysmem blit because of scaling.\n");
1602 else if (convert)
1603 TRACE("Not doing sysmem blit because of format conversion.\n");
1604 else
1605 goto cpu;
1608 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
1609 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
1611 colour_key = &fx->src_color_key;
1612 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
1614 else if (flags & WINED3D_BLT_SRC_CKEY)
1616 colour_key = &src_texture->async.src_blt_color_key;
1617 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
1619 else if (flags & WINED3D_BLT_ALPHA_TEST)
1621 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
1623 else if ((src_sub_resource->locations & surface_simple_locations)
1624 && !(dst_sub_resource->locations & surface_simple_locations)
1625 && (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1627 /* Upload */
1628 if (scale)
1629 TRACE("Not doing upload because of scaling.\n");
1630 else if (convert)
1631 TRACE("Not doing upload because of format conversion.\n");
1632 else if (dst_texture->resource.format->conv_byte_count)
1633 TRACE("Not doing upload because the destination format needs conversion.\n");
1634 else
1636 wined3d_texture_upload_from_texture(dst_texture, dst_sub_resource_idx, dst_box->left,
1637 dst_box->top, dst_box->front, src_texture, src_sub_resource_idx, src_box);
1638 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
1640 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
1641 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx,
1642 context, dst_texture->resource.draw_binding);
1643 context_release(context);
1645 return WINED3D_OK;
1648 else if (!(src_sub_resource->locations & surface_simple_locations)
1649 && (dst_sub_resource->locations & dst_texture->resource.map_binding)
1650 && !(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1652 /* Download */
1653 if (scale)
1654 TRACE("Not doing download because of scaling.\n");
1655 else if (convert)
1656 TRACE("Not doing download because of format conversion.\n");
1657 else if (src_texture->resource.format->conv_byte_count)
1658 TRACE("Not doing download because the source format needs conversion.\n");
1659 else if (!(src_texture->flags & WINED3D_TEXTURE_DOWNLOADABLE))
1660 TRACE("Not doing download because texture is not downloadable.\n");
1661 else if (!wined3d_texture_is_full_rect(src_texture, src_sub_resource_idx % src_texture->level_count, &src_rect))
1662 TRACE("Not doing download because of partial download (src).\n");
1663 else if (!wined3d_texture_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, &dst_rect))
1664 TRACE("Not doing download because of partial download (dst).\n");
1665 else
1667 wined3d_texture_download_from_texture(dst_texture, dst_sub_resource_idx, src_texture,
1668 src_sub_resource_idx);
1669 return WINED3D_OK;
1672 else if (dst_swapchain && dst_swapchain->back_buffers
1673 && dst_texture == dst_swapchain->front_buffer
1674 && src_texture == dst_swapchain->back_buffers[0])
1676 /* Use present for back -> front blits. The idea behind this is that
1677 * present is potentially faster than a blit, in particular when FBO
1678 * blits aren't available. Some ddraw applications like Half-Life and
1679 * Prince of Persia 3D use Blt() from the backbuffer to the
1680 * frontbuffer instead of doing a Flip(). D3d8 and d3d9 applications
1681 * can't blit directly to the frontbuffer. */
1682 enum wined3d_swap_effect swap_effect = dst_swapchain->state.desc.swap_effect;
1684 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
1686 /* Set the swap effect to COPY, we don't want the backbuffer to become
1687 * undefined. */
1688 dst_swapchain->state.desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
1689 wined3d_swapchain_present(dst_swapchain, NULL, NULL,
1690 dst_swapchain->win_handle, dst_swapchain->swap_interval, 0);
1691 dst_swapchain->state.desc.swap_effect = swap_effect;
1693 return WINED3D_OK;
1696 if ((flags & WINED3D_BLT_RAW) || (blit_op == WINED3D_BLIT_OP_COLOR_BLIT && !scale && !convert && !resolve))
1697 blit_op = WINED3D_BLIT_OP_RAW_BLIT;
1699 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
1701 if (src_texture->resource.multisample_type != WINED3D_MULTISAMPLE_NONE
1702 && ((scale && !context->d3d_info->scaled_resolve)
1703 || convert || !wined3d_is_colour_blit(blit_op)))
1704 src_location = WINED3D_LOCATION_RB_RESOLVED;
1705 else
1706 src_location = src_texture->resource.draw_binding;
1708 if (!(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1709 dst_location = dst_texture->resource.map_binding;
1710 else if (dst_texture->resource.multisample_type != WINED3D_MULTISAMPLE_NONE
1711 && (scale || convert || !wined3d_is_colour_blit(blit_op)))
1712 dst_location = WINED3D_LOCATION_RB_RESOLVED;
1713 else
1714 dst_location = dst_texture->resource.draw_binding;
1716 valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context,
1717 src_texture, src_sub_resource_idx, src_location, &src_rect,
1718 dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, colour_key, filter);
1720 context_release(context);
1722 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
1723 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
1725 return WINED3D_OK;
1727 cpu:
1728 return surface_cpu_blt(dst_texture, dst_sub_resource_idx, dst_box,
1729 src_texture, src_sub_resource_idx, src_box, flags, fx, filter);