wined3d: Use wined3d_mask_from_size() in shader_glsl_gather4().
[wine.git] / dlls / wined3d / surface.c
blob3309df951dc718748dc5f44657d00217fd7f5787
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 "wined3d_private.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
33 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
35 static const DWORD surface_simple_locations = WINED3D_LOCATION_SYSMEM | WINED3D_LOCATION_BUFFER;
37 /* Works correctly only for <= 4 bpp formats. */
38 static void get_color_masks(const struct wined3d_format *format, uint32_t *masks)
40 masks[0] = wined3d_mask_from_size(format->red_size) << format->red_offset;
41 masks[1] = wined3d_mask_from_size(format->green_size) << format->green_offset;
42 masks[2] = wined3d_mask_from_size(format->blue_size) << format->blue_offset;
45 /* See also float_16_to_32() in wined3d_private.h */
46 static inline unsigned short float_32_to_16(const float *in)
48 int exp = 0;
49 float tmp = fabsf(*in);
50 unsigned int mantissa;
51 unsigned short ret;
53 /* Deal with special numbers */
54 if (*in == 0.0f)
55 return 0x0000;
56 if (isnan(*in))
57 return 0x7c01;
58 if (isinf(*in))
59 return (*in < 0.0f ? 0xfc00 : 0x7c00);
61 if (tmp < (float)(1u << 10))
65 tmp = tmp * 2.0f;
66 exp--;
67 } while (tmp < (float)(1u << 10));
69 else if (tmp >= (float)(1u << 11))
73 tmp /= 2.0f;
74 exp++;
75 } while (tmp >= (float)(1u << 11));
78 mantissa = (unsigned int)tmp;
79 if (tmp - mantissa >= 0.5f)
80 ++mantissa; /* Round to nearest, away from zero. */
82 exp += 10; /* Normalize the mantissa. */
83 exp += 15; /* Exponent is encoded with excess 15. */
85 if (exp > 30) /* too big */
87 ret = 0x7c00; /* INF */
89 else if (exp <= 0)
91 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
92 while (exp <= 0)
94 mantissa = mantissa >> 1;
95 ++exp;
97 ret = mantissa & 0x3ff;
99 else
101 ret = (exp << 10) | (mantissa & 0x3ff);
104 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
105 return ret;
108 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
109 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
111 unsigned short *dst_s;
112 const float *src_f;
113 unsigned int x, y;
115 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
117 for (y = 0; y < h; ++y)
119 src_f = (const float *)(src + y * pitch_in);
120 dst_s = (unsigned short *) (dst + y * pitch_out);
121 for (x = 0; x < w; ++x)
123 dst_s[x] = float_32_to_16(src_f + x);
128 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
129 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
131 static const unsigned char convert_5to8[] =
133 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
134 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
135 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
136 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
138 static const unsigned char convert_6to8[] =
140 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
141 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
142 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
143 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
144 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
145 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
146 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
147 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
149 unsigned int x, y;
151 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
153 for (y = 0; y < h; ++y)
155 const WORD *src_line = (const WORD *)(src + y * pitch_in);
156 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
157 for (x = 0; x < w; ++x)
159 WORD pixel = src_line[x];
160 dst_line[x] = 0xff000000u
161 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
162 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
163 | convert_5to8[(pixel & 0x001fu)];
168 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
169 * in both cases we're just setting the X / Alpha channel to 0xff. */
170 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
171 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
173 unsigned int x, y;
175 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
177 for (y = 0; y < h; ++y)
179 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
180 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
182 for (x = 0; x < w; ++x)
184 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
189 static inline BYTE cliptobyte(int x)
191 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
194 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
195 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
197 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
198 unsigned int x, y;
200 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
202 for (y = 0; y < h; ++y)
204 const BYTE *src_line = src + y * pitch_in;
205 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
206 for (x = 0; x < w; ++x)
208 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
209 * C = Y - 16; D = U - 128; E = V - 128;
210 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
211 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
212 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
213 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
214 * U and V are shared between the pixels. */
215 if (!(x & 1)) /* For every even pixel, read new U and V. */
217 d = (int) src_line[1] - 128;
218 e = (int) src_line[3] - 128;
219 r2 = 409 * e + 128;
220 g2 = - 100 * d - 208 * e + 128;
221 b2 = 516 * d + 128;
223 c2 = 298 * ((int) src_line[0] - 16);
224 dst_line[x] = 0xff000000
225 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
226 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
227 | cliptobyte((c2 + b2) >> 8); /* blue */
228 /* Scale RGB values to 0..255 range,
229 * then clip them if still not in range (may be negative),
230 * then shift them within DWORD if necessary. */
231 src_line += 2;
236 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
237 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
239 unsigned int x, y;
240 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
242 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
244 for (y = 0; y < h; ++y)
246 const BYTE *src_line = src + y * pitch_in;
247 WORD *dst_line = (WORD *)(dst + y * pitch_out);
248 for (x = 0; x < w; ++x)
250 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
251 * C = Y - 16; D = U - 128; E = V - 128;
252 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
253 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
254 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
255 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
256 * U and V are shared between the pixels. */
257 if (!(x & 1)) /* For every even pixel, read new U and V. */
259 d = (int) src_line[1] - 128;
260 e = (int) src_line[3] - 128;
261 r2 = 409 * e + 128;
262 g2 = - 100 * d - 208 * e + 128;
263 b2 = 516 * d + 128;
265 c2 = 298 * ((int) src_line[0] - 16);
266 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
267 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
268 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
269 /* Scale RGB values to 0..255 range,
270 * then clip them if still not in range (may be negative),
271 * then shift them within DWORD if necessary. */
272 src_line += 2;
277 struct d3dfmt_converter_desc
279 enum wined3d_format_id from, to;
280 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
283 static const struct d3dfmt_converter_desc converters[] =
285 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
286 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
287 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
288 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
289 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
290 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
293 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
294 enum wined3d_format_id to)
296 unsigned int i;
298 for (i = 0; i < ARRAY_SIZE(converters); ++i)
300 if (converters[i].from == from && converters[i].to == to)
301 return &converters[i];
304 return NULL;
307 static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture,
308 unsigned int sub_resource_idx, const struct wined3d_format *dst_format)
310 unsigned int texture_level = sub_resource_idx % src_texture->level_count;
311 const struct wined3d_format *src_format = src_texture->resource.format;
312 struct wined3d_device *device = src_texture->resource.device;
313 const struct d3dfmt_converter_desc *conv = NULL;
314 unsigned int src_row_pitch, src_slice_pitch;
315 struct wined3d_texture *dst_texture;
316 struct wined3d_bo_address src_data;
317 struct wined3d_resource_desc desc;
318 struct wined3d_context *context;
319 DWORD map_binding;
321 if (!(conv = find_converter(src_format->id, dst_format->id)) && ((device->wined3d->flags & WINED3D_NO3D)
322 || !is_identity_fixup(src_format->color_fixup) || src_format->conv_byte_count
323 || !is_identity_fixup(dst_format->color_fixup) || dst_format->conv_byte_count
324 || ((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
325 && !src_format->decompress)))
327 FIXME("Cannot find a conversion function from format %s to %s.\n",
328 debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
329 return NULL;
332 /* FIXME: Multisampled conversion? */
333 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
334 desc.format = dst_format->id;
335 desc.multisample_type = WINED3D_MULTISAMPLE_NONE;
336 desc.multisample_quality = 0;
337 desc.usage = WINED3DUSAGE_SCRATCH | WINED3DUSAGE_PRIVATE;
338 desc.bind_flags = 0;
339 desc.access = WINED3D_RESOURCE_ACCESS_CPU | WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W;
340 desc.width = wined3d_texture_get_level_width(src_texture, texture_level);
341 desc.height = wined3d_texture_get_level_height(src_texture, texture_level);
342 desc.depth = 1;
343 desc.size = 0;
344 if (FAILED(wined3d_texture_create(device, &desc, 1, 1, WINED3D_TEXTURE_CREATE_DISCARD,
345 NULL, NULL, &wined3d_null_parent_ops, &dst_texture)))
347 ERR("Failed to create a destination texture for conversion.\n");
348 return NULL;
351 context = context_acquire(device, NULL, 0);
353 map_binding = src_texture->resource.map_binding;
354 if (!wined3d_texture_load_location(src_texture, sub_resource_idx, context, map_binding))
355 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
356 wined3d_texture_get_pitch(src_texture, texture_level, &src_row_pitch, &src_slice_pitch);
357 wined3d_texture_get_memory(src_texture, sub_resource_idx, &src_data, map_binding);
359 if (conv)
361 unsigned int dst_row_pitch, dst_slice_pitch;
362 struct wined3d_bo_address dst_data;
363 struct wined3d_range range;
364 const BYTE *src;
365 BYTE *dst;
367 map_binding = dst_texture->resource.map_binding;
368 if (!wined3d_texture_load_location(dst_texture, 0, context, map_binding))
369 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
370 wined3d_texture_get_pitch(dst_texture, 0, &dst_row_pitch, &dst_slice_pitch);
371 wined3d_texture_get_memory(dst_texture, 0, &dst_data, map_binding);
373 src = wined3d_context_map_bo_address(context, &src_data,
374 src_texture->sub_resources[sub_resource_idx].size, WINED3D_MAP_READ);
375 dst = wined3d_context_map_bo_address(context, &dst_data,
376 dst_texture->sub_resources[0].size, WINED3D_MAP_WRITE);
378 conv->convert(src, dst, src_row_pitch, dst_row_pitch, desc.width, desc.height);
380 range.offset = 0;
381 range.size = dst_texture->sub_resources[0].size;
382 wined3d_texture_invalidate_location(dst_texture, 0, ~map_binding);
383 wined3d_context_unmap_bo_address(context, &dst_data, 1, &range);
384 wined3d_context_unmap_bo_address(context, &src_data, 0, NULL);
386 else
388 struct wined3d_box src_box = {0, 0, desc.width, desc.height, 0, 1};
390 TRACE("Using upload conversion.\n");
392 wined3d_texture_prepare_location(dst_texture, 0, context, WINED3D_LOCATION_TEXTURE_RGB);
393 dst_texture->texture_ops->texture_upload_data(context, wined3d_const_bo_address(&src_data),
394 src_format, &src_box, src_row_pitch, src_slice_pitch,
395 dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB, 0, 0, 0);
397 wined3d_texture_validate_location(dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB);
398 wined3d_texture_invalidate_location(dst_texture, 0, ~WINED3D_LOCATION_TEXTURE_RGB);
401 context_release(context);
403 return dst_texture;
406 void texture2d_read_from_framebuffer(struct wined3d_texture *texture, unsigned int sub_resource_idx,
407 struct wined3d_context *context, DWORD src_location, DWORD dst_location)
409 struct wined3d_resource *resource = &texture->resource;
410 struct wined3d_device *device = resource->device;
411 const struct wined3d_format_gl *format_gl;
412 struct wined3d_texture *restore_texture;
413 const struct wined3d_gl_info *gl_info;
414 struct wined3d_context_gl *context_gl;
415 unsigned int row_pitch, slice_pitch;
416 unsigned int width, height, level;
417 struct wined3d_bo_address data;
418 unsigned int restore_idx;
419 BYTE *row, *top, *bottom;
420 BOOL src_is_upside_down;
421 BYTE *mem = NULL;
422 uint8_t *offset;
423 unsigned int i;
425 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
426 offset = data.addr;
428 restore_texture = context->current_rt.texture;
429 restore_idx = context->current_rt.sub_resource_idx;
430 if (restore_texture != texture || restore_idx != sub_resource_idx)
431 context = context_acquire(device, texture, sub_resource_idx);
432 else
433 restore_texture = NULL;
434 context_gl = wined3d_context_gl(context);
435 gl_info = context_gl->gl_info;
437 if (src_location != resource->draw_binding)
439 wined3d_context_gl_apply_fbo_state_blit(context_gl, GL_READ_FRAMEBUFFER,
440 resource, sub_resource_idx, NULL, 0, src_location);
441 wined3d_context_gl_check_fbo_status(context_gl, GL_READ_FRAMEBUFFER);
442 context_invalidate_state(context, STATE_FRAMEBUFFER);
444 else
446 wined3d_context_gl_apply_blit_state(context_gl, device);
449 /* Select the correct read buffer, and give some debug output.
450 * There is no need to keep track of the current read buffer or reset it,
451 * every part of the code that reads sets the read buffer as desired.
453 if (src_location != WINED3D_LOCATION_DRAWABLE || wined3d_resource_is_offscreen(resource))
455 /* Mapping the primary render target which is not on a swapchain.
456 * Read from the back buffer. */
457 TRACE("Mapping offscreen render target.\n");
458 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_context_gl_get_offscreen_gl_buffer(context_gl));
459 src_is_upside_down = TRUE;
461 else
463 /* Onscreen surfaces are always part of a swapchain */
464 GLenum buffer = wined3d_texture_get_gl_buffer(texture);
465 TRACE("Mapping %#x buffer.\n", buffer);
466 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
467 src_is_upside_down = FALSE;
469 checkGLcall("glReadBuffer");
471 if (data.buffer_object)
473 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, wined3d_bo_gl(data.buffer_object)->id));
474 checkGLcall("glBindBuffer");
475 offset += data.buffer_object->buffer_offset;
477 else
479 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
480 checkGLcall("glBindBuffer");
483 level = sub_resource_idx % texture->level_count;
484 wined3d_texture_get_pitch(texture, level, &row_pitch, &slice_pitch);
485 format_gl = wined3d_format_gl(resource->format);
487 /* Setup pixel store pack state -- to glReadPixels into the correct place */
488 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / format_gl->f.byte_count);
489 checkGLcall("glPixelStorei");
491 width = wined3d_texture_get_level_width(texture, level);
492 height = wined3d_texture_get_level_height(texture, level);
493 gl_info->gl_ops.gl.p_glReadPixels(0, 0, width, height,
494 format_gl->format, format_gl->type, offset);
495 checkGLcall("glReadPixels");
497 /* Reset previous pixel store pack state */
498 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
499 checkGLcall("glPixelStorei");
501 if (!src_is_upside_down)
503 /* glReadPixels returns the image upside down, and there is no way to
504 * prevent this. Flip the lines in software. */
506 if (!(row = heap_alloc(row_pitch)))
507 goto error;
509 if (data.buffer_object)
511 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
512 checkGLcall("glMapBuffer");
514 mem += (uintptr_t)offset;
516 top = mem;
517 bottom = mem + row_pitch * (height - 1);
518 for (i = 0; i < height / 2; i++)
520 memcpy(row, top, row_pitch);
521 memcpy(top, bottom, row_pitch);
522 memcpy(bottom, row, row_pitch);
523 top += row_pitch;
524 bottom -= row_pitch;
526 heap_free(row);
528 if (data.buffer_object)
529 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
532 error:
533 if (data.buffer_object)
535 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
536 wined3d_context_gl_reference_bo(context_gl, wined3d_bo_gl(data.buffer_object));
537 checkGLcall("glBindBuffer");
540 if (restore_texture)
541 context_restore(context, restore_texture, restore_idx);
544 /* Read the framebuffer contents into a texture. Note that this function
545 * doesn't do any kind of flipping. Using this on an onscreen surface will
546 * result in a flipped D3D texture.
548 * Context activation is done by the caller. This function may temporarily
549 * switch to a different context and restore the original one before return. */
550 void texture2d_load_fb_texture(struct wined3d_texture_gl *texture_gl,
551 unsigned int sub_resource_idx, BOOL srgb, struct wined3d_context *context)
553 struct wined3d_texture *restore_texture;
554 const struct wined3d_gl_info *gl_info;
555 struct wined3d_context_gl *context_gl;
556 struct wined3d_resource *resource;
557 unsigned int restore_idx, level;
558 struct wined3d_device *device;
559 GLenum target;
561 resource = &texture_gl->t.resource;
562 device = resource->device;
563 restore_texture = context->current_rt.texture;
564 restore_idx = context->current_rt.sub_resource_idx;
565 if (restore_texture != &texture_gl->t || restore_idx != sub_resource_idx)
566 context = context_acquire(device, &texture_gl->t, sub_resource_idx);
567 else
568 restore_texture = NULL;
569 context_gl = wined3d_context_gl(context);
571 gl_info = context_gl->gl_info;
572 device_invalidate_state(device, STATE_FRAMEBUFFER);
574 wined3d_texture_gl_prepare_texture(texture_gl, context_gl, srgb);
575 wined3d_texture_gl_bind_and_dirtify(texture_gl, context_gl, srgb);
577 TRACE("Reading back offscreen render target %p, %u.\n", texture_gl, sub_resource_idx);
579 if (wined3d_resource_is_offscreen(resource))
580 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_context_gl_get_offscreen_gl_buffer(context_gl));
581 else
582 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(&texture_gl->t));
583 checkGLcall("glReadBuffer");
585 level = sub_resource_idx % texture_gl->t.level_count;
586 target = wined3d_texture_gl_get_sub_resource_target(texture_gl, sub_resource_idx);
587 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(target, level, 0, 0, 0, 0,
588 wined3d_texture_get_level_width(&texture_gl->t, level),
589 wined3d_texture_get_level_height(&texture_gl->t, level));
590 checkGLcall("glCopyTexSubImage2D");
592 if (restore_texture)
593 context_restore(context, restore_texture, restore_idx);
596 /* Context activation is done by the caller. */
597 static void cpu_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
599 struct wined3d_blitter *next;
601 if ((next = blitter->next))
602 next->ops->blitter_destroy(next, context);
604 heap_free(blitter);
607 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
608 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
609 const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx)
611 UINT row_block_count;
612 const BYTE *src_row;
613 BYTE *dst_row;
614 UINT x, y;
616 src_row = src_data;
617 dst_row = dst_data;
619 row_block_count = (update_w + format->block_width - 1) / format->block_width;
621 if (!flags)
623 for (y = 0; y < update_h; y += format->block_height)
625 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
626 src_row += src_pitch;
627 dst_row += dst_pitch;
630 return WINED3D_OK;
633 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
635 src_row += (((update_h / format->block_height) - 1) * src_pitch);
637 switch (format->id)
639 case WINED3DFMT_DXT1:
640 for (y = 0; y < update_h; y += format->block_height)
642 struct block
644 WORD color[2];
645 BYTE control_row[4];
648 const struct block *s = (const struct block *)src_row;
649 struct block *d = (struct block *)dst_row;
651 for (x = 0; x < row_block_count; ++x)
653 d[x].color[0] = s[x].color[0];
654 d[x].color[1] = s[x].color[1];
655 d[x].control_row[0] = s[x].control_row[3];
656 d[x].control_row[1] = s[x].control_row[2];
657 d[x].control_row[2] = s[x].control_row[1];
658 d[x].control_row[3] = s[x].control_row[0];
660 src_row -= src_pitch;
661 dst_row += dst_pitch;
663 return WINED3D_OK;
665 case WINED3DFMT_DXT2:
666 case WINED3DFMT_DXT3:
667 for (y = 0; y < update_h; y += format->block_height)
669 struct block
671 WORD alpha_row[4];
672 WORD color[2];
673 BYTE control_row[4];
676 const struct block *s = (const struct block *)src_row;
677 struct block *d = (struct block *)dst_row;
679 for (x = 0; x < row_block_count; ++x)
681 d[x].alpha_row[0] = s[x].alpha_row[3];
682 d[x].alpha_row[1] = s[x].alpha_row[2];
683 d[x].alpha_row[2] = s[x].alpha_row[1];
684 d[x].alpha_row[3] = s[x].alpha_row[0];
685 d[x].color[0] = s[x].color[0];
686 d[x].color[1] = s[x].color[1];
687 d[x].control_row[0] = s[x].control_row[3];
688 d[x].control_row[1] = s[x].control_row[2];
689 d[x].control_row[2] = s[x].control_row[1];
690 d[x].control_row[3] = s[x].control_row[0];
692 src_row -= src_pitch;
693 dst_row += dst_pitch;
695 return WINED3D_OK;
697 default:
698 FIXME("Compressed flip not implemented for format %s.\n",
699 debug_d3dformat(format->id));
700 return E_NOTIMPL;
704 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
705 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
707 return E_NOTIMPL;
710 static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
711 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
712 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
713 enum wined3d_texture_filter_type filter)
715 unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count;
716 struct wined3d_device *device = dst_texture->resource.device;
717 const struct wined3d_format *src_format, *dst_format;
718 struct wined3d_texture *converted_texture = NULL;
719 struct wined3d_bo_address src_data, dst_data;
720 unsigned int src_fmt_flags, dst_fmt_flags;
721 struct wined3d_map_desc dst_map, src_map;
722 unsigned int x, sx, xinc, y, sy, yinc;
723 struct wined3d_context *context;
724 struct wined3d_range dst_range;
725 unsigned int texture_level;
726 HRESULT hr = WINED3D_OK;
727 BOOL same_sub_resource;
728 BOOL upload = FALSE;
729 DWORD map_binding;
730 const BYTE *sbase;
731 const BYTE *sbuf;
732 BYTE *dbuf;
734 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
735 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
736 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture,
737 src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
739 context = context_acquire(device, NULL, 0);
741 src_format = src_texture->resource.format;
742 dst_format = dst_texture->resource.format;
744 if (wined3d_format_is_typeless(src_format) && src_format->id == dst_format->typeless_id)
745 src_format = dst_format;
746 if (wined3d_format_is_typeless(dst_format) && dst_format->id == src_format->typeless_id)
747 dst_format = src_format;
749 src_height = src_box->bottom - src_box->top;
750 src_width = src_box->right - src_box->left;
751 dst_height = dst_box->bottom - dst_box->top;
752 dst_width = dst_box->right - dst_box->left;
754 dst_range.offset = 0;
755 dst_range.size = dst_texture->sub_resources[dst_sub_resource_idx].size;
756 if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx)
758 same_sub_resource = TRUE;
760 map_binding = dst_texture->resource.map_binding;
761 texture_level = dst_sub_resource_idx % dst_texture->level_count;
762 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
763 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
764 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
765 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
766 wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
767 dst_map.data = wined3d_context_map_bo_address(context, &dst_data,
768 dst_texture->sub_resources[dst_sub_resource_idx].size, WINED3D_MAP_READ | WINED3D_MAP_WRITE);
770 src_map = dst_map;
772 else
774 same_sub_resource = FALSE;
775 upload = dst_format->flags[dst_texture->resource.gl_type] & WINED3DFMT_FLAG_BLOCKS
776 && (dst_width != src_width || dst_height != src_height);
778 if (upload)
780 dst_format = src_format->flags[dst_texture->resource.gl_type] & WINED3DFMT_FLAG_BLOCKS
781 ? wined3d_get_format(device->adapter, WINED3DFMT_B8G8R8A8_UNORM, 0) : src_format;
784 if (!(flags & WINED3D_BLT_RAW) && dst_format->id != src_format->id)
786 if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format)))
788 FIXME("Cannot convert %s to %s.\n", debug_d3dformat(src_format->id),
789 debug_d3dformat(dst_format->id));
790 context_release(context);
791 return WINED3DERR_NOTAVAILABLE;
793 src_texture = converted_texture;
794 src_sub_resource_idx = 0;
795 src_format = src_texture->resource.format;
798 map_binding = src_texture->resource.map_binding;
799 texture_level = src_sub_resource_idx % src_texture->level_count;
800 if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, map_binding))
801 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
802 wined3d_texture_get_pitch(src_texture, texture_level, &src_map.row_pitch, &src_map.slice_pitch);
803 wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &src_data, map_binding);
804 src_map.data = wined3d_context_map_bo_address(context, &src_data,
805 src_texture->sub_resources[src_sub_resource_idx].size, WINED3D_MAP_READ);
807 if (upload)
809 wined3d_format_calculate_pitch(dst_format, 1, dst_box->right, dst_box->bottom,
810 &dst_map.row_pitch, &dst_map.slice_pitch);
811 dst_map.data = heap_alloc(dst_map.slice_pitch);
813 else
815 map_binding = dst_texture->resource.map_binding;
816 texture_level = dst_sub_resource_idx % dst_texture->level_count;
817 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
818 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
820 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
821 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
822 wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
823 dst_map.data = wined3d_context_map_bo_address(context, &dst_data,
824 dst_texture->sub_resources[dst_sub_resource_idx].size, WINED3D_MAP_WRITE);
827 src_fmt_flags = src_format->flags[src_texture->resource.gl_type];
828 dst_fmt_flags = dst_format->flags[dst_texture->resource.gl_type];
829 flags &= ~WINED3D_BLT_RAW;
831 bpp = dst_format->byte_count;
832 row_byte_count = dst_width * bpp;
834 sbase = (BYTE *)src_map.data
835 + ((src_box->top / src_format->block_height) * src_map.row_pitch)
836 + ((src_box->left / src_format->block_width) * src_format->block_byte_count);
837 dbuf = (BYTE *)dst_map.data
838 + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch)
839 + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count);
841 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
843 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
845 if (same_sub_resource)
847 FIXME("Only plain blits supported on compressed surfaces.\n");
848 hr = E_NOTIMPL;
849 goto release;
852 hr = surface_cpu_blt_compressed(sbase, dbuf,
853 src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height,
854 src_format, flags, fx);
855 goto release;
858 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
859 && (src_width != dst_width || src_height != dst_height))
861 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
862 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
865 xinc = (src_width << 16) / dst_width;
866 yinc = (src_height << 16) / dst_height;
868 if (!flags)
870 /* No effects, we can cheat here. */
871 if (dst_width == src_width)
873 if (dst_height == src_height)
875 /* No stretching in either direction. This needs to be as fast
876 * as possible. */
877 sbuf = sbase;
879 /* Check for overlapping surfaces. */
880 if (!same_sub_resource || dst_box->top < src_box->top
881 || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
883 /* No overlap, or dst above src, so copy from top downwards. */
884 for (y = 0; y < dst_height; ++y)
886 memcpy(dbuf, sbuf, row_byte_count);
887 sbuf += src_map.row_pitch;
888 dbuf += dst_map.row_pitch;
891 else if (dst_box->top > src_box->top)
893 /* Copy from bottom upwards. */
894 sbuf += src_map.row_pitch * dst_height;
895 dbuf += dst_map.row_pitch * dst_height;
896 for (y = 0; y < dst_height; ++y)
898 sbuf -= src_map.row_pitch;
899 dbuf -= dst_map.row_pitch;
900 memcpy(dbuf, sbuf, row_byte_count);
903 else
905 /* Src and dst overlapping on the same line, use memmove. */
906 for (y = 0; y < dst_height; ++y)
908 memmove(dbuf, sbuf, row_byte_count);
909 sbuf += src_map.row_pitch;
910 dbuf += dst_map.row_pitch;
914 else
916 /* Stretching in y direction only. */
917 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
919 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
920 memcpy(dbuf, sbuf, row_byte_count);
921 dbuf += dst_map.row_pitch;
925 else
927 /* Stretching in X direction. */
928 unsigned int last_sy = ~0u;
929 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
931 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
933 if ((sy >> 16) == (last_sy >> 16))
935 /* This source row is the same as last source row -
936 * Copy the already stretched row. */
937 memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count);
939 else
941 #define STRETCH_ROW(type) \
942 do { \
943 const type *s = (const type *)sbuf; \
944 type *d = (type *)dbuf; \
945 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
946 d[x] = s[sx >> 16]; \
947 } while(0)
949 switch(bpp)
951 case 1:
952 STRETCH_ROW(BYTE);
953 break;
954 case 2:
955 STRETCH_ROW(WORD);
956 break;
957 case 4:
958 STRETCH_ROW(DWORD);
959 break;
960 case 3:
962 const BYTE *s;
963 BYTE *d = dbuf;
964 for (x = sx = 0; x < dst_width; x++, sx+= xinc)
966 DWORD pixel;
968 s = sbuf + 3 * (sx >> 16);
969 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
970 d[0] = (pixel ) & 0xff;
971 d[1] = (pixel >> 8) & 0xff;
972 d[2] = (pixel >> 16) & 0xff;
973 d += 3;
975 break;
977 default:
978 FIXME("Stretched blit not implemented for bpp %u.\n", bpp * 8);
979 hr = WINED3DERR_NOTAVAILABLE;
980 goto error;
982 #undef STRETCH_ROW
984 dbuf += dst_map.row_pitch;
985 last_sy = sy;
989 else
991 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
992 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
993 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
994 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
995 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
997 /* The color keying flags are checked for correctness in ddraw. */
998 if (flags & WINED3D_BLT_SRC_CKEY)
1000 keylow = src_texture->async.src_blt_color_key.color_space_low_value;
1001 keyhigh = src_texture->async.src_blt_color_key.color_space_high_value;
1003 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
1005 keylow = fx->src_color_key.color_space_low_value;
1006 keyhigh = fx->src_color_key.color_space_high_value;
1009 if (flags & WINED3D_BLT_DST_CKEY)
1011 /* Destination color keys are taken from the source surface! */
1012 destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value;
1013 destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value;
1015 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
1017 destkeylow = fx->dst_color_key.color_space_low_value;
1018 destkeyhigh = fx->dst_color_key.color_space_high_value;
1021 if (bpp == 1)
1023 keymask = 0xff;
1025 else
1027 DWORD masks[3];
1028 get_color_masks(src_format, masks);
1029 keymask = masks[0] | masks[1] | masks[2];
1031 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
1032 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
1035 if (flags & WINED3D_BLT_FX)
1037 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
1038 LONG tmpxy;
1039 dTopLeft = dbuf;
1040 dTopRight = dbuf + ((dst_width - 1) * bpp);
1041 dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
1042 dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
1044 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
1046 /* I don't think we need to do anything about this flag. */
1047 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
1049 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
1051 tmp = dTopRight;
1052 dTopRight = dTopLeft;
1053 dTopLeft = tmp;
1054 tmp = dBottomRight;
1055 dBottomRight = dBottomLeft;
1056 dBottomLeft = tmp;
1057 dstxinc = dstxinc * -1;
1059 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
1061 tmp = dTopLeft;
1062 dTopLeft = dBottomLeft;
1063 dBottomLeft = tmp;
1064 tmp = dTopRight;
1065 dTopRight = dBottomRight;
1066 dBottomRight = tmp;
1067 dstyinc = dstyinc * -1;
1069 if (fx->fx & WINEDDBLTFX_NOTEARING)
1071 /* I don't think we need to do anything about this flag. */
1072 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
1074 if (fx->fx & WINEDDBLTFX_ROTATE180)
1076 tmp = dBottomRight;
1077 dBottomRight = dTopLeft;
1078 dTopLeft = tmp;
1079 tmp = dBottomLeft;
1080 dBottomLeft = dTopRight;
1081 dTopRight = tmp;
1082 dstxinc = dstxinc * -1;
1083 dstyinc = dstyinc * -1;
1085 if (fx->fx & WINEDDBLTFX_ROTATE270)
1087 tmp = dTopLeft;
1088 dTopLeft = dBottomLeft;
1089 dBottomLeft = dBottomRight;
1090 dBottomRight = dTopRight;
1091 dTopRight = tmp;
1092 tmpxy = dstxinc;
1093 dstxinc = dstyinc;
1094 dstyinc = tmpxy;
1095 dstxinc = dstxinc * -1;
1097 if (fx->fx & WINEDDBLTFX_ROTATE90)
1099 tmp = dTopLeft;
1100 dTopLeft = dTopRight;
1101 dTopRight = dBottomRight;
1102 dBottomRight = dBottomLeft;
1103 dBottomLeft = tmp;
1104 tmpxy = dstxinc;
1105 dstxinc = dstyinc;
1106 dstyinc = tmpxy;
1107 dstyinc = dstyinc * -1;
1109 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
1111 /* I don't think we need to do anything about this flag. */
1112 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
1114 dbuf = dTopLeft;
1115 flags &= ~(WINED3D_BLT_FX);
1118 #define COPY_COLORKEY_FX(type) \
1119 do { \
1120 const type *s; \
1121 type *d = (type *)dbuf, *dx, tmp; \
1122 for (y = sy = 0; y < dst_height; ++y, sy += yinc) \
1124 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
1125 dx = d; \
1126 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
1128 tmp = s[sx >> 16]; \
1129 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
1130 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
1132 dx[0] = tmp; \
1134 dx = (type *)(((BYTE *)dx) + dstxinc); \
1136 d = (type *)(((BYTE *)d) + dstyinc); \
1138 } while(0)
1140 switch (bpp)
1142 case 1:
1143 COPY_COLORKEY_FX(BYTE);
1144 break;
1145 case 2:
1146 COPY_COLORKEY_FX(WORD);
1147 break;
1148 case 4:
1149 COPY_COLORKEY_FX(DWORD);
1150 break;
1151 case 3:
1153 const BYTE *s;
1154 BYTE *d = dbuf, *dx;
1155 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
1157 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
1158 dx = d;
1159 for (x = sx = 0; x < dst_width; ++x, sx+= xinc)
1161 DWORD pixel, dpixel = 0;
1162 s = sbuf + 3 * (sx>>16);
1163 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
1164 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
1165 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
1166 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
1168 dx[0] = (pixel ) & 0xff;
1169 dx[1] = (pixel >> 8) & 0xff;
1170 dx[2] = (pixel >> 16) & 0xff;
1172 dx += dstxinc;
1174 d += dstyinc;
1176 break;
1178 default:
1179 FIXME("%s color-keyed blit not implemented for bpp %u.\n",
1180 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
1181 hr = WINED3DERR_NOTAVAILABLE;
1182 goto error;
1183 #undef COPY_COLORKEY_FX
1187 error:
1188 if (flags)
1189 FIXME(" Unsupported flags %#x.\n", flags);
1191 release:
1192 if (upload && hr == WINED3D_OK)
1194 struct wined3d_bo_address data;
1196 data.buffer_object = 0;
1197 data.addr = dst_map.data;
1199 texture_level = dst_sub_resource_idx % dst_texture->level_count;
1201 wined3d_texture_prepare_location(dst_texture, texture_level, context, WINED3D_LOCATION_TEXTURE_RGB);
1202 dst_texture->texture_ops->texture_upload_data(context, wined3d_const_bo_address(&data), dst_format,
1203 dst_box, dst_map.row_pitch, dst_map.slice_pitch, dst_texture, texture_level,
1204 WINED3D_LOCATION_TEXTURE_RGB, dst_box->left, dst_box->top, 0);
1206 wined3d_texture_validate_location(dst_texture, texture_level, WINED3D_LOCATION_TEXTURE_RGB);
1207 wined3d_texture_invalidate_location(dst_texture, texture_level, ~WINED3D_LOCATION_TEXTURE_RGB);
1210 if (upload)
1212 heap_free(dst_map.data);
1214 else
1216 wined3d_context_unmap_bo_address(context, &dst_data, 1, &dst_range);
1219 if (!same_sub_resource)
1220 wined3d_context_unmap_bo_address(context, &src_data, 0, NULL);
1221 if (SUCCEEDED(hr) && dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture)
1223 SetRect(&dst_texture->swapchain->front_buffer_update,
1224 dst_box->left, dst_box->top, dst_box->right, dst_box->bottom);
1225 dst_texture->swapchain->swapchain_ops->swapchain_frontbuffer_updated(dst_texture->swapchain);
1227 if (converted_texture)
1228 wined3d_texture_decref(converted_texture);
1229 context_release(context);
1231 return hr;
1234 static void surface_cpu_blt_colour_fill(struct wined3d_rendertarget_view *view,
1235 const struct wined3d_box *box, const struct wined3d_color *colour)
1237 struct wined3d_device *device = view->resource->device;
1238 unsigned int x, y, z, w, h, d, bpp, level;
1239 struct wined3d_context *context;
1240 struct wined3d_texture *texture;
1241 struct wined3d_bo_address data;
1242 struct wined3d_map_desc map;
1243 struct wined3d_range range;
1244 DWORD map_binding;
1245 uint8_t *dst;
1246 DWORD c;
1248 TRACE("view %p, box %s, colour %s.\n", view, debug_box(box), debug_color(colour));
1250 if (view->format_flags & WINED3DFMT_FLAG_BLOCKS)
1252 FIXME("Not implemented for format %s.\n", debug_d3dformat(view->format->id));
1253 return;
1256 if (view->format->id != view->resource->format->id)
1257 FIXME("View format %s doesn't match resource format %s.\n",
1258 debug_d3dformat(view->format->id), debug_d3dformat(view->resource->format->id));
1260 if (view->resource->type == WINED3D_RTYPE_BUFFER)
1262 FIXME("Not implemented for buffers.\n");
1263 return;
1266 context = context_acquire(device, NULL, 0);
1268 texture = texture_from_resource(view->resource);
1269 level = view->sub_resource_idx % texture->level_count;
1271 c = wined3d_format_convert_from_float(view->format, colour);
1272 bpp = view->format->byte_count;
1273 w = min(box->right, view->width) - min(box->left, view->width);
1274 h = min(box->bottom, view->height) - min(box->top, view->height);
1275 if (view->resource->type != WINED3D_RTYPE_TEXTURE_3D)
1277 d = 1;
1279 else
1281 d = wined3d_texture_get_level_depth(texture, level);
1282 d = min(box->back, d) - min(box->front, d);
1285 map_binding = texture->resource.map_binding;
1286 if (!wined3d_texture_load_location(texture, view->sub_resource_idx, context, map_binding))
1287 ERR("Failed to load the sub-resource into %s.\n", wined3d_debug_location(map_binding));
1288 wined3d_texture_invalidate_location(texture, view->sub_resource_idx, ~map_binding);
1289 wined3d_texture_get_pitch(texture, level, &map.row_pitch, &map.slice_pitch);
1290 wined3d_texture_get_memory(texture, view->sub_resource_idx, &data, map_binding);
1291 map.data = wined3d_context_map_bo_address(context, &data,
1292 texture->sub_resources[view->sub_resource_idx].size, WINED3D_MAP_WRITE);
1293 map.data = (BYTE *)map.data
1294 + (box->front * map.slice_pitch)
1295 + ((box->top / view->format->block_height) * map.row_pitch)
1296 + ((box->left / view->format->block_width) * view->format->block_byte_count);
1297 range.offset = 0;
1298 range.size = texture->sub_resources[view->sub_resource_idx].size;
1300 switch (bpp)
1302 case 1:
1303 for (x = 0; x < w; ++x)
1305 ((BYTE *)map.data)[x] = c;
1307 break;
1309 case 2:
1310 for (x = 0; x < w; ++x)
1312 ((WORD *)map.data)[x] = c;
1314 break;
1316 case 3:
1318 dst = map.data;
1319 for (x = 0; x < w; ++x, dst += 3)
1321 dst[0] = (c ) & 0xff;
1322 dst[1] = (c >> 8) & 0xff;
1323 dst[2] = (c >> 16) & 0xff;
1325 break;
1327 case 4:
1328 for (x = 0; x < w; ++x)
1330 ((DWORD *)map.data)[x] = c;
1332 break;
1334 default:
1335 FIXME("Not implemented for bpp %u.\n", bpp);
1336 wined3d_resource_unmap(view->resource, view->sub_resource_idx);
1337 return;
1340 dst = map.data;
1341 for (y = 1; y < h; ++y)
1343 dst += map.row_pitch;
1344 memcpy(dst, map.data, w * bpp);
1347 dst = map.data;
1348 for (z = 1; z < d; ++z)
1350 dst += map.slice_pitch;
1351 memcpy(dst, map.data, w * h * bpp);
1354 wined3d_context_unmap_bo_address(context, &data, 1, &range);
1355 context_release(context);
1358 static void cpu_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
1359 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
1360 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
1362 struct wined3d_color c = {depth, 0.0f, 0.0f, 0.0f};
1363 struct wined3d_rendertarget_view *view;
1364 struct wined3d_box box;
1365 unsigned int i, j;
1367 if (!rect_count)
1369 rect_count = 1;
1370 clear_rects = draw_rect;
1373 for (i = 0; i < rect_count; ++i)
1375 box.left = max(clear_rects[i].left, draw_rect->left);
1376 box.top = max(clear_rects[i].top, draw_rect->top);
1377 box.right = min(clear_rects[i].right, draw_rect->right);
1378 box.bottom = min(clear_rects[i].bottom, draw_rect->bottom);
1379 box.front = 0;
1380 box.back = ~0u;
1382 if (box.left >= box.right || box.top >= box.bottom)
1383 continue;
1385 if (flags & WINED3DCLEAR_TARGET)
1387 for (j = 0; j < rt_count; ++j)
1389 if ((view = fb->render_targets[j]))
1390 surface_cpu_blt_colour_fill(view, &box, colour);
1394 if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil))
1396 if ((view->format->depth_size && !(flags & WINED3DCLEAR_ZBUFFER))
1397 || (view->format->stencil_size && !(flags & WINED3DCLEAR_STENCIL)))
1398 FIXME("Clearing %#x on %s.\n", flags, debug_d3dformat(view->format->id));
1400 surface_cpu_blt_colour_fill(view, &box, &c);
1405 static DWORD cpu_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
1406 struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1407 DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture,
1408 unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect,
1409 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter,
1410 const struct wined3d_format *resolve_format)
1412 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
1413 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
1414 struct wined3d_blt_fx fx;
1415 DWORD flags = 0;
1417 memset(&fx, 0, sizeof(fx));
1418 switch (op)
1420 case WINED3D_BLIT_OP_COLOR_BLIT:
1421 case WINED3D_BLIT_OP_DEPTH_BLIT:
1422 break;
1423 case WINED3D_BLIT_OP_RAW_BLIT:
1424 flags |= WINED3D_BLT_RAW;
1425 break;
1426 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
1427 flags |= WINED3D_BLT_ALPHA_TEST;
1428 break;
1429 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
1430 flags |= WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_FX;
1431 fx.src_color_key = *color_key;
1432 break;
1433 default:
1434 FIXME("Unhandled op %#x.\n", op);
1435 break;
1438 if (FAILED(surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
1439 src_texture, src_sub_resource_idx, &src_box, flags, &fx, filter)))
1440 ERR("Failed to blit.\n");
1441 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
1443 return dst_location | (dst_texture->sub_resources[dst_sub_resource_idx].locations
1444 & dst_texture->resource.map_binding);
1447 static const struct wined3d_blitter_ops cpu_blitter_ops =
1449 cpu_blitter_destroy,
1450 cpu_blitter_clear,
1451 cpu_blitter_blit,
1454 struct wined3d_blitter *wined3d_cpu_blitter_create(void)
1456 struct wined3d_blitter *blitter;
1458 if (!(blitter = heap_alloc(sizeof(*blitter))))
1459 return NULL;
1461 TRACE("Created blitter %p.\n", blitter);
1463 blitter->ops = &cpu_blitter_ops;
1464 blitter->next = NULL;
1466 return blitter;
1469 static bool wined3d_is_colour_blit(enum wined3d_blit_op blit_op)
1471 switch (blit_op)
1473 case WINED3D_BLIT_OP_COLOR_BLIT:
1474 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
1475 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
1476 return true;
1478 default:
1479 return false;
1483 HRESULT texture2d_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
1484 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1485 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
1486 enum wined3d_texture_filter_type filter)
1488 struct wined3d_texture_sub_resource *src_sub_resource, *dst_sub_resource;
1489 struct wined3d_device *device = dst_texture->resource.device;
1490 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1491 BOOL scale, convert, resolve, resolve_typeless = FALSE;
1492 const struct wined3d_format *resolve_format = NULL;
1493 const struct wined3d_color_key *colour_key = NULL;
1494 DWORD src_location, dst_location, valid_locations;
1495 struct wined3d_context *context;
1496 enum wined3d_blit_op blit_op;
1497 RECT src_rect, dst_rect;
1498 bool src_ds, dst_ds;
1500 static const DWORD simple_blit = WINED3D_BLT_SRC_CKEY
1501 | WINED3D_BLT_SRC_CKEY_OVERRIDE
1502 | WINED3D_BLT_ALPHA_TEST
1503 | WINED3D_BLT_RAW;
1505 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
1506 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
1507 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture, src_sub_resource_idx,
1508 debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
1509 TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage));
1511 if (fx)
1513 TRACE("fx %#x.\n", fx->fx);
1514 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
1515 fx->dst_color_key.color_space_low_value,
1516 fx->dst_color_key.color_space_high_value);
1517 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
1518 fx->src_color_key.color_space_low_value,
1519 fx->src_color_key.color_space_high_value);
1520 TRACE("resolve_format_id %s.\n", debug_d3dformat(fx->resolve_format_id));
1522 if (fx->resolve_format_id != WINED3DFMT_UNKNOWN)
1523 resolve_format = wined3d_get_format(device->adapter, fx->resolve_format_id, 0);
1526 dst_sub_resource = &dst_texture->sub_resources[dst_sub_resource_idx];
1527 src_sub_resource = &src_texture->sub_resources[src_sub_resource_idx];
1529 if (src_sub_resource->locations & WINED3D_LOCATION_DISCARDED)
1531 WARN("Source sub-resource is discarded, nothing to do.\n");
1532 return WINED3D_OK;
1535 SetRect(&src_rect, src_box->left, src_box->top, src_box->right, src_box->bottom);
1536 SetRect(&dst_rect, dst_box->left, dst_box->top, dst_box->right, dst_box->bottom);
1538 if (!fx || !(fx->fx))
1539 flags &= ~WINED3D_BLT_FX;
1541 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
1542 if (flags & WINED3D_BLT_DO_NOT_WAIT)
1544 static unsigned int once;
1546 if (!once++)
1547 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
1550 flags &= ~(WINED3D_BLT_SYNCHRONOUS | WINED3D_BLT_DO_NOT_WAIT | WINED3D_BLT_WAIT);
1552 if (flags & ~simple_blit)
1554 WARN_(d3d_perf)("Using CPU fallback for complex blit (%#x).\n", flags);
1555 goto cpu;
1558 src_swapchain = src_texture->swapchain;
1559 dst_swapchain = dst_texture->swapchain;
1561 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain
1562 && (wined3d_settings.offscreen_rendering_mode != ORM_FBO
1563 || src_texture == src_swapchain->front_buffer))
1565 /* TODO: We could support cross-swapchain blits by first downloading
1566 * the source to a texture. */
1567 FIXME("Cross-swapchain blit not supported.\n");
1568 return WINED3DERR_INVALIDCALL;
1571 scale = src_box->right - src_box->left != dst_box->right - dst_box->left
1572 || src_box->bottom - src_box->top != dst_box->bottom - dst_box->top;
1573 convert = src_texture->resource.format->id != dst_texture->resource.format->id;
1574 resolve = src_texture->resource.multisample_type != dst_texture->resource.multisample_type;
1575 if (resolve)
1577 resolve_typeless = (wined3d_format_is_typeless(src_texture->resource.format)
1578 || wined3d_format_is_typeless(dst_texture->resource.format))
1579 && (src_texture->resource.format->typeless_id == dst_texture->resource.format->typeless_id);
1580 if (resolve_typeless && !resolve_format)
1581 WARN("Resolve format for typeless resolve not specified.\n");
1584 dst_ds = dst_texture->resource.format->depth_size || dst_texture->resource.format->stencil_size;
1585 src_ds = src_texture->resource.format->depth_size || src_texture->resource.format->stencil_size;
1587 if (src_ds || dst_ds)
1589 TRACE("Depth/stencil blit.\n");
1591 if (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)
1592 dst_location = dst_texture->resource.draw_binding;
1593 else
1594 dst_location = dst_texture->resource.map_binding;
1596 if ((flags & WINED3D_BLT_RAW) || (!scale && !convert && !resolve))
1597 blit_op = WINED3D_BLIT_OP_RAW_BLIT;
1598 else
1599 blit_op = WINED3D_BLIT_OP_DEPTH_BLIT;
1601 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
1602 valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context,
1603 src_texture, src_sub_resource_idx, src_texture->resource.draw_binding, &src_rect,
1604 dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, NULL, filter, resolve_format);
1605 context_release(context);
1607 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
1608 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
1610 return WINED3D_OK;
1613 TRACE("Colour blit.\n");
1615 /* In principle this would apply to depth blits as well, but we don't
1616 * implement those in the CPU blitter at the moment. */
1617 if ((dst_sub_resource->locations & dst_texture->resource.map_binding)
1618 && (src_sub_resource->locations & src_texture->resource.map_binding))
1620 if (scale)
1621 TRACE("Not doing sysmem blit because of scaling.\n");
1622 else if (convert)
1623 TRACE("Not doing sysmem blit because of format conversion.\n");
1624 else
1625 goto cpu;
1628 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
1629 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
1631 colour_key = &fx->src_color_key;
1632 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
1634 else if (flags & WINED3D_BLT_SRC_CKEY)
1636 colour_key = &src_texture->async.src_blt_color_key;
1637 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
1639 else if (flags & WINED3D_BLT_ALPHA_TEST)
1641 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
1643 else if ((src_sub_resource->locations & surface_simple_locations)
1644 && !(dst_sub_resource->locations & surface_simple_locations)
1645 && (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1647 /* Upload */
1648 if (scale)
1649 TRACE("Not doing upload because of scaling.\n");
1650 else if (convert)
1651 TRACE("Not doing upload because of format conversion.\n");
1652 else if (dst_texture->resource.format->conv_byte_count)
1653 TRACE("Not doing upload because the destination format needs conversion.\n");
1654 else
1656 wined3d_texture_upload_from_texture(dst_texture, dst_sub_resource_idx, dst_box->left,
1657 dst_box->top, dst_box->front, src_texture, src_sub_resource_idx, src_box);
1658 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
1660 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
1661 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx,
1662 context, dst_texture->resource.draw_binding);
1663 context_release(context);
1665 return WINED3D_OK;
1668 else if (!(src_sub_resource->locations & surface_simple_locations)
1669 && (dst_sub_resource->locations & dst_texture->resource.map_binding)
1670 && !(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1672 /* Download */
1673 if (scale)
1674 TRACE("Not doing download because of scaling.\n");
1675 else if (convert)
1676 TRACE("Not doing download because of format conversion.\n");
1677 else if (src_texture->resource.format->conv_byte_count)
1678 TRACE("Not doing download because the source format needs conversion.\n");
1679 else if (!(src_texture->flags & WINED3D_TEXTURE_DOWNLOADABLE))
1680 TRACE("Not doing download because texture is not downloadable.\n");
1681 else if (!wined3d_texture_is_full_rect(src_texture, src_sub_resource_idx % src_texture->level_count, &src_rect))
1682 TRACE("Not doing download because of partial download (src).\n");
1683 else if (!wined3d_texture_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, &dst_rect))
1684 TRACE("Not doing download because of partial download (dst).\n");
1685 else
1687 wined3d_texture_download_from_texture(dst_texture, dst_sub_resource_idx, src_texture,
1688 src_sub_resource_idx);
1689 return WINED3D_OK;
1692 else if (dst_swapchain && dst_swapchain->back_buffers
1693 && dst_texture == dst_swapchain->front_buffer
1694 && src_texture == dst_swapchain->back_buffers[0])
1696 /* Use present for back -> front blits. The idea behind this is that
1697 * present is potentially faster than a blit, in particular when FBO
1698 * blits aren't available. Some ddraw applications like Half-Life and
1699 * Prince of Persia 3D use Blt() from the backbuffer to the
1700 * frontbuffer instead of doing a Flip(). D3d8 and d3d9 applications
1701 * can't blit directly to the frontbuffer. */
1702 enum wined3d_swap_effect swap_effect = dst_swapchain->state.desc.swap_effect;
1704 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
1706 /* Set the swap effect to COPY, we don't want the backbuffer to become
1707 * undefined. */
1708 dst_swapchain->state.desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
1709 wined3d_swapchain_present(dst_swapchain, NULL, NULL,
1710 dst_swapchain->win_handle, dst_swapchain->swap_interval, 0);
1711 dst_swapchain->state.desc.swap_effect = swap_effect;
1713 return WINED3D_OK;
1716 if ((flags & WINED3D_BLT_RAW) || (blit_op == WINED3D_BLIT_OP_COLOR_BLIT && !scale && !convert && !resolve))
1717 blit_op = WINED3D_BLIT_OP_RAW_BLIT;
1719 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
1721 if (src_texture->resource.multisample_type != WINED3D_MULTISAMPLE_NONE && !resolve_typeless
1722 && ((scale && !context->d3d_info->scaled_resolve)
1723 || convert || !wined3d_is_colour_blit(blit_op)))
1724 src_location = WINED3D_LOCATION_RB_RESOLVED;
1725 else
1726 src_location = src_texture->resource.draw_binding;
1728 if (!(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1729 dst_location = dst_texture->resource.map_binding;
1730 else if (dst_texture->resource.multisample_type != WINED3D_MULTISAMPLE_NONE
1731 && (scale || convert || !wined3d_is_colour_blit(blit_op)))
1732 dst_location = WINED3D_LOCATION_RB_RESOLVED;
1733 else
1734 dst_location = dst_texture->resource.draw_binding;
1736 valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context,
1737 src_texture, src_sub_resource_idx, src_location, &src_rect,
1738 dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, colour_key, filter, resolve_format);
1740 context_release(context);
1742 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
1743 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
1745 return WINED3D_OK;
1747 cpu:
1748 return surface_cpu_blt(dst_texture, dst_sub_resource_idx, dst_box,
1749 src_texture, src_sub_resource_idx, src_box, flags, fx, filter);