ntdll: Validate blocks in the heap pending free request list.
[wine.git] / dlls / wined3d / surface.c
blobfd8db9ce60de52314ab3acf0761c0bd55ac92503
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 "wined3d_private.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
32 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
34 /* Works correctly only for <= 4 bpp formats. */
35 static void get_color_masks(const struct wined3d_format *format, uint32_t *masks)
37 masks[0] = wined3d_mask_from_size(format->red_size) << format->red_offset;
38 masks[1] = wined3d_mask_from_size(format->green_size) << format->green_offset;
39 masks[2] = wined3d_mask_from_size(format->blue_size) << format->blue_offset;
42 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
43 unsigned int pitch_in, unsigned int pitch_out, unsigned int w, unsigned int h)
45 unsigned short *dst_s;
46 const float *src_f;
47 unsigned int x, y;
49 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
51 for (y = 0; y < h; ++y)
53 src_f = (const float *)(src + y * pitch_in);
54 dst_s = (unsigned short *) (dst + y * pitch_out);
55 for (x = 0; x < w; ++x)
57 dst_s[x] = float_32_to_16(src_f + x);
62 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
63 unsigned int pitch_in, unsigned int pitch_out, unsigned int w, unsigned int h)
65 static const unsigned char convert_5to8[] =
67 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
68 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
69 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
70 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
72 static const unsigned char convert_6to8[] =
74 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
75 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
76 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
77 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
78 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
79 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
80 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
81 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
83 unsigned int x, y;
85 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
87 for (y = 0; y < h; ++y)
89 const WORD *src_line = (const WORD *)(src + y * pitch_in);
90 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
91 for (x = 0; x < w; ++x)
93 WORD pixel = src_line[x];
94 dst_line[x] = 0xff000000u
95 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
96 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
97 | convert_5to8[(pixel & 0x001fu)];
102 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
103 * in both cases we're just setting the X / Alpha channel to 0xff. */
104 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
105 unsigned int pitch_in, unsigned int pitch_out, unsigned int w, unsigned int h)
107 unsigned int x, y;
109 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
111 for (y = 0; y < h; ++y)
113 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
114 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
116 for (x = 0; x < w; ++x)
118 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
123 static inline BYTE cliptobyte(int x)
125 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
128 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
129 unsigned int pitch_in, unsigned int pitch_out, unsigned int w, unsigned int h)
131 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
132 unsigned int x, y;
134 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
136 for (y = 0; y < h; ++y)
138 const BYTE *src_line = src + y * pitch_in;
139 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
140 for (x = 0; x < w; ++x)
142 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
143 * C = Y - 16; D = U - 128; E = V - 128;
144 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
145 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
146 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
147 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
148 * U and V are shared between the pixels. */
149 if (!(x & 1)) /* For every even pixel, read new U and V. */
151 d = (int) src_line[1] - 128;
152 e = (int) src_line[3] - 128;
153 r2 = 409 * e + 128;
154 g2 = - 100 * d - 208 * e + 128;
155 b2 = 516 * d + 128;
157 c2 = 298 * ((int) src_line[0] - 16);
158 dst_line[x] = 0xff000000
159 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
160 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
161 | cliptobyte((c2 + b2) >> 8); /* blue */
162 /* Scale RGB values to 0..255 range,
163 * then clip them if still not in range (may be negative),
164 * then shift them within DWORD if necessary. */
165 src_line += 2;
170 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
171 unsigned int pitch_in, unsigned int pitch_out, unsigned int w, unsigned int h)
173 unsigned int x, y;
174 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
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 BYTE *src_line = src + y * pitch_in;
181 WORD *dst_line = (WORD *)(dst + y * pitch_out);
182 for (x = 0; x < w; ++x)
184 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
185 * C = Y - 16; D = U - 128; E = V - 128;
186 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
187 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
188 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
189 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
190 * U and V are shared between the pixels. */
191 if (!(x & 1)) /* For every even pixel, read new U and V. */
193 d = (int) src_line[1] - 128;
194 e = (int) src_line[3] - 128;
195 r2 = 409 * e + 128;
196 g2 = - 100 * d - 208 * e + 128;
197 b2 = 516 * d + 128;
199 c2 = 298 * ((int) src_line[0] - 16);
200 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
201 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
202 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
203 /* Scale RGB values to 0..255 range,
204 * then clip them if still not in range (may be negative),
205 * then shift them within DWORD if necessary. */
206 src_line += 2;
211 struct d3dfmt_converter_desc
213 enum wined3d_format_id from, to;
214 void (*convert)(const BYTE *src, BYTE *dst,
215 unsigned int pitch_in, unsigned int pitch_out,
216 unsigned int w, unsigned int h);
219 static const struct d3dfmt_converter_desc converters[] =
221 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
222 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
223 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
224 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
225 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
226 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
229 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
230 enum wined3d_format_id to)
232 unsigned int i;
234 for (i = 0; i < ARRAY_SIZE(converters); ++i)
236 if (converters[i].from == from && converters[i].to == to)
237 return &converters[i];
240 return NULL;
243 static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture,
244 unsigned int sub_resource_idx, const struct wined3d_format *dst_format)
246 unsigned int texture_level = sub_resource_idx % src_texture->level_count;
247 const struct wined3d_format *src_format = src_texture->resource.format;
248 struct wined3d_device *device = src_texture->resource.device;
249 const struct d3dfmt_converter_desc *conv = NULL;
250 unsigned int src_row_pitch, src_slice_pitch;
251 struct wined3d_texture *dst_texture;
252 struct wined3d_bo_address src_data;
253 struct wined3d_resource_desc desc;
254 struct wined3d_context *context;
255 DWORD map_binding;
257 if (!(conv = find_converter(src_format->id, dst_format->id)) && ((device->wined3d->flags & WINED3D_NO3D)
258 || !is_identity_fixup(src_format->color_fixup) || src_format->conv_byte_count
259 || !is_identity_fixup(dst_format->color_fixup) || dst_format->conv_byte_count
260 || ((src_format->attrs & WINED3D_FORMAT_ATTR_COMPRESSED)
261 && !src_format->decompress)))
263 FIXME("Cannot find a conversion function from format %s to %s.\n",
264 debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
265 return NULL;
268 /* FIXME: Multisampled conversion? */
269 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
270 desc.format = dst_format->id;
271 desc.multisample_type = WINED3D_MULTISAMPLE_NONE;
272 desc.multisample_quality = 0;
273 desc.usage = WINED3DUSAGE_SCRATCH | WINED3DUSAGE_PRIVATE;
274 desc.bind_flags = 0;
275 desc.access = WINED3D_RESOURCE_ACCESS_CPU | WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W;
276 desc.width = wined3d_texture_get_level_width(src_texture, texture_level);
277 desc.height = wined3d_texture_get_level_height(src_texture, texture_level);
278 desc.depth = 1;
279 desc.size = 0;
280 if (FAILED(wined3d_texture_create(device, &desc, 1, 1, WINED3D_TEXTURE_CREATE_DISCARD,
281 NULL, NULL, &wined3d_null_parent_ops, &dst_texture)))
283 ERR("Failed to create a destination texture for conversion.\n");
284 return NULL;
287 context = context_acquire(device, NULL, 0);
289 map_binding = src_texture->resource.map_binding;
290 if (!wined3d_texture_load_location(src_texture, sub_resource_idx, context, map_binding))
291 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
292 wined3d_texture_get_pitch(src_texture, texture_level, &src_row_pitch, &src_slice_pitch);
293 wined3d_texture_get_bo_address(src_texture, sub_resource_idx, &src_data, map_binding);
295 if (conv)
297 unsigned int dst_row_pitch, dst_slice_pitch;
298 struct wined3d_bo_address dst_data;
299 struct wined3d_range range;
300 const BYTE *src;
301 BYTE *dst;
303 map_binding = dst_texture->resource.map_binding;
304 if (!wined3d_texture_load_location(dst_texture, 0, context, map_binding))
305 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
306 wined3d_texture_get_pitch(dst_texture, 0, &dst_row_pitch, &dst_slice_pitch);
307 wined3d_texture_get_bo_address(dst_texture, 0, &dst_data, map_binding);
309 src = wined3d_context_map_bo_address(context, &src_data,
310 src_texture->sub_resources[sub_resource_idx].size, WINED3D_MAP_READ);
311 dst = wined3d_context_map_bo_address(context, &dst_data,
312 dst_texture->sub_resources[0].size, WINED3D_MAP_WRITE);
314 conv->convert(src, dst, src_row_pitch, dst_row_pitch, desc.width, desc.height);
316 range.offset = 0;
317 range.size = dst_texture->sub_resources[0].size;
318 wined3d_texture_invalidate_location(dst_texture, 0, ~map_binding);
319 wined3d_context_unmap_bo_address(context, &dst_data, 1, &range);
320 wined3d_context_unmap_bo_address(context, &src_data, 0, NULL);
322 else
324 struct wined3d_box src_box = {0, 0, desc.width, desc.height, 0, 1};
326 TRACE("Using upload conversion.\n");
328 wined3d_texture_prepare_location(dst_texture, 0, context, WINED3D_LOCATION_TEXTURE_RGB);
329 dst_texture->texture_ops->texture_upload_data(context, wined3d_const_bo_address(&src_data),
330 src_format, &src_box, src_row_pitch, src_slice_pitch,
331 dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB, 0, 0, 0);
333 wined3d_texture_validate_location(dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB);
334 wined3d_texture_invalidate_location(dst_texture, 0, ~WINED3D_LOCATION_TEXTURE_RGB);
337 context_release(context);
339 return dst_texture;
342 void texture2d_read_from_framebuffer(struct wined3d_texture *texture, unsigned int sub_resource_idx,
343 struct wined3d_context *context, DWORD src_location, DWORD dst_location)
345 struct wined3d_resource *resource = &texture->resource;
346 struct wined3d_device *device = resource->device;
347 const struct wined3d_format_gl *format_gl;
348 struct wined3d_texture *restore_texture;
349 const struct wined3d_gl_info *gl_info;
350 struct wined3d_context_gl *context_gl;
351 unsigned int row_pitch, slice_pitch;
352 unsigned int width, height, level;
353 struct wined3d_bo_address data;
354 unsigned int restore_idx;
355 BYTE *row, *top, *bottom;
356 BOOL src_is_upside_down;
357 BYTE *mem = NULL;
358 uint8_t *offset;
359 unsigned int i;
361 /* dst_location was already prepared by the caller. */
362 wined3d_texture_get_bo_address(texture, sub_resource_idx, &data, dst_location);
363 offset = data.addr;
365 restore_texture = context->current_rt.texture;
366 restore_idx = context->current_rt.sub_resource_idx;
367 if (restore_texture != texture || restore_idx != sub_resource_idx)
368 context = context_acquire(device, texture, sub_resource_idx);
369 else
370 restore_texture = NULL;
371 context_gl = wined3d_context_gl(context);
372 gl_info = context_gl->gl_info;
374 if (src_location != resource->draw_binding)
376 wined3d_context_gl_apply_fbo_state_blit(context_gl, GL_READ_FRAMEBUFFER,
377 resource, sub_resource_idx, NULL, 0, src_location);
378 wined3d_context_gl_check_fbo_status(context_gl, GL_READ_FRAMEBUFFER);
379 context_invalidate_state(context, STATE_FRAMEBUFFER);
381 else
383 wined3d_context_gl_apply_blit_state(context_gl, device);
386 /* Select the correct read buffer, and give some debug output.
387 * There is no need to keep track of the current read buffer or reset it,
388 * every part of the code that reads sets the read buffer as desired.
390 if (src_location != WINED3D_LOCATION_DRAWABLE || wined3d_resource_is_offscreen(resource))
392 /* Mapping the primary render target which is not on a swapchain.
393 * Read from the back buffer. */
394 TRACE("Mapping offscreen render target.\n");
395 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_context_gl_get_offscreen_gl_buffer(context_gl));
396 src_is_upside_down = TRUE;
398 else
400 /* Onscreen surfaces are always part of a swapchain */
401 GLenum buffer = wined3d_texture_get_gl_buffer(texture);
402 TRACE("Mapping %#x buffer.\n", buffer);
403 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
404 src_is_upside_down = FALSE;
406 checkGLcall("glReadBuffer");
408 if (data.buffer_object)
410 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, wined3d_bo_gl(data.buffer_object)->id));
411 checkGLcall("glBindBuffer");
412 offset += data.buffer_object->buffer_offset;
414 else
416 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
417 checkGLcall("glBindBuffer");
420 level = sub_resource_idx % texture->level_count;
421 wined3d_texture_get_pitch(texture, level, &row_pitch, &slice_pitch);
422 format_gl = wined3d_format_gl(resource->format);
424 /* Setup pixel store pack state -- to glReadPixels into the correct place */
425 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / format_gl->f.byte_count);
426 checkGLcall("glPixelStorei");
428 width = wined3d_texture_get_level_width(texture, level);
429 height = wined3d_texture_get_level_height(texture, level);
430 gl_info->gl_ops.gl.p_glReadPixels(0, 0, width, height,
431 format_gl->format, format_gl->type, offset);
432 checkGLcall("glReadPixels");
434 /* Reset previous pixel store pack state */
435 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
436 checkGLcall("glPixelStorei");
438 if (!src_is_upside_down)
440 /* glReadPixels returns the image upside down, and there is no way to
441 * prevent this. Flip the lines in software. */
443 if (!(row = heap_alloc(row_pitch)))
444 goto error;
446 if (data.buffer_object)
448 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
449 checkGLcall("glMapBuffer");
451 mem += (uintptr_t)offset;
453 top = mem;
454 bottom = mem + row_pitch * (height - 1);
455 for (i = 0; i < height / 2; i++)
457 memcpy(row, top, row_pitch);
458 memcpy(top, bottom, row_pitch);
459 memcpy(bottom, row, row_pitch);
460 top += row_pitch;
461 bottom -= row_pitch;
463 heap_free(row);
465 if (data.buffer_object)
466 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
469 error:
470 if (data.buffer_object)
472 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
473 wined3d_context_gl_reference_bo(context_gl, wined3d_bo_gl(data.buffer_object));
474 checkGLcall("glBindBuffer");
477 if (restore_texture)
478 context_restore(context, restore_texture, restore_idx);
481 /* Read the framebuffer contents into a texture. Note that this function
482 * doesn't do any kind of flipping. Using this on an onscreen surface will
483 * result in a flipped D3D texture.
485 * Context activation is done by the caller. This function may temporarily
486 * switch to a different context and restore the original one before return. */
487 void texture2d_load_fb_texture(struct wined3d_texture_gl *texture_gl,
488 unsigned int sub_resource_idx, BOOL srgb, struct wined3d_context *context)
490 struct wined3d_texture *restore_texture;
491 const struct wined3d_gl_info *gl_info;
492 struct wined3d_context_gl *context_gl;
493 struct wined3d_resource *resource;
494 unsigned int restore_idx, level;
495 struct wined3d_device *device;
496 GLenum target;
498 resource = &texture_gl->t.resource;
499 device = resource->device;
500 restore_texture = context->current_rt.texture;
501 restore_idx = context->current_rt.sub_resource_idx;
502 if (restore_texture != &texture_gl->t || restore_idx != sub_resource_idx)
503 context = context_acquire(device, &texture_gl->t, sub_resource_idx);
504 else
505 restore_texture = NULL;
506 context_gl = wined3d_context_gl(context);
508 gl_info = context_gl->gl_info;
509 device_invalidate_state(device, STATE_FRAMEBUFFER);
511 wined3d_texture_gl_prepare_texture(texture_gl, context_gl, srgb);
512 wined3d_texture_gl_bind_and_dirtify(texture_gl, context_gl, srgb);
514 TRACE("Reading back offscreen render target %p, %u.\n", texture_gl, sub_resource_idx);
516 if (wined3d_resource_is_offscreen(resource))
517 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_context_gl_get_offscreen_gl_buffer(context_gl));
518 else
519 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(&texture_gl->t));
520 checkGLcall("glReadBuffer");
522 level = sub_resource_idx % texture_gl->t.level_count;
523 target = wined3d_texture_gl_get_sub_resource_target(texture_gl, sub_resource_idx);
524 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(target, level, 0, 0, 0, 0,
525 wined3d_texture_get_level_width(&texture_gl->t, level),
526 wined3d_texture_get_level_height(&texture_gl->t, level));
527 checkGLcall("glCopyTexSubImage2D");
529 if (restore_texture)
530 context_restore(context, restore_texture, restore_idx);
533 /* Context activation is done by the caller. */
534 static void cpu_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
536 struct wined3d_blitter *next;
538 if ((next = blitter->next))
539 next->ops->blitter_destroy(next, context);
541 heap_free(blitter);
544 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
545 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
546 const struct wined3d_format *format, uint32_t flags, const struct wined3d_blt_fx *fx)
548 UINT row_block_count;
549 const BYTE *src_row;
550 BYTE *dst_row;
551 UINT x, y;
553 src_row = src_data;
554 dst_row = dst_data;
556 row_block_count = (update_w + format->block_width - 1) / format->block_width;
558 if (!flags)
560 for (y = 0; y < update_h; y += format->block_height)
562 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
563 src_row += src_pitch;
564 dst_row += dst_pitch;
567 return WINED3D_OK;
570 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
572 src_row += (((update_h / format->block_height) - 1) * src_pitch);
574 switch (format->id)
576 case WINED3DFMT_DXT1:
577 for (y = 0; y < update_h; y += format->block_height)
579 struct block
581 WORD color[2];
582 BYTE control_row[4];
585 const struct block *s = (const struct block *)src_row;
586 struct block *d = (struct block *)dst_row;
588 for (x = 0; x < row_block_count; ++x)
590 d[x].color[0] = s[x].color[0];
591 d[x].color[1] = s[x].color[1];
592 d[x].control_row[0] = s[x].control_row[3];
593 d[x].control_row[1] = s[x].control_row[2];
594 d[x].control_row[2] = s[x].control_row[1];
595 d[x].control_row[3] = s[x].control_row[0];
597 src_row -= src_pitch;
598 dst_row += dst_pitch;
600 return WINED3D_OK;
602 case WINED3DFMT_DXT2:
603 case WINED3DFMT_DXT3:
604 for (y = 0; y < update_h; y += format->block_height)
606 struct block
608 WORD alpha_row[4];
609 WORD color[2];
610 BYTE control_row[4];
613 const struct block *s = (const struct block *)src_row;
614 struct block *d = (struct block *)dst_row;
616 for (x = 0; x < row_block_count; ++x)
618 d[x].alpha_row[0] = s[x].alpha_row[3];
619 d[x].alpha_row[1] = s[x].alpha_row[2];
620 d[x].alpha_row[2] = s[x].alpha_row[1];
621 d[x].alpha_row[3] = s[x].alpha_row[0];
622 d[x].color[0] = s[x].color[0];
623 d[x].color[1] = s[x].color[1];
624 d[x].control_row[0] = s[x].control_row[3];
625 d[x].control_row[1] = s[x].control_row[2];
626 d[x].control_row[2] = s[x].control_row[1];
627 d[x].control_row[3] = s[x].control_row[0];
629 src_row -= src_pitch;
630 dst_row += dst_pitch;
632 return WINED3D_OK;
634 default:
635 FIXME("Compressed flip not implemented for format %s.\n",
636 debug_d3dformat(format->id));
637 return E_NOTIMPL;
641 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
642 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
644 return E_NOTIMPL;
647 static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
648 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
649 const struct wined3d_box *src_box, uint32_t flags, const struct wined3d_blt_fx *fx,
650 enum wined3d_texture_filter_type filter)
652 unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count;
653 struct wined3d_device *device = dst_texture->resource.device;
654 const struct wined3d_format *src_format, *dst_format;
655 struct wined3d_texture *converted_texture = NULL;
656 struct wined3d_bo_address src_data, dst_data;
657 unsigned int src_fmt_attrs, dst_fmt_attrs;
658 struct wined3d_map_desc dst_map, src_map;
659 unsigned int x, sx, xinc, y, sy, yinc;
660 struct wined3d_context *context;
661 struct wined3d_range dst_range;
662 unsigned int texture_level;
663 HRESULT hr = WINED3D_OK;
664 BOOL same_sub_resource;
665 BOOL upload = FALSE;
666 DWORD map_binding;
667 const BYTE *sbase;
668 const BYTE *sbuf;
669 BYTE *dbuf;
671 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
672 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
673 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture,
674 src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
676 context = context_acquire(device, NULL, 0);
678 src_format = src_texture->resource.format;
679 dst_format = dst_texture->resource.format;
681 if (wined3d_format_is_typeless(src_format) && src_format->id == dst_format->typeless_id)
682 src_format = dst_format;
683 if (wined3d_format_is_typeless(dst_format) && dst_format->id == src_format->typeless_id)
684 dst_format = src_format;
686 src_height = src_box->bottom - src_box->top;
687 src_width = src_box->right - src_box->left;
688 dst_height = dst_box->bottom - dst_box->top;
689 dst_width = dst_box->right - dst_box->left;
691 dst_range.offset = 0;
692 dst_range.size = dst_texture->sub_resources[dst_sub_resource_idx].size;
693 if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx)
695 same_sub_resource = TRUE;
697 map_binding = dst_texture->resource.map_binding;
698 texture_level = dst_sub_resource_idx % dst_texture->level_count;
699 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
700 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
701 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
702 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
703 wined3d_texture_get_bo_address(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
704 dst_map.data = wined3d_context_map_bo_address(context, &dst_data,
705 dst_texture->sub_resources[dst_sub_resource_idx].size, WINED3D_MAP_READ | WINED3D_MAP_WRITE);
707 src_map = dst_map;
709 else
711 same_sub_resource = FALSE;
712 upload = dst_format->attrs & WINED3D_FORMAT_ATTR_BLOCKS
713 && (dst_width != src_width || dst_height != src_height);
715 if (upload)
717 dst_format = src_format->attrs & WINED3D_FORMAT_ATTR_BLOCKS
718 ? wined3d_get_format(device->adapter, WINED3DFMT_B8G8R8A8_UNORM, 0) : src_format;
721 if (!(flags & WINED3D_BLT_RAW) && dst_format->id != src_format->id)
723 if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format)))
725 FIXME("Cannot convert %s to %s.\n", debug_d3dformat(src_format->id),
726 debug_d3dformat(dst_format->id));
727 context_release(context);
728 return WINED3DERR_NOTAVAILABLE;
730 src_texture = converted_texture;
731 src_sub_resource_idx = 0;
732 src_format = src_texture->resource.format;
735 map_binding = src_texture->resource.map_binding;
736 texture_level = src_sub_resource_idx % src_texture->level_count;
737 if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, map_binding))
738 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
739 wined3d_texture_get_pitch(src_texture, texture_level, &src_map.row_pitch, &src_map.slice_pitch);
740 wined3d_texture_get_bo_address(src_texture, src_sub_resource_idx, &src_data, map_binding);
741 src_map.data = wined3d_context_map_bo_address(context, &src_data,
742 src_texture->sub_resources[src_sub_resource_idx].size, WINED3D_MAP_READ);
744 if (upload)
746 wined3d_format_calculate_pitch(dst_format, 1, dst_box->right, dst_box->bottom,
747 &dst_map.row_pitch, &dst_map.slice_pitch);
748 dst_map.data = heap_alloc(dst_map.slice_pitch);
750 else
752 map_binding = dst_texture->resource.map_binding;
753 texture_level = dst_sub_resource_idx % dst_texture->level_count;
754 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
755 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
757 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
758 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
759 wined3d_texture_get_bo_address(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
760 dst_map.data = wined3d_context_map_bo_address(context, &dst_data,
761 dst_texture->sub_resources[dst_sub_resource_idx].size, WINED3D_MAP_WRITE);
764 src_fmt_attrs = src_format->attrs;
765 dst_fmt_attrs = dst_format->attrs;
766 flags &= ~WINED3D_BLT_RAW;
768 bpp = dst_format->byte_count;
769 row_byte_count = dst_width * bpp;
771 sbase = (BYTE *)src_map.data
772 + ((src_box->top / src_format->block_height) * src_map.row_pitch)
773 + ((src_box->left / src_format->block_width) * src_format->block_byte_count);
774 dbuf = (BYTE *)dst_map.data
775 + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch)
776 + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count);
778 if (src_fmt_attrs & dst_fmt_attrs & WINED3D_FORMAT_ATTR_BLOCKS)
780 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
782 if (same_sub_resource)
784 FIXME("Only plain blits supported on compressed surfaces.\n");
785 hr = E_NOTIMPL;
786 goto release;
789 hr = surface_cpu_blt_compressed(sbase, dbuf,
790 src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height,
791 src_format, flags, fx);
792 goto release;
795 if ((src_fmt_attrs | dst_fmt_attrs) & WINED3D_FORMAT_ATTR_HEIGHT_SCALE)
797 FIXME("Unsupported blit between height-scaled formats (src %s, dst %s).\n",
798 debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
799 hr = E_NOTIMPL;
800 goto release;
803 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
804 && (src_width != dst_width || src_height != dst_height))
806 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
807 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
810 xinc = (src_width << 16) / dst_width;
811 yinc = (src_height << 16) / dst_height;
813 if (!flags)
815 /* No effects, we can cheat here. */
816 if (dst_width == src_width)
818 if (dst_height == src_height)
820 /* No stretching in either direction. This needs to be as fast
821 * as possible. */
822 sbuf = sbase;
824 /* Check for overlapping surfaces. */
825 if (!same_sub_resource || dst_box->top < src_box->top
826 || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
828 /* No overlap, or dst above src, so copy from top downwards. */
829 for (y = 0; y < dst_height; ++y)
831 memcpy(dbuf, sbuf, row_byte_count);
832 sbuf += src_map.row_pitch;
833 dbuf += dst_map.row_pitch;
836 else if (dst_box->top > src_box->top)
838 /* Copy from bottom upwards. */
839 sbuf += src_map.row_pitch * dst_height;
840 dbuf += dst_map.row_pitch * dst_height;
841 for (y = 0; y < dst_height; ++y)
843 sbuf -= src_map.row_pitch;
844 dbuf -= dst_map.row_pitch;
845 memcpy(dbuf, sbuf, row_byte_count);
848 else
850 /* Src and dst overlapping on the same line, use memmove. */
851 for (y = 0; y < dst_height; ++y)
853 memmove(dbuf, sbuf, row_byte_count);
854 sbuf += src_map.row_pitch;
855 dbuf += dst_map.row_pitch;
859 else
861 /* Stretching in y direction only. */
862 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
864 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
865 memcpy(dbuf, sbuf, row_byte_count);
866 dbuf += dst_map.row_pitch;
870 else
872 /* Stretching in X direction. */
873 unsigned int last_sy = ~0u;
874 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
876 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
878 if ((sy >> 16) == (last_sy >> 16))
880 /* This source row is the same as last source row -
881 * Copy the already stretched row. */
882 memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count);
884 else
886 #define STRETCH_ROW(type) \
887 do { \
888 const type *s = (const type *)sbuf; \
889 type *d = (type *)dbuf; \
890 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
891 d[x] = s[sx >> 16]; \
892 } while(0)
894 switch(bpp)
896 case 1:
897 STRETCH_ROW(BYTE);
898 break;
899 case 2:
900 STRETCH_ROW(WORD);
901 break;
902 case 4:
903 STRETCH_ROW(DWORD);
904 break;
905 case 3:
907 const BYTE *s;
908 BYTE *d = dbuf;
909 for (x = sx = 0; x < dst_width; x++, sx+= xinc)
911 DWORD pixel;
913 s = sbuf + 3 * (sx >> 16);
914 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
915 d[0] = (pixel ) & 0xff;
916 d[1] = (pixel >> 8) & 0xff;
917 d[2] = (pixel >> 16) & 0xff;
918 d += 3;
920 break;
922 default:
923 FIXME("Stretched blit not implemented for bpp %u.\n", bpp * 8);
924 hr = WINED3DERR_NOTAVAILABLE;
925 goto error;
927 #undef STRETCH_ROW
929 dbuf += dst_map.row_pitch;
930 last_sy = sy;
934 else
936 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
937 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
938 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
939 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
940 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
942 /* The color keying flags are checked for correctness in ddraw. */
943 if (flags & WINED3D_BLT_SRC_CKEY)
945 keylow = src_texture->async.src_blt_color_key.color_space_low_value;
946 keyhigh = src_texture->async.src_blt_color_key.color_space_high_value;
948 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
950 keylow = fx->src_color_key.color_space_low_value;
951 keyhigh = fx->src_color_key.color_space_high_value;
954 if (flags & WINED3D_BLT_DST_CKEY)
956 /* Destination color keys are taken from the source surface! */
957 destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value;
958 destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value;
960 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
962 destkeylow = fx->dst_color_key.color_space_low_value;
963 destkeyhigh = fx->dst_color_key.color_space_high_value;
966 if (bpp == 1)
968 keymask = 0xff;
970 else
972 uint32_t masks[3];
973 get_color_masks(src_format, masks);
974 keymask = masks[0] | masks[1] | masks[2];
976 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
977 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
980 if (flags & WINED3D_BLT_FX)
982 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
983 LONG tmpxy;
984 dTopLeft = dbuf;
985 dTopRight = dbuf + ((dst_width - 1) * bpp);
986 dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
987 dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
989 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
991 /* I don't think we need to do anything about this flag. */
992 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
994 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
996 tmp = dTopRight;
997 dTopRight = dTopLeft;
998 dTopLeft = tmp;
999 tmp = dBottomRight;
1000 dBottomRight = dBottomLeft;
1001 dBottomLeft = tmp;
1002 dstxinc = dstxinc * -1;
1004 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
1006 tmp = dTopLeft;
1007 dTopLeft = dBottomLeft;
1008 dBottomLeft = tmp;
1009 tmp = dTopRight;
1010 dTopRight = dBottomRight;
1011 dBottomRight = tmp;
1012 dstyinc = dstyinc * -1;
1014 if (fx->fx & WINEDDBLTFX_NOTEARING)
1016 /* I don't think we need to do anything about this flag. */
1017 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
1019 if (fx->fx & WINEDDBLTFX_ROTATE180)
1021 tmp = dBottomRight;
1022 dBottomRight = dTopLeft;
1023 dTopLeft = tmp;
1024 tmp = dBottomLeft;
1025 dBottomLeft = dTopRight;
1026 dTopRight = tmp;
1027 dstxinc = dstxinc * -1;
1028 dstyinc = dstyinc * -1;
1030 if (fx->fx & WINEDDBLTFX_ROTATE270)
1032 tmp = dTopLeft;
1033 dTopLeft = dBottomLeft;
1034 dBottomLeft = dBottomRight;
1035 dBottomRight = dTopRight;
1036 dTopRight = tmp;
1037 tmpxy = dstxinc;
1038 dstxinc = dstyinc;
1039 dstyinc = tmpxy;
1040 dstxinc = dstxinc * -1;
1042 if (fx->fx & WINEDDBLTFX_ROTATE90)
1044 tmp = dTopLeft;
1045 dTopLeft = dTopRight;
1046 dTopRight = dBottomRight;
1047 dBottomRight = dBottomLeft;
1048 dBottomLeft = tmp;
1049 tmpxy = dstxinc;
1050 dstxinc = dstyinc;
1051 dstyinc = tmpxy;
1052 dstyinc = dstyinc * -1;
1054 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
1056 /* I don't think we need to do anything about this flag. */
1057 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
1059 dbuf = dTopLeft;
1060 flags &= ~(WINED3D_BLT_FX);
1063 #define COPY_COLORKEY_FX(type) \
1064 do { \
1065 const type *s; \
1066 type *d = (type *)dbuf, *dx, tmp; \
1067 for (y = sy = 0; y < dst_height; ++y, sy += yinc) \
1069 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
1070 dx = d; \
1071 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
1073 tmp = s[sx >> 16]; \
1074 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
1075 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
1077 dx[0] = tmp; \
1079 dx = (type *)(((BYTE *)dx) + dstxinc); \
1081 d = (type *)(((BYTE *)d) + dstyinc); \
1083 } while(0)
1085 switch (bpp)
1087 case 1:
1088 COPY_COLORKEY_FX(BYTE);
1089 break;
1090 case 2:
1091 COPY_COLORKEY_FX(WORD);
1092 break;
1093 case 4:
1094 COPY_COLORKEY_FX(DWORD);
1095 break;
1096 case 3:
1098 const BYTE *s;
1099 BYTE *d = dbuf, *dx;
1100 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
1102 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
1103 dx = d;
1104 for (x = sx = 0; x < dst_width; ++x, sx+= xinc)
1106 DWORD pixel, dpixel = 0;
1107 s = sbuf + 3 * (sx>>16);
1108 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
1109 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
1110 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
1111 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
1113 dx[0] = (pixel ) & 0xff;
1114 dx[1] = (pixel >> 8) & 0xff;
1115 dx[2] = (pixel >> 16) & 0xff;
1117 dx += dstxinc;
1119 d += dstyinc;
1121 break;
1123 default:
1124 FIXME("%s color-keyed blit not implemented for bpp %u.\n",
1125 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
1126 hr = WINED3DERR_NOTAVAILABLE;
1127 goto error;
1128 #undef COPY_COLORKEY_FX
1132 error:
1133 if (flags)
1134 FIXME(" Unsupported flags %#x.\n", flags);
1136 release:
1137 if (upload && hr == WINED3D_OK)
1139 struct wined3d_bo_address data;
1141 data.buffer_object = 0;
1142 data.addr = dst_map.data;
1144 texture_level = dst_sub_resource_idx % dst_texture->level_count;
1146 wined3d_texture_prepare_location(dst_texture, texture_level, context, WINED3D_LOCATION_TEXTURE_RGB);
1147 dst_texture->texture_ops->texture_upload_data(context, wined3d_const_bo_address(&data), dst_format,
1148 dst_box, dst_map.row_pitch, dst_map.slice_pitch, dst_texture, texture_level,
1149 WINED3D_LOCATION_TEXTURE_RGB, dst_box->left, dst_box->top, 0);
1151 wined3d_texture_validate_location(dst_texture, texture_level, WINED3D_LOCATION_TEXTURE_RGB);
1152 wined3d_texture_invalidate_location(dst_texture, texture_level, ~WINED3D_LOCATION_TEXTURE_RGB);
1155 if (upload)
1157 heap_free(dst_map.data);
1159 else
1161 wined3d_context_unmap_bo_address(context, &dst_data, 1, &dst_range);
1164 if (!same_sub_resource)
1165 wined3d_context_unmap_bo_address(context, &src_data, 0, NULL);
1166 if (SUCCEEDED(hr) && dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture)
1168 SetRect(&dst_texture->swapchain->front_buffer_update,
1169 dst_box->left, dst_box->top, dst_box->right, dst_box->bottom);
1170 dst_texture->swapchain->swapchain_ops->swapchain_frontbuffer_updated(dst_texture->swapchain);
1172 if (converted_texture)
1173 wined3d_texture_decref(converted_texture);
1174 context_release(context);
1176 return hr;
1179 static void surface_cpu_blt_colour_fill(struct wined3d_rendertarget_view *view,
1180 const struct wined3d_box *box, const struct wined3d_color *colour)
1182 struct wined3d_device *device = view->resource->device;
1183 struct wined3d_context *context;
1184 struct wined3d_texture *texture;
1185 struct wined3d_bo_address data;
1186 struct wined3d_box level_box;
1187 struct wined3d_map_desc map;
1188 struct wined3d_range range;
1189 bool full_subresource;
1190 unsigned int level;
1191 DWORD map_binding;
1193 TRACE("view %p, box %s, colour %s.\n", view, debug_box(box), debug_color(colour));
1195 if (view->format_attrs & WINED3D_FORMAT_ATTR_BLOCKS)
1197 FIXME("Not implemented for format %s.\n", debug_d3dformat(view->format->id));
1198 return;
1201 if (view->format->id != view->resource->format->id)
1202 FIXME("View format %s doesn't match resource format %s.\n",
1203 debug_d3dformat(view->format->id), debug_d3dformat(view->resource->format->id));
1205 if (view->resource->type == WINED3D_RTYPE_BUFFER)
1207 FIXME("Not implemented for buffers.\n");
1208 return;
1211 context = context_acquire(device, NULL, 0);
1213 texture = texture_from_resource(view->resource);
1214 level = view->sub_resource_idx % texture->level_count;
1215 wined3d_texture_get_level_box(texture_from_resource(view->resource), level, &level_box);
1216 full_subresource = !memcmp(box, &level_box, sizeof(*box));
1218 map_binding = texture->resource.map_binding;
1219 if (!wined3d_texture_load_location(texture, view->sub_resource_idx, context, map_binding))
1220 ERR("Failed to load the sub-resource into %s.\n", wined3d_debug_location(map_binding));
1221 wined3d_texture_invalidate_location(texture, view->sub_resource_idx, ~map_binding);
1222 wined3d_texture_get_pitch(texture, level, &map.row_pitch, &map.slice_pitch);
1223 wined3d_texture_get_bo_address(texture, view->sub_resource_idx, &data, map_binding);
1224 map.data = wined3d_context_map_bo_address(context, &data,
1225 texture->sub_resources[view->sub_resource_idx].size, WINED3D_MAP_WRITE);
1226 range.offset = 0;
1227 range.size = texture->sub_resources[view->sub_resource_idx].size;
1229 wined3d_resource_memory_colour_fill(view->resource, &map, colour, box, full_subresource);
1231 wined3d_context_unmap_bo_address(context, &data, 1, &range);
1232 context_release(context);
1235 static bool wined3d_box_intersect(struct wined3d_box *ret, const struct wined3d_box *b1,
1236 const struct wined3d_box *b2)
1238 wined3d_box_set(ret, max(b1->left, b2->left), max(b1->top, b2->top),
1239 min(b1->right, b2->right), min(b1->bottom, b2->bottom),
1240 max(b1->front, b2->front), min(b1->back, b2->back));
1241 return ret->right > ret->left && ret->bottom > ret->top && ret->back > ret->front;
1244 static void cpu_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
1245 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
1246 const RECT *draw_rect, uint32_t flags, const struct wined3d_color *colour, float depth, unsigned int stencil)
1248 struct wined3d_color c = {depth, 0.0f, 0.0f, 0.0f};
1249 struct wined3d_box box, box_clip, box_view;
1250 struct wined3d_rendertarget_view *view;
1251 unsigned int i, j;
1253 if (!rect_count)
1255 rect_count = 1;
1256 clear_rects = draw_rect;
1259 for (i = 0; i < rect_count; ++i)
1261 box.left = max(clear_rects[i].left, draw_rect->left);
1262 box.top = max(clear_rects[i].top, draw_rect->top);
1263 box.right = min(clear_rects[i].right, draw_rect->right);
1264 box.bottom = min(clear_rects[i].bottom, draw_rect->bottom);
1265 box.front = 0;
1266 box.back = ~0u;
1268 if (box.left >= box.right || box.top >= box.bottom)
1269 continue;
1271 if (flags & WINED3DCLEAR_TARGET)
1273 for (j = 0; j < rt_count; ++j)
1275 if ((view = fb->render_targets[j]))
1277 wined3d_rendertarget_view_get_box(view, &box_view);
1278 if (wined3d_box_intersect(&box_clip, &box_view, &box))
1279 surface_cpu_blt_colour_fill(view, &box_clip, colour);
1284 if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil))
1286 if ((view->format->depth_size && !(flags & WINED3DCLEAR_ZBUFFER))
1287 || (view->format->stencil_size && !(flags & WINED3DCLEAR_STENCIL)))
1288 FIXME("Clearing %#x on %s.\n", flags, debug_d3dformat(view->format->id));
1290 wined3d_rendertarget_view_get_box(view, &box_view);
1291 if (wined3d_box_intersect(&box_clip, &box_view, &box))
1292 surface_cpu_blt_colour_fill(view, &box_clip, &c);
1297 static DWORD cpu_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
1298 struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1299 DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture,
1300 unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect,
1301 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter,
1302 const struct wined3d_format *resolve_format)
1304 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
1305 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
1306 struct wined3d_blt_fx fx;
1307 DWORD flags = 0;
1309 memset(&fx, 0, sizeof(fx));
1310 switch (op)
1312 case WINED3D_BLIT_OP_COLOR_BLIT:
1313 case WINED3D_BLIT_OP_DEPTH_BLIT:
1314 break;
1315 case WINED3D_BLIT_OP_RAW_BLIT:
1316 flags |= WINED3D_BLT_RAW;
1317 break;
1318 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
1319 flags |= WINED3D_BLT_ALPHA_TEST;
1320 break;
1321 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
1322 flags |= WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_FX;
1323 fx.src_color_key = *color_key;
1324 break;
1325 default:
1326 FIXME("Unhandled op %#x.\n", op);
1327 break;
1330 if (FAILED(surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
1331 src_texture, src_sub_resource_idx, &src_box, flags, &fx, filter)))
1332 ERR("Failed to blit.\n");
1333 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
1335 return dst_location | (dst_texture->sub_resources[dst_sub_resource_idx].locations
1336 & dst_texture->resource.map_binding);
1339 static const struct wined3d_blitter_ops cpu_blitter_ops =
1341 cpu_blitter_destroy,
1342 cpu_blitter_clear,
1343 cpu_blitter_blit,
1346 struct wined3d_blitter *wined3d_cpu_blitter_create(void)
1348 struct wined3d_blitter *blitter;
1350 if (!(blitter = heap_alloc(sizeof(*blitter))))
1351 return NULL;
1353 TRACE("Created blitter %p.\n", blitter);
1355 blitter->ops = &cpu_blitter_ops;
1356 blitter->next = NULL;
1358 return blitter;
1361 static bool wined3d_is_colour_blit(enum wined3d_blit_op blit_op)
1363 switch (blit_op)
1365 case WINED3D_BLIT_OP_COLOR_BLIT:
1366 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
1367 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
1368 return true;
1370 default:
1371 return false;
1375 static bool sub_resource_is_on_cpu(const struct wined3d_texture *texture, unsigned int sub_resource_idx)
1377 DWORD locations = texture->sub_resources[sub_resource_idx].locations;
1379 if (locations & (WINED3D_LOCATION_BUFFER | WINED3D_LOCATION_SYSMEM))
1380 return true;
1382 if (!(texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU) && (locations & WINED3D_LOCATION_CLEARED))
1383 return true;
1385 return false;
1388 HRESULT texture2d_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
1389 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1390 const struct wined3d_box *src_box, uint32_t flags, const struct wined3d_blt_fx *fx,
1391 enum wined3d_texture_filter_type filter)
1393 struct wined3d_texture_sub_resource *src_sub_resource, *dst_sub_resource;
1394 struct wined3d_device *device = dst_texture->resource.device;
1395 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1396 BOOL scale, convert, resolve, resolve_typeless = FALSE;
1397 const struct wined3d_format *resolve_format = NULL;
1398 const struct wined3d_color_key *colour_key = NULL;
1399 DWORD src_location, dst_location, valid_locations;
1400 struct wined3d_context *context;
1401 enum wined3d_blit_op blit_op;
1402 RECT src_rect, dst_rect;
1403 bool src_ds, dst_ds;
1405 static const DWORD simple_blit = WINED3D_BLT_SRC_CKEY
1406 | WINED3D_BLT_SRC_CKEY_OVERRIDE
1407 | WINED3D_BLT_ALPHA_TEST
1408 | WINED3D_BLT_RAW;
1410 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
1411 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
1412 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture, src_sub_resource_idx,
1413 debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
1414 TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage));
1416 if (fx)
1418 TRACE("fx %#x.\n", fx->fx);
1419 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
1420 fx->dst_color_key.color_space_low_value,
1421 fx->dst_color_key.color_space_high_value);
1422 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
1423 fx->src_color_key.color_space_low_value,
1424 fx->src_color_key.color_space_high_value);
1425 TRACE("resolve_format_id %s.\n", debug_d3dformat(fx->resolve_format_id));
1427 if (fx->resolve_format_id != WINED3DFMT_UNKNOWN)
1428 resolve_format = wined3d_get_format(device->adapter, fx->resolve_format_id, 0);
1431 dst_sub_resource = &dst_texture->sub_resources[dst_sub_resource_idx];
1432 src_sub_resource = &src_texture->sub_resources[src_sub_resource_idx];
1434 if (src_sub_resource->locations & WINED3D_LOCATION_DISCARDED)
1436 WARN("Source sub-resource is discarded, nothing to do.\n");
1437 return WINED3D_OK;
1440 SetRect(&src_rect, src_box->left, src_box->top, src_box->right, src_box->bottom);
1441 SetRect(&dst_rect, dst_box->left, dst_box->top, dst_box->right, dst_box->bottom);
1443 if (!fx || !(fx->fx))
1444 flags &= ~WINED3D_BLT_FX;
1446 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
1447 if (flags & WINED3D_BLT_DO_NOT_WAIT)
1449 static unsigned int once;
1451 if (!once++)
1452 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
1455 flags &= ~(WINED3D_BLT_SYNCHRONOUS | WINED3D_BLT_DO_NOT_WAIT | WINED3D_BLT_WAIT);
1457 if (flags & ~simple_blit)
1459 WARN_(d3d_perf)("Using CPU fallback for complex blit (%#x).\n", flags);
1460 goto cpu;
1463 src_swapchain = src_texture->swapchain;
1464 dst_swapchain = dst_texture->swapchain;
1466 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain
1467 && (wined3d_settings.offscreen_rendering_mode != ORM_FBO
1468 || src_texture == src_swapchain->front_buffer))
1470 /* TODO: We could support cross-swapchain blits by first downloading
1471 * the source to a texture. */
1472 FIXME("Cross-swapchain blit not supported.\n");
1473 return WINED3DERR_INVALIDCALL;
1476 scale = src_box->right - src_box->left != dst_box->right - dst_box->left
1477 || src_box->bottom - src_box->top != dst_box->bottom - dst_box->top;
1478 convert = src_texture->resource.format->id != dst_texture->resource.format->id;
1479 resolve = src_texture->resource.multisample_type != dst_texture->resource.multisample_type;
1480 if (resolve)
1482 resolve_typeless = (wined3d_format_is_typeless(src_texture->resource.format)
1483 || wined3d_format_is_typeless(dst_texture->resource.format))
1484 && (src_texture->resource.format->typeless_id == dst_texture->resource.format->typeless_id);
1485 if (resolve_typeless && !resolve_format)
1486 WARN("Resolve format for typeless resolve not specified.\n");
1489 dst_ds = dst_texture->resource.format->depth_size || dst_texture->resource.format->stencil_size;
1490 src_ds = src_texture->resource.format->depth_size || src_texture->resource.format->stencil_size;
1492 if (src_ds || dst_ds)
1494 TRACE("Depth/stencil blit.\n");
1496 if (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)
1497 dst_location = dst_texture->resource.draw_binding;
1498 else
1499 dst_location = dst_texture->resource.map_binding;
1501 if ((flags & WINED3D_BLT_RAW) || (!scale && !convert && !resolve))
1502 blit_op = WINED3D_BLIT_OP_RAW_BLIT;
1503 else
1504 blit_op = WINED3D_BLIT_OP_DEPTH_BLIT;
1506 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
1507 valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context,
1508 src_texture, src_sub_resource_idx, src_texture->resource.draw_binding, &src_rect,
1509 dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, NULL, filter, resolve_format);
1510 context_release(context);
1512 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
1513 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
1515 return WINED3D_OK;
1518 TRACE("Colour blit.\n");
1520 /* In principle this would apply to depth blits as well, but we don't
1521 * implement those in the CPU blitter at the moment. */
1522 if ((dst_sub_resource->locations & dst_texture->resource.map_binding)
1523 && (src_sub_resource->locations & src_texture->resource.map_binding))
1525 if (scale)
1526 TRACE("Not doing sysmem blit because of scaling.\n");
1527 else if (convert)
1528 TRACE("Not doing sysmem blit because of format conversion.\n");
1529 else
1530 goto cpu;
1533 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
1534 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
1536 colour_key = &fx->src_color_key;
1537 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
1539 else if (flags & WINED3D_BLT_SRC_CKEY)
1541 colour_key = &src_texture->async.src_blt_color_key;
1542 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
1544 else if (flags & WINED3D_BLT_ALPHA_TEST)
1546 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
1548 else if (sub_resource_is_on_cpu(src_texture, src_sub_resource_idx)
1549 && !sub_resource_is_on_cpu(dst_texture, dst_sub_resource_idx)
1550 && (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1552 /* Upload */
1553 if (scale)
1554 TRACE("Not doing upload because of scaling.\n");
1555 else if (convert)
1556 TRACE("Not doing upload because of format conversion.\n");
1557 else if (dst_texture->resource.format->conv_byte_count)
1558 TRACE("Not doing upload because the destination format needs conversion.\n");
1559 else
1561 wined3d_texture_upload_from_texture(dst_texture, dst_sub_resource_idx, dst_box->left,
1562 dst_box->top, dst_box->front, src_texture, src_sub_resource_idx, src_box);
1563 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
1565 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
1566 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx,
1567 context, dst_texture->resource.draw_binding);
1568 context_release(context);
1570 return WINED3D_OK;
1573 else if (!sub_resource_is_on_cpu(src_texture, src_sub_resource_idx)
1574 && (dst_sub_resource->locations & dst_texture->resource.map_binding)
1575 && !(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1577 /* Download */
1578 if (scale)
1579 TRACE("Not doing download because of scaling.\n");
1580 else if (convert)
1581 TRACE("Not doing download because of format conversion.\n");
1582 else if (src_texture->resource.format->conv_byte_count)
1583 TRACE("Not doing download because the source format needs conversion.\n");
1584 else if (!(src_texture->flags & WINED3D_TEXTURE_DOWNLOADABLE))
1585 TRACE("Not doing download because texture is not downloadable.\n");
1586 else if (!wined3d_texture_is_full_rect(src_texture, src_sub_resource_idx % src_texture->level_count, &src_rect))
1587 TRACE("Not doing download because of partial download (src).\n");
1588 else if (!wined3d_texture_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, &dst_rect))
1589 TRACE("Not doing download because of partial download (dst).\n");
1590 else
1592 wined3d_texture_download_from_texture(dst_texture, dst_sub_resource_idx, src_texture,
1593 src_sub_resource_idx);
1594 return WINED3D_OK;
1597 else if (dst_swapchain && dst_swapchain->back_buffers
1598 && dst_texture == dst_swapchain->front_buffer
1599 && src_texture == dst_swapchain->back_buffers[0])
1601 /* Use present for back -> front blits. The idea behind this is that
1602 * present is potentially faster than a blit, in particular when FBO
1603 * blits aren't available. Some ddraw applications like Half-Life and
1604 * Prince of Persia 3D use Blt() from the backbuffer to the
1605 * frontbuffer instead of doing a Flip(). D3d8 and d3d9 applications
1606 * can't blit directly to the frontbuffer. */
1607 enum wined3d_swap_effect swap_effect = dst_swapchain->state.desc.swap_effect;
1609 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
1611 /* Set the swap effect to COPY, we don't want the backbuffer to become
1612 * undefined. */
1613 dst_swapchain->state.desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
1614 wined3d_swapchain_present(dst_swapchain, NULL, NULL,
1615 dst_swapchain->win_handle, dst_swapchain->swap_interval, 0);
1616 dst_swapchain->state.desc.swap_effect = swap_effect;
1618 return WINED3D_OK;
1621 if ((flags & WINED3D_BLT_RAW) || (blit_op == WINED3D_BLIT_OP_COLOR_BLIT && !scale && !convert && !resolve))
1622 blit_op = WINED3D_BLIT_OP_RAW_BLIT;
1624 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
1626 if (src_texture->resource.multisample_type != WINED3D_MULTISAMPLE_NONE && !resolve_typeless
1627 && ((scale && !context->d3d_info->scaled_resolve)
1628 || convert || !wined3d_is_colour_blit(blit_op)))
1629 src_location = WINED3D_LOCATION_RB_RESOLVED;
1630 else
1631 src_location = src_texture->resource.draw_binding;
1633 if (!(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1634 dst_location = dst_texture->resource.map_binding;
1635 else if (dst_texture->resource.multisample_type != WINED3D_MULTISAMPLE_NONE
1636 && (scale || convert || !wined3d_is_colour_blit(blit_op)))
1637 dst_location = WINED3D_LOCATION_RB_RESOLVED;
1638 else
1639 dst_location = dst_texture->resource.draw_binding;
1641 valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context,
1642 src_texture, src_sub_resource_idx, src_location, &src_rect,
1643 dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, colour_key, filter, resolve_format);
1645 context_release(context);
1647 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
1648 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
1650 return WINED3D_OK;
1652 cpu:
1653 return surface_cpu_blt(dst_texture, dst_sub_resource_idx, dst_box,
1654 src_texture, src_sub_resource_idx, src_box, flags, fx, filter);