win32u: Move NtUserDrawCaptionTemp implementation from user32.
[wine.git] / dlls / wined3d / surface.c
blob7d62af5b31f42c7133dc681055ff64956e97f7be
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 DWORD pitch_in, DWORD 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 DWORD pitch_in, DWORD 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 DWORD pitch_in, DWORD 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 DWORD pitch_in, DWORD 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 DWORD pitch_in, DWORD 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, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
217 static const struct d3dfmt_converter_desc converters[] =
219 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
220 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
221 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
222 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
223 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
224 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
227 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
228 enum wined3d_format_id to)
230 unsigned int i;
232 for (i = 0; i < ARRAY_SIZE(converters); ++i)
234 if (converters[i].from == from && converters[i].to == to)
235 return &converters[i];
238 return NULL;
241 static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture,
242 unsigned int sub_resource_idx, const struct wined3d_format *dst_format)
244 unsigned int texture_level = sub_resource_idx % src_texture->level_count;
245 const struct wined3d_format *src_format = src_texture->resource.format;
246 struct wined3d_device *device = src_texture->resource.device;
247 const struct d3dfmt_converter_desc *conv = NULL;
248 unsigned int src_row_pitch, src_slice_pitch;
249 struct wined3d_texture *dst_texture;
250 struct wined3d_bo_address src_data;
251 struct wined3d_resource_desc desc;
252 struct wined3d_context *context;
253 DWORD map_binding;
255 if (!(conv = find_converter(src_format->id, dst_format->id)) && ((device->wined3d->flags & WINED3D_NO3D)
256 || !is_identity_fixup(src_format->color_fixup) || src_format->conv_byte_count
257 || !is_identity_fixup(dst_format->color_fixup) || dst_format->conv_byte_count
258 || ((src_format->attrs & WINED3D_FORMAT_ATTR_COMPRESSED)
259 && !src_format->decompress)))
261 FIXME("Cannot find a conversion function from format %s to %s.\n",
262 debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
263 return NULL;
266 /* FIXME: Multisampled conversion? */
267 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
268 desc.format = dst_format->id;
269 desc.multisample_type = WINED3D_MULTISAMPLE_NONE;
270 desc.multisample_quality = 0;
271 desc.usage = WINED3DUSAGE_SCRATCH | WINED3DUSAGE_PRIVATE;
272 desc.bind_flags = 0;
273 desc.access = WINED3D_RESOURCE_ACCESS_CPU | WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W;
274 desc.width = wined3d_texture_get_level_width(src_texture, texture_level);
275 desc.height = wined3d_texture_get_level_height(src_texture, texture_level);
276 desc.depth = 1;
277 desc.size = 0;
278 if (FAILED(wined3d_texture_create(device, &desc, 1, 1, WINED3D_TEXTURE_CREATE_DISCARD,
279 NULL, NULL, &wined3d_null_parent_ops, &dst_texture)))
281 ERR("Failed to create a destination texture for conversion.\n");
282 return NULL;
285 context = context_acquire(device, NULL, 0);
287 map_binding = src_texture->resource.map_binding;
288 if (!wined3d_texture_load_location(src_texture, sub_resource_idx, context, map_binding))
289 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
290 wined3d_texture_get_pitch(src_texture, texture_level, &src_row_pitch, &src_slice_pitch);
291 wined3d_texture_get_bo_address(src_texture, sub_resource_idx, &src_data, map_binding);
293 if (conv)
295 unsigned int dst_row_pitch, dst_slice_pitch;
296 struct wined3d_bo_address dst_data;
297 struct wined3d_range range;
298 const BYTE *src;
299 BYTE *dst;
301 map_binding = dst_texture->resource.map_binding;
302 if (!wined3d_texture_load_location(dst_texture, 0, context, map_binding))
303 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
304 wined3d_texture_get_pitch(dst_texture, 0, &dst_row_pitch, &dst_slice_pitch);
305 wined3d_texture_get_bo_address(dst_texture, 0, &dst_data, map_binding);
307 src = wined3d_context_map_bo_address(context, &src_data,
308 src_texture->sub_resources[sub_resource_idx].size, WINED3D_MAP_READ);
309 dst = wined3d_context_map_bo_address(context, &dst_data,
310 dst_texture->sub_resources[0].size, WINED3D_MAP_WRITE);
312 conv->convert(src, dst, src_row_pitch, dst_row_pitch, desc.width, desc.height);
314 range.offset = 0;
315 range.size = dst_texture->sub_resources[0].size;
316 wined3d_texture_invalidate_location(dst_texture, 0, ~map_binding);
317 wined3d_context_unmap_bo_address(context, &dst_data, 1, &range);
318 wined3d_context_unmap_bo_address(context, &src_data, 0, NULL);
320 else
322 struct wined3d_box src_box = {0, 0, desc.width, desc.height, 0, 1};
324 TRACE("Using upload conversion.\n");
326 wined3d_texture_prepare_location(dst_texture, 0, context, WINED3D_LOCATION_TEXTURE_RGB);
327 dst_texture->texture_ops->texture_upload_data(context, wined3d_const_bo_address(&src_data),
328 src_format, &src_box, src_row_pitch, src_slice_pitch,
329 dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB, 0, 0, 0);
331 wined3d_texture_validate_location(dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB);
332 wined3d_texture_invalidate_location(dst_texture, 0, ~WINED3D_LOCATION_TEXTURE_RGB);
335 context_release(context);
337 return dst_texture;
340 void texture2d_read_from_framebuffer(struct wined3d_texture *texture, unsigned int sub_resource_idx,
341 struct wined3d_context *context, DWORD src_location, DWORD dst_location)
343 struct wined3d_resource *resource = &texture->resource;
344 struct wined3d_device *device = resource->device;
345 const struct wined3d_format_gl *format_gl;
346 struct wined3d_texture *restore_texture;
347 const struct wined3d_gl_info *gl_info;
348 struct wined3d_context_gl *context_gl;
349 unsigned int row_pitch, slice_pitch;
350 unsigned int width, height, level;
351 struct wined3d_bo_address data;
352 unsigned int restore_idx;
353 BYTE *row, *top, *bottom;
354 BOOL src_is_upside_down;
355 BYTE *mem = NULL;
356 uint8_t *offset;
357 unsigned int i;
359 /* dst_location was already prepared by the caller. */
360 wined3d_texture_get_bo_address(texture, sub_resource_idx, &data, dst_location);
361 offset = data.addr;
363 restore_texture = context->current_rt.texture;
364 restore_idx = context->current_rt.sub_resource_idx;
365 if (restore_texture != texture || restore_idx != sub_resource_idx)
366 context = context_acquire(device, texture, sub_resource_idx);
367 else
368 restore_texture = NULL;
369 context_gl = wined3d_context_gl(context);
370 gl_info = context_gl->gl_info;
372 if (src_location != resource->draw_binding)
374 wined3d_context_gl_apply_fbo_state_blit(context_gl, GL_READ_FRAMEBUFFER,
375 resource, sub_resource_idx, NULL, 0, src_location);
376 wined3d_context_gl_check_fbo_status(context_gl, GL_READ_FRAMEBUFFER);
377 context_invalidate_state(context, STATE_FRAMEBUFFER);
379 else
381 wined3d_context_gl_apply_blit_state(context_gl, device);
384 /* Select the correct read buffer, and give some debug output.
385 * There is no need to keep track of the current read buffer or reset it,
386 * every part of the code that reads sets the read buffer as desired.
388 if (src_location != WINED3D_LOCATION_DRAWABLE || wined3d_resource_is_offscreen(resource))
390 /* Mapping the primary render target which is not on a swapchain.
391 * Read from the back buffer. */
392 TRACE("Mapping offscreen render target.\n");
393 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_context_gl_get_offscreen_gl_buffer(context_gl));
394 src_is_upside_down = TRUE;
396 else
398 /* Onscreen surfaces are always part of a swapchain */
399 GLenum buffer = wined3d_texture_get_gl_buffer(texture);
400 TRACE("Mapping %#x buffer.\n", buffer);
401 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
402 src_is_upside_down = FALSE;
404 checkGLcall("glReadBuffer");
406 if (data.buffer_object)
408 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, wined3d_bo_gl(data.buffer_object)->id));
409 checkGLcall("glBindBuffer");
410 offset += data.buffer_object->buffer_offset;
412 else
414 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
415 checkGLcall("glBindBuffer");
418 level = sub_resource_idx % texture->level_count;
419 wined3d_texture_get_pitch(texture, level, &row_pitch, &slice_pitch);
420 format_gl = wined3d_format_gl(resource->format);
422 /* Setup pixel store pack state -- to glReadPixels into the correct place */
423 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / format_gl->f.byte_count);
424 checkGLcall("glPixelStorei");
426 width = wined3d_texture_get_level_width(texture, level);
427 height = wined3d_texture_get_level_height(texture, level);
428 gl_info->gl_ops.gl.p_glReadPixels(0, 0, width, height,
429 format_gl->format, format_gl->type, offset);
430 checkGLcall("glReadPixels");
432 /* Reset previous pixel store pack state */
433 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
434 checkGLcall("glPixelStorei");
436 if (!src_is_upside_down)
438 /* glReadPixels returns the image upside down, and there is no way to
439 * prevent this. Flip the lines in software. */
441 if (!(row = heap_alloc(row_pitch)))
442 goto error;
444 if (data.buffer_object)
446 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
447 checkGLcall("glMapBuffer");
449 mem += (uintptr_t)offset;
451 top = mem;
452 bottom = mem + row_pitch * (height - 1);
453 for (i = 0; i < height / 2; i++)
455 memcpy(row, top, row_pitch);
456 memcpy(top, bottom, row_pitch);
457 memcpy(bottom, row, row_pitch);
458 top += row_pitch;
459 bottom -= row_pitch;
461 heap_free(row);
463 if (data.buffer_object)
464 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
467 error:
468 if (data.buffer_object)
470 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
471 wined3d_context_gl_reference_bo(context_gl, wined3d_bo_gl(data.buffer_object));
472 checkGLcall("glBindBuffer");
475 if (restore_texture)
476 context_restore(context, restore_texture, restore_idx);
479 /* Read the framebuffer contents into a texture. Note that this function
480 * doesn't do any kind of flipping. Using this on an onscreen surface will
481 * result in a flipped D3D texture.
483 * Context activation is done by the caller. This function may temporarily
484 * switch to a different context and restore the original one before return. */
485 void texture2d_load_fb_texture(struct wined3d_texture_gl *texture_gl,
486 unsigned int sub_resource_idx, BOOL srgb, struct wined3d_context *context)
488 struct wined3d_texture *restore_texture;
489 const struct wined3d_gl_info *gl_info;
490 struct wined3d_context_gl *context_gl;
491 struct wined3d_resource *resource;
492 unsigned int restore_idx, level;
493 struct wined3d_device *device;
494 GLenum target;
496 resource = &texture_gl->t.resource;
497 device = resource->device;
498 restore_texture = context->current_rt.texture;
499 restore_idx = context->current_rt.sub_resource_idx;
500 if (restore_texture != &texture_gl->t || restore_idx != sub_resource_idx)
501 context = context_acquire(device, &texture_gl->t, sub_resource_idx);
502 else
503 restore_texture = NULL;
504 context_gl = wined3d_context_gl(context);
506 gl_info = context_gl->gl_info;
507 device_invalidate_state(device, STATE_FRAMEBUFFER);
509 wined3d_texture_gl_prepare_texture(texture_gl, context_gl, srgb);
510 wined3d_texture_gl_bind_and_dirtify(texture_gl, context_gl, srgb);
512 TRACE("Reading back offscreen render target %p, %u.\n", texture_gl, sub_resource_idx);
514 if (wined3d_resource_is_offscreen(resource))
515 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_context_gl_get_offscreen_gl_buffer(context_gl));
516 else
517 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(&texture_gl->t));
518 checkGLcall("glReadBuffer");
520 level = sub_resource_idx % texture_gl->t.level_count;
521 target = wined3d_texture_gl_get_sub_resource_target(texture_gl, sub_resource_idx);
522 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(target, level, 0, 0, 0, 0,
523 wined3d_texture_get_level_width(&texture_gl->t, level),
524 wined3d_texture_get_level_height(&texture_gl->t, level));
525 checkGLcall("glCopyTexSubImage2D");
527 if (restore_texture)
528 context_restore(context, restore_texture, restore_idx);
531 /* Context activation is done by the caller. */
532 static void cpu_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
534 struct wined3d_blitter *next;
536 if ((next = blitter->next))
537 next->ops->blitter_destroy(next, context);
539 heap_free(blitter);
542 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
543 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
544 const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx)
546 UINT row_block_count;
547 const BYTE *src_row;
548 BYTE *dst_row;
549 UINT x, y;
551 src_row = src_data;
552 dst_row = dst_data;
554 row_block_count = (update_w + format->block_width - 1) / format->block_width;
556 if (!flags)
558 for (y = 0; y < update_h; y += format->block_height)
560 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
561 src_row += src_pitch;
562 dst_row += dst_pitch;
565 return WINED3D_OK;
568 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
570 src_row += (((update_h / format->block_height) - 1) * src_pitch);
572 switch (format->id)
574 case WINED3DFMT_DXT1:
575 for (y = 0; y < update_h; y += format->block_height)
577 struct block
579 WORD color[2];
580 BYTE control_row[4];
583 const struct block *s = (const struct block *)src_row;
584 struct block *d = (struct block *)dst_row;
586 for (x = 0; x < row_block_count; ++x)
588 d[x].color[0] = s[x].color[0];
589 d[x].color[1] = s[x].color[1];
590 d[x].control_row[0] = s[x].control_row[3];
591 d[x].control_row[1] = s[x].control_row[2];
592 d[x].control_row[2] = s[x].control_row[1];
593 d[x].control_row[3] = s[x].control_row[0];
595 src_row -= src_pitch;
596 dst_row += dst_pitch;
598 return WINED3D_OK;
600 case WINED3DFMT_DXT2:
601 case WINED3DFMT_DXT3:
602 for (y = 0; y < update_h; y += format->block_height)
604 struct block
606 WORD alpha_row[4];
607 WORD color[2];
608 BYTE control_row[4];
611 const struct block *s = (const struct block *)src_row;
612 struct block *d = (struct block *)dst_row;
614 for (x = 0; x < row_block_count; ++x)
616 d[x].alpha_row[0] = s[x].alpha_row[3];
617 d[x].alpha_row[1] = s[x].alpha_row[2];
618 d[x].alpha_row[2] = s[x].alpha_row[1];
619 d[x].alpha_row[3] = s[x].alpha_row[0];
620 d[x].color[0] = s[x].color[0];
621 d[x].color[1] = s[x].color[1];
622 d[x].control_row[0] = s[x].control_row[3];
623 d[x].control_row[1] = s[x].control_row[2];
624 d[x].control_row[2] = s[x].control_row[1];
625 d[x].control_row[3] = s[x].control_row[0];
627 src_row -= src_pitch;
628 dst_row += dst_pitch;
630 return WINED3D_OK;
632 default:
633 FIXME("Compressed flip not implemented for format %s.\n",
634 debug_d3dformat(format->id));
635 return E_NOTIMPL;
639 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
640 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
642 return E_NOTIMPL;
645 static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
646 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
647 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
648 enum wined3d_texture_filter_type filter)
650 unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count;
651 struct wined3d_device *device = dst_texture->resource.device;
652 const struct wined3d_format *src_format, *dst_format;
653 struct wined3d_texture *converted_texture = NULL;
654 struct wined3d_bo_address src_data, dst_data;
655 unsigned int src_fmt_attrs, dst_fmt_attrs;
656 struct wined3d_map_desc dst_map, src_map;
657 unsigned int x, sx, xinc, y, sy, yinc;
658 struct wined3d_context *context;
659 struct wined3d_range dst_range;
660 unsigned int texture_level;
661 HRESULT hr = WINED3D_OK;
662 BOOL same_sub_resource;
663 BOOL upload = FALSE;
664 DWORD map_binding;
665 const BYTE *sbase;
666 const BYTE *sbuf;
667 BYTE *dbuf;
669 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
670 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
671 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture,
672 src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
674 context = context_acquire(device, NULL, 0);
676 src_format = src_texture->resource.format;
677 dst_format = dst_texture->resource.format;
679 if (wined3d_format_is_typeless(src_format) && src_format->id == dst_format->typeless_id)
680 src_format = dst_format;
681 if (wined3d_format_is_typeless(dst_format) && dst_format->id == src_format->typeless_id)
682 dst_format = src_format;
684 src_height = src_box->bottom - src_box->top;
685 src_width = src_box->right - src_box->left;
686 dst_height = dst_box->bottom - dst_box->top;
687 dst_width = dst_box->right - dst_box->left;
689 dst_range.offset = 0;
690 dst_range.size = dst_texture->sub_resources[dst_sub_resource_idx].size;
691 if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx)
693 same_sub_resource = TRUE;
695 map_binding = dst_texture->resource.map_binding;
696 texture_level = dst_sub_resource_idx % dst_texture->level_count;
697 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
698 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
699 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
700 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
701 wined3d_texture_get_bo_address(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
702 dst_map.data = wined3d_context_map_bo_address(context, &dst_data,
703 dst_texture->sub_resources[dst_sub_resource_idx].size, WINED3D_MAP_READ | WINED3D_MAP_WRITE);
705 src_map = dst_map;
707 else
709 same_sub_resource = FALSE;
710 upload = dst_format->attrs & WINED3D_FORMAT_ATTR_BLOCKS
711 && (dst_width != src_width || dst_height != src_height);
713 if (upload)
715 dst_format = src_format->attrs & WINED3D_FORMAT_ATTR_BLOCKS
716 ? wined3d_get_format(device->adapter, WINED3DFMT_B8G8R8A8_UNORM, 0) : src_format;
719 if (!(flags & WINED3D_BLT_RAW) && dst_format->id != src_format->id)
721 if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format)))
723 FIXME("Cannot convert %s to %s.\n", debug_d3dformat(src_format->id),
724 debug_d3dformat(dst_format->id));
725 context_release(context);
726 return WINED3DERR_NOTAVAILABLE;
728 src_texture = converted_texture;
729 src_sub_resource_idx = 0;
730 src_format = src_texture->resource.format;
733 map_binding = src_texture->resource.map_binding;
734 texture_level = src_sub_resource_idx % src_texture->level_count;
735 if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, map_binding))
736 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
737 wined3d_texture_get_pitch(src_texture, texture_level, &src_map.row_pitch, &src_map.slice_pitch);
738 wined3d_texture_get_bo_address(src_texture, src_sub_resource_idx, &src_data, map_binding);
739 src_map.data = wined3d_context_map_bo_address(context, &src_data,
740 src_texture->sub_resources[src_sub_resource_idx].size, WINED3D_MAP_READ);
742 if (upload)
744 wined3d_format_calculate_pitch(dst_format, 1, dst_box->right, dst_box->bottom,
745 &dst_map.row_pitch, &dst_map.slice_pitch);
746 dst_map.data = heap_alloc(dst_map.slice_pitch);
748 else
750 map_binding = dst_texture->resource.map_binding;
751 texture_level = dst_sub_resource_idx % dst_texture->level_count;
752 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
753 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
755 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
756 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
757 wined3d_texture_get_bo_address(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
758 dst_map.data = wined3d_context_map_bo_address(context, &dst_data,
759 dst_texture->sub_resources[dst_sub_resource_idx].size, WINED3D_MAP_WRITE);
762 src_fmt_attrs = src_format->attrs;
763 dst_fmt_attrs = dst_format->attrs;
764 flags &= ~WINED3D_BLT_RAW;
766 bpp = dst_format->byte_count;
767 row_byte_count = dst_width * bpp;
769 sbase = (BYTE *)src_map.data
770 + ((src_box->top / src_format->block_height) * src_map.row_pitch)
771 + ((src_box->left / src_format->block_width) * src_format->block_byte_count);
772 dbuf = (BYTE *)dst_map.data
773 + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch)
774 + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count);
776 if (src_fmt_attrs & dst_fmt_attrs & WINED3D_FORMAT_ATTR_BLOCKS)
778 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
780 if (same_sub_resource)
782 FIXME("Only plain blits supported on compressed surfaces.\n");
783 hr = E_NOTIMPL;
784 goto release;
787 hr = surface_cpu_blt_compressed(sbase, dbuf,
788 src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height,
789 src_format, flags, fx);
790 goto release;
793 if ((src_fmt_attrs | dst_fmt_attrs) & WINED3D_FORMAT_ATTR_HEIGHT_SCALE)
795 FIXME("Unsupported blit between height-scaled formats (src %s, dst %s).\n",
796 debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
797 hr = E_NOTIMPL;
798 goto release;
801 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
802 && (src_width != dst_width || src_height != dst_height))
804 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
805 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
808 xinc = (src_width << 16) / dst_width;
809 yinc = (src_height << 16) / dst_height;
811 if (!flags)
813 /* No effects, we can cheat here. */
814 if (dst_width == src_width)
816 if (dst_height == src_height)
818 /* No stretching in either direction. This needs to be as fast
819 * as possible. */
820 sbuf = sbase;
822 /* Check for overlapping surfaces. */
823 if (!same_sub_resource || dst_box->top < src_box->top
824 || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
826 /* No overlap, or dst above src, so copy from top downwards. */
827 for (y = 0; y < dst_height; ++y)
829 memcpy(dbuf, sbuf, row_byte_count);
830 sbuf += src_map.row_pitch;
831 dbuf += dst_map.row_pitch;
834 else if (dst_box->top > src_box->top)
836 /* Copy from bottom upwards. */
837 sbuf += src_map.row_pitch * dst_height;
838 dbuf += dst_map.row_pitch * dst_height;
839 for (y = 0; y < dst_height; ++y)
841 sbuf -= src_map.row_pitch;
842 dbuf -= dst_map.row_pitch;
843 memcpy(dbuf, sbuf, row_byte_count);
846 else
848 /* Src and dst overlapping on the same line, use memmove. */
849 for (y = 0; y < dst_height; ++y)
851 memmove(dbuf, sbuf, row_byte_count);
852 sbuf += src_map.row_pitch;
853 dbuf += dst_map.row_pitch;
857 else
859 /* Stretching in y direction only. */
860 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
862 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
863 memcpy(dbuf, sbuf, row_byte_count);
864 dbuf += dst_map.row_pitch;
868 else
870 /* Stretching in X direction. */
871 unsigned int last_sy = ~0u;
872 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
874 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
876 if ((sy >> 16) == (last_sy >> 16))
878 /* This source row is the same as last source row -
879 * Copy the already stretched row. */
880 memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count);
882 else
884 #define STRETCH_ROW(type) \
885 do { \
886 const type *s = (const type *)sbuf; \
887 type *d = (type *)dbuf; \
888 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
889 d[x] = s[sx >> 16]; \
890 } while(0)
892 switch(bpp)
894 case 1:
895 STRETCH_ROW(BYTE);
896 break;
897 case 2:
898 STRETCH_ROW(WORD);
899 break;
900 case 4:
901 STRETCH_ROW(DWORD);
902 break;
903 case 3:
905 const BYTE *s;
906 BYTE *d = dbuf;
907 for (x = sx = 0; x < dst_width; x++, sx+= xinc)
909 DWORD pixel;
911 s = sbuf + 3 * (sx >> 16);
912 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
913 d[0] = (pixel ) & 0xff;
914 d[1] = (pixel >> 8) & 0xff;
915 d[2] = (pixel >> 16) & 0xff;
916 d += 3;
918 break;
920 default:
921 FIXME("Stretched blit not implemented for bpp %u.\n", bpp * 8);
922 hr = WINED3DERR_NOTAVAILABLE;
923 goto error;
925 #undef STRETCH_ROW
927 dbuf += dst_map.row_pitch;
928 last_sy = sy;
932 else
934 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
935 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
936 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
937 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
938 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
940 /* The color keying flags are checked for correctness in ddraw. */
941 if (flags & WINED3D_BLT_SRC_CKEY)
943 keylow = src_texture->async.src_blt_color_key.color_space_low_value;
944 keyhigh = src_texture->async.src_blt_color_key.color_space_high_value;
946 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
948 keylow = fx->src_color_key.color_space_low_value;
949 keyhigh = fx->src_color_key.color_space_high_value;
952 if (flags & WINED3D_BLT_DST_CKEY)
954 /* Destination color keys are taken from the source surface! */
955 destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value;
956 destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value;
958 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
960 destkeylow = fx->dst_color_key.color_space_low_value;
961 destkeyhigh = fx->dst_color_key.color_space_high_value;
964 if (bpp == 1)
966 keymask = 0xff;
968 else
970 DWORD masks[3];
971 get_color_masks(src_format, masks);
972 keymask = masks[0] | masks[1] | masks[2];
974 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
975 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
978 if (flags & WINED3D_BLT_FX)
980 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
981 LONG tmpxy;
982 dTopLeft = dbuf;
983 dTopRight = dbuf + ((dst_width - 1) * bpp);
984 dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
985 dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
987 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
989 /* I don't think we need to do anything about this flag. */
990 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
992 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
994 tmp = dTopRight;
995 dTopRight = dTopLeft;
996 dTopLeft = tmp;
997 tmp = dBottomRight;
998 dBottomRight = dBottomLeft;
999 dBottomLeft = tmp;
1000 dstxinc = dstxinc * -1;
1002 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
1004 tmp = dTopLeft;
1005 dTopLeft = dBottomLeft;
1006 dBottomLeft = tmp;
1007 tmp = dTopRight;
1008 dTopRight = dBottomRight;
1009 dBottomRight = tmp;
1010 dstyinc = dstyinc * -1;
1012 if (fx->fx & WINEDDBLTFX_NOTEARING)
1014 /* I don't think we need to do anything about this flag. */
1015 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
1017 if (fx->fx & WINEDDBLTFX_ROTATE180)
1019 tmp = dBottomRight;
1020 dBottomRight = dTopLeft;
1021 dTopLeft = tmp;
1022 tmp = dBottomLeft;
1023 dBottomLeft = dTopRight;
1024 dTopRight = tmp;
1025 dstxinc = dstxinc * -1;
1026 dstyinc = dstyinc * -1;
1028 if (fx->fx & WINEDDBLTFX_ROTATE270)
1030 tmp = dTopLeft;
1031 dTopLeft = dBottomLeft;
1032 dBottomLeft = dBottomRight;
1033 dBottomRight = dTopRight;
1034 dTopRight = tmp;
1035 tmpxy = dstxinc;
1036 dstxinc = dstyinc;
1037 dstyinc = tmpxy;
1038 dstxinc = dstxinc * -1;
1040 if (fx->fx & WINEDDBLTFX_ROTATE90)
1042 tmp = dTopLeft;
1043 dTopLeft = dTopRight;
1044 dTopRight = dBottomRight;
1045 dBottomRight = dBottomLeft;
1046 dBottomLeft = tmp;
1047 tmpxy = dstxinc;
1048 dstxinc = dstyinc;
1049 dstyinc = tmpxy;
1050 dstyinc = dstyinc * -1;
1052 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
1054 /* I don't think we need to do anything about this flag. */
1055 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
1057 dbuf = dTopLeft;
1058 flags &= ~(WINED3D_BLT_FX);
1061 #define COPY_COLORKEY_FX(type) \
1062 do { \
1063 const type *s; \
1064 type *d = (type *)dbuf, *dx, tmp; \
1065 for (y = sy = 0; y < dst_height; ++y, sy += yinc) \
1067 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
1068 dx = d; \
1069 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
1071 tmp = s[sx >> 16]; \
1072 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
1073 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
1075 dx[0] = tmp; \
1077 dx = (type *)(((BYTE *)dx) + dstxinc); \
1079 d = (type *)(((BYTE *)d) + dstyinc); \
1081 } while(0)
1083 switch (bpp)
1085 case 1:
1086 COPY_COLORKEY_FX(BYTE);
1087 break;
1088 case 2:
1089 COPY_COLORKEY_FX(WORD);
1090 break;
1091 case 4:
1092 COPY_COLORKEY_FX(DWORD);
1093 break;
1094 case 3:
1096 const BYTE *s;
1097 BYTE *d = dbuf, *dx;
1098 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
1100 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
1101 dx = d;
1102 for (x = sx = 0; x < dst_width; ++x, sx+= xinc)
1104 DWORD pixel, dpixel = 0;
1105 s = sbuf + 3 * (sx>>16);
1106 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
1107 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
1108 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
1109 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
1111 dx[0] = (pixel ) & 0xff;
1112 dx[1] = (pixel >> 8) & 0xff;
1113 dx[2] = (pixel >> 16) & 0xff;
1115 dx += dstxinc;
1117 d += dstyinc;
1119 break;
1121 default:
1122 FIXME("%s color-keyed blit not implemented for bpp %u.\n",
1123 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
1124 hr = WINED3DERR_NOTAVAILABLE;
1125 goto error;
1126 #undef COPY_COLORKEY_FX
1130 error:
1131 if (flags)
1132 FIXME(" Unsupported flags %#x.\n", flags);
1134 release:
1135 if (upload && hr == WINED3D_OK)
1137 struct wined3d_bo_address data;
1139 data.buffer_object = 0;
1140 data.addr = dst_map.data;
1142 texture_level = dst_sub_resource_idx % dst_texture->level_count;
1144 wined3d_texture_prepare_location(dst_texture, texture_level, context, WINED3D_LOCATION_TEXTURE_RGB);
1145 dst_texture->texture_ops->texture_upload_data(context, wined3d_const_bo_address(&data), dst_format,
1146 dst_box, dst_map.row_pitch, dst_map.slice_pitch, dst_texture, texture_level,
1147 WINED3D_LOCATION_TEXTURE_RGB, dst_box->left, dst_box->top, 0);
1149 wined3d_texture_validate_location(dst_texture, texture_level, WINED3D_LOCATION_TEXTURE_RGB);
1150 wined3d_texture_invalidate_location(dst_texture, texture_level, ~WINED3D_LOCATION_TEXTURE_RGB);
1153 if (upload)
1155 heap_free(dst_map.data);
1157 else
1159 wined3d_context_unmap_bo_address(context, &dst_data, 1, &dst_range);
1162 if (!same_sub_resource)
1163 wined3d_context_unmap_bo_address(context, &src_data, 0, NULL);
1164 if (SUCCEEDED(hr) && dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture)
1166 SetRect(&dst_texture->swapchain->front_buffer_update,
1167 dst_box->left, dst_box->top, dst_box->right, dst_box->bottom);
1168 dst_texture->swapchain->swapchain_ops->swapchain_frontbuffer_updated(dst_texture->swapchain);
1170 if (converted_texture)
1171 wined3d_texture_decref(converted_texture);
1172 context_release(context);
1174 return hr;
1177 static void surface_cpu_blt_colour_fill(struct wined3d_rendertarget_view *view,
1178 const struct wined3d_box *box, const struct wined3d_color *colour)
1180 struct wined3d_device *device = view->resource->device;
1181 struct wined3d_context *context;
1182 struct wined3d_texture *texture;
1183 struct wined3d_bo_address data;
1184 struct wined3d_box level_box;
1185 struct wined3d_map_desc map;
1186 struct wined3d_range range;
1187 bool full_subresource;
1188 unsigned int level;
1189 DWORD map_binding;
1191 TRACE("view %p, box %s, colour %s.\n", view, debug_box(box), debug_color(colour));
1193 if (view->format_attrs & WINED3D_FORMAT_ATTR_BLOCKS)
1195 FIXME("Not implemented for format %s.\n", debug_d3dformat(view->format->id));
1196 return;
1199 if (view->format->id != view->resource->format->id)
1200 FIXME("View format %s doesn't match resource format %s.\n",
1201 debug_d3dformat(view->format->id), debug_d3dformat(view->resource->format->id));
1203 if (view->resource->type == WINED3D_RTYPE_BUFFER)
1205 FIXME("Not implemented for buffers.\n");
1206 return;
1209 context = context_acquire(device, NULL, 0);
1211 texture = texture_from_resource(view->resource);
1212 level = view->sub_resource_idx % texture->level_count;
1213 wined3d_texture_get_level_box(texture_from_resource(view->resource), level, &level_box);
1214 full_subresource = !memcmp(box, &level_box, sizeof(*box));
1216 map_binding = texture->resource.map_binding;
1217 if (!wined3d_texture_load_location(texture, view->sub_resource_idx, context, map_binding))
1218 ERR("Failed to load the sub-resource into %s.\n", wined3d_debug_location(map_binding));
1219 wined3d_texture_invalidate_location(texture, view->sub_resource_idx, ~map_binding);
1220 wined3d_texture_get_pitch(texture, level, &map.row_pitch, &map.slice_pitch);
1221 wined3d_texture_get_bo_address(texture, view->sub_resource_idx, &data, map_binding);
1222 map.data = wined3d_context_map_bo_address(context, &data,
1223 texture->sub_resources[view->sub_resource_idx].size, WINED3D_MAP_WRITE);
1224 range.offset = 0;
1225 range.size = texture->sub_resources[view->sub_resource_idx].size;
1227 wined3d_resource_memory_colour_fill(view->resource, &map, colour, box, full_subresource);
1229 wined3d_context_unmap_bo_address(context, &data, 1, &range);
1230 context_release(context);
1233 static bool wined3d_box_intersect(struct wined3d_box *ret, const struct wined3d_box *b1,
1234 const struct wined3d_box *b2)
1236 wined3d_box_set(ret, max(b1->left, b2->left), max(b1->top, b2->top),
1237 min(b1->right, b2->right), min(b1->bottom, b2->bottom),
1238 max(b1->front, b2->front), min(b1->back, b2->back));
1239 return ret->right > ret->left && ret->bottom > ret->top && ret->back > ret->front;
1242 static void cpu_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
1243 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
1244 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
1246 struct wined3d_color c = {depth, 0.0f, 0.0f, 0.0f};
1247 struct wined3d_box box, box_clip, box_view;
1248 struct wined3d_rendertarget_view *view;
1249 unsigned int i, j;
1251 if (!rect_count)
1253 rect_count = 1;
1254 clear_rects = draw_rect;
1257 for (i = 0; i < rect_count; ++i)
1259 box.left = max(clear_rects[i].left, draw_rect->left);
1260 box.top = max(clear_rects[i].top, draw_rect->top);
1261 box.right = min(clear_rects[i].right, draw_rect->right);
1262 box.bottom = min(clear_rects[i].bottom, draw_rect->bottom);
1263 box.front = 0;
1264 box.back = ~0u;
1266 if (box.left >= box.right || box.top >= box.bottom)
1267 continue;
1269 if (flags & WINED3DCLEAR_TARGET)
1271 for (j = 0; j < rt_count; ++j)
1273 if ((view = fb->render_targets[j]))
1275 wined3d_rendertarget_view_get_box(view, &box_view);
1276 if (wined3d_box_intersect(&box_clip, &box_view, &box))
1277 surface_cpu_blt_colour_fill(view, &box_clip, colour);
1282 if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil))
1284 if ((view->format->depth_size && !(flags & WINED3DCLEAR_ZBUFFER))
1285 || (view->format->stencil_size && !(flags & WINED3DCLEAR_STENCIL)))
1286 FIXME("Clearing %#x on %s.\n", flags, debug_d3dformat(view->format->id));
1288 wined3d_rendertarget_view_get_box(view, &box_view);
1289 if (wined3d_box_intersect(&box_clip, &box_view, &box))
1290 surface_cpu_blt_colour_fill(view, &box_clip, &c);
1295 static DWORD cpu_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
1296 struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1297 DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture,
1298 unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect,
1299 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter,
1300 const struct wined3d_format *resolve_format)
1302 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
1303 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
1304 struct wined3d_blt_fx fx;
1305 DWORD flags = 0;
1307 memset(&fx, 0, sizeof(fx));
1308 switch (op)
1310 case WINED3D_BLIT_OP_COLOR_BLIT:
1311 case WINED3D_BLIT_OP_DEPTH_BLIT:
1312 break;
1313 case WINED3D_BLIT_OP_RAW_BLIT:
1314 flags |= WINED3D_BLT_RAW;
1315 break;
1316 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
1317 flags |= WINED3D_BLT_ALPHA_TEST;
1318 break;
1319 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
1320 flags |= WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_FX;
1321 fx.src_color_key = *color_key;
1322 break;
1323 default:
1324 FIXME("Unhandled op %#x.\n", op);
1325 break;
1328 if (FAILED(surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
1329 src_texture, src_sub_resource_idx, &src_box, flags, &fx, filter)))
1330 ERR("Failed to blit.\n");
1331 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
1333 return dst_location | (dst_texture->sub_resources[dst_sub_resource_idx].locations
1334 & dst_texture->resource.map_binding);
1337 static const struct wined3d_blitter_ops cpu_blitter_ops =
1339 cpu_blitter_destroy,
1340 cpu_blitter_clear,
1341 cpu_blitter_blit,
1344 struct wined3d_blitter *wined3d_cpu_blitter_create(void)
1346 struct wined3d_blitter *blitter;
1348 if (!(blitter = heap_alloc(sizeof(*blitter))))
1349 return NULL;
1351 TRACE("Created blitter %p.\n", blitter);
1353 blitter->ops = &cpu_blitter_ops;
1354 blitter->next = NULL;
1356 return blitter;
1359 static bool wined3d_is_colour_blit(enum wined3d_blit_op blit_op)
1361 switch (blit_op)
1363 case WINED3D_BLIT_OP_COLOR_BLIT:
1364 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
1365 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
1366 return true;
1368 default:
1369 return false;
1373 static bool sub_resource_is_on_cpu(const struct wined3d_texture *texture, unsigned int sub_resource_idx)
1375 DWORD locations = texture->sub_resources[sub_resource_idx].locations;
1377 if (locations & (WINED3D_LOCATION_BUFFER | WINED3D_LOCATION_SYSMEM))
1378 return true;
1380 if (!(texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU) && (locations & WINED3D_LOCATION_CLEARED))
1381 return true;
1383 return false;
1386 HRESULT texture2d_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
1387 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1388 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
1389 enum wined3d_texture_filter_type filter)
1391 struct wined3d_texture_sub_resource *src_sub_resource, *dst_sub_resource;
1392 struct wined3d_device *device = dst_texture->resource.device;
1393 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1394 BOOL scale, convert, resolve, resolve_typeless = FALSE;
1395 const struct wined3d_format *resolve_format = NULL;
1396 const struct wined3d_color_key *colour_key = NULL;
1397 DWORD src_location, dst_location, valid_locations;
1398 struct wined3d_context *context;
1399 enum wined3d_blit_op blit_op;
1400 RECT src_rect, dst_rect;
1401 bool src_ds, dst_ds;
1403 static const DWORD simple_blit = WINED3D_BLT_SRC_CKEY
1404 | WINED3D_BLT_SRC_CKEY_OVERRIDE
1405 | WINED3D_BLT_ALPHA_TEST
1406 | WINED3D_BLT_RAW;
1408 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
1409 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
1410 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture, src_sub_resource_idx,
1411 debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
1412 TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage));
1414 if (fx)
1416 TRACE("fx %#x.\n", fx->fx);
1417 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
1418 fx->dst_color_key.color_space_low_value,
1419 fx->dst_color_key.color_space_high_value);
1420 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
1421 fx->src_color_key.color_space_low_value,
1422 fx->src_color_key.color_space_high_value);
1423 TRACE("resolve_format_id %s.\n", debug_d3dformat(fx->resolve_format_id));
1425 if (fx->resolve_format_id != WINED3DFMT_UNKNOWN)
1426 resolve_format = wined3d_get_format(device->adapter, fx->resolve_format_id, 0);
1429 dst_sub_resource = &dst_texture->sub_resources[dst_sub_resource_idx];
1430 src_sub_resource = &src_texture->sub_resources[src_sub_resource_idx];
1432 if (src_sub_resource->locations & WINED3D_LOCATION_DISCARDED)
1434 WARN("Source sub-resource is discarded, nothing to do.\n");
1435 return WINED3D_OK;
1438 SetRect(&src_rect, src_box->left, src_box->top, src_box->right, src_box->bottom);
1439 SetRect(&dst_rect, dst_box->left, dst_box->top, dst_box->right, dst_box->bottom);
1441 if (!fx || !(fx->fx))
1442 flags &= ~WINED3D_BLT_FX;
1444 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
1445 if (flags & WINED3D_BLT_DO_NOT_WAIT)
1447 static unsigned int once;
1449 if (!once++)
1450 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
1453 flags &= ~(WINED3D_BLT_SYNCHRONOUS | WINED3D_BLT_DO_NOT_WAIT | WINED3D_BLT_WAIT);
1455 if (flags & ~simple_blit)
1457 WARN_(d3d_perf)("Using CPU fallback for complex blit (%#x).\n", flags);
1458 goto cpu;
1461 src_swapchain = src_texture->swapchain;
1462 dst_swapchain = dst_texture->swapchain;
1464 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain
1465 && (wined3d_settings.offscreen_rendering_mode != ORM_FBO
1466 || src_texture == src_swapchain->front_buffer))
1468 /* TODO: We could support cross-swapchain blits by first downloading
1469 * the source to a texture. */
1470 FIXME("Cross-swapchain blit not supported.\n");
1471 return WINED3DERR_INVALIDCALL;
1474 scale = src_box->right - src_box->left != dst_box->right - dst_box->left
1475 || src_box->bottom - src_box->top != dst_box->bottom - dst_box->top;
1476 convert = src_texture->resource.format->id != dst_texture->resource.format->id;
1477 resolve = src_texture->resource.multisample_type != dst_texture->resource.multisample_type;
1478 if (resolve)
1480 resolve_typeless = (wined3d_format_is_typeless(src_texture->resource.format)
1481 || wined3d_format_is_typeless(dst_texture->resource.format))
1482 && (src_texture->resource.format->typeless_id == dst_texture->resource.format->typeless_id);
1483 if (resolve_typeless && !resolve_format)
1484 WARN("Resolve format for typeless resolve not specified.\n");
1487 dst_ds = dst_texture->resource.format->depth_size || dst_texture->resource.format->stencil_size;
1488 src_ds = src_texture->resource.format->depth_size || src_texture->resource.format->stencil_size;
1490 if (src_ds || dst_ds)
1492 TRACE("Depth/stencil blit.\n");
1494 if (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)
1495 dst_location = dst_texture->resource.draw_binding;
1496 else
1497 dst_location = dst_texture->resource.map_binding;
1499 if ((flags & WINED3D_BLT_RAW) || (!scale && !convert && !resolve))
1500 blit_op = WINED3D_BLIT_OP_RAW_BLIT;
1501 else
1502 blit_op = WINED3D_BLIT_OP_DEPTH_BLIT;
1504 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
1505 valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context,
1506 src_texture, src_sub_resource_idx, src_texture->resource.draw_binding, &src_rect,
1507 dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, NULL, filter, resolve_format);
1508 context_release(context);
1510 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
1511 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
1513 return WINED3D_OK;
1516 TRACE("Colour blit.\n");
1518 /* In principle this would apply to depth blits as well, but we don't
1519 * implement those in the CPU blitter at the moment. */
1520 if ((dst_sub_resource->locations & dst_texture->resource.map_binding)
1521 && (src_sub_resource->locations & src_texture->resource.map_binding))
1523 if (scale)
1524 TRACE("Not doing sysmem blit because of scaling.\n");
1525 else if (convert)
1526 TRACE("Not doing sysmem blit because of format conversion.\n");
1527 else
1528 goto cpu;
1531 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
1532 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
1534 colour_key = &fx->src_color_key;
1535 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
1537 else if (flags & WINED3D_BLT_SRC_CKEY)
1539 colour_key = &src_texture->async.src_blt_color_key;
1540 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
1542 else if (flags & WINED3D_BLT_ALPHA_TEST)
1544 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
1546 else if (sub_resource_is_on_cpu(src_texture, src_sub_resource_idx)
1547 && !sub_resource_is_on_cpu(dst_texture, dst_sub_resource_idx)
1548 && (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1550 /* Upload */
1551 if (scale)
1552 TRACE("Not doing upload because of scaling.\n");
1553 else if (convert)
1554 TRACE("Not doing upload because of format conversion.\n");
1555 else if (dst_texture->resource.format->conv_byte_count)
1556 TRACE("Not doing upload because the destination format needs conversion.\n");
1557 else
1559 wined3d_texture_upload_from_texture(dst_texture, dst_sub_resource_idx, dst_box->left,
1560 dst_box->top, dst_box->front, src_texture, src_sub_resource_idx, src_box);
1561 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
1563 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
1564 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx,
1565 context, dst_texture->resource.draw_binding);
1566 context_release(context);
1568 return WINED3D_OK;
1571 else if (!sub_resource_is_on_cpu(src_texture, src_sub_resource_idx)
1572 && (dst_sub_resource->locations & dst_texture->resource.map_binding)
1573 && !(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1575 /* Download */
1576 if (scale)
1577 TRACE("Not doing download because of scaling.\n");
1578 else if (convert)
1579 TRACE("Not doing download because of format conversion.\n");
1580 else if (src_texture->resource.format->conv_byte_count)
1581 TRACE("Not doing download because the source format needs conversion.\n");
1582 else if (!(src_texture->flags & WINED3D_TEXTURE_DOWNLOADABLE))
1583 TRACE("Not doing download because texture is not downloadable.\n");
1584 else if (!wined3d_texture_is_full_rect(src_texture, src_sub_resource_idx % src_texture->level_count, &src_rect))
1585 TRACE("Not doing download because of partial download (src).\n");
1586 else if (!wined3d_texture_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, &dst_rect))
1587 TRACE("Not doing download because of partial download (dst).\n");
1588 else
1590 wined3d_texture_download_from_texture(dst_texture, dst_sub_resource_idx, src_texture,
1591 src_sub_resource_idx);
1592 return WINED3D_OK;
1595 else if (dst_swapchain && dst_swapchain->back_buffers
1596 && dst_texture == dst_swapchain->front_buffer
1597 && src_texture == dst_swapchain->back_buffers[0])
1599 /* Use present for back -> front blits. The idea behind this is that
1600 * present is potentially faster than a blit, in particular when FBO
1601 * blits aren't available. Some ddraw applications like Half-Life and
1602 * Prince of Persia 3D use Blt() from the backbuffer to the
1603 * frontbuffer instead of doing a Flip(). D3d8 and d3d9 applications
1604 * can't blit directly to the frontbuffer. */
1605 enum wined3d_swap_effect swap_effect = dst_swapchain->state.desc.swap_effect;
1607 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
1609 /* Set the swap effect to COPY, we don't want the backbuffer to become
1610 * undefined. */
1611 dst_swapchain->state.desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
1612 wined3d_swapchain_present(dst_swapchain, NULL, NULL,
1613 dst_swapchain->win_handle, dst_swapchain->swap_interval, 0);
1614 dst_swapchain->state.desc.swap_effect = swap_effect;
1616 return WINED3D_OK;
1619 if ((flags & WINED3D_BLT_RAW) || (blit_op == WINED3D_BLIT_OP_COLOR_BLIT && !scale && !convert && !resolve))
1620 blit_op = WINED3D_BLIT_OP_RAW_BLIT;
1622 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
1624 if (src_texture->resource.multisample_type != WINED3D_MULTISAMPLE_NONE && !resolve_typeless
1625 && ((scale && !context->d3d_info->scaled_resolve)
1626 || convert || !wined3d_is_colour_blit(blit_op)))
1627 src_location = WINED3D_LOCATION_RB_RESOLVED;
1628 else
1629 src_location = src_texture->resource.draw_binding;
1631 if (!(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1632 dst_location = dst_texture->resource.map_binding;
1633 else if (dst_texture->resource.multisample_type != WINED3D_MULTISAMPLE_NONE
1634 && (scale || convert || !wined3d_is_colour_blit(blit_op)))
1635 dst_location = WINED3D_LOCATION_RB_RESOLVED;
1636 else
1637 dst_location = dst_texture->resource.draw_binding;
1639 valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context,
1640 src_texture, src_sub_resource_idx, src_location, &src_rect,
1641 dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, colour_key, filter, resolve_format);
1643 context_release(context);
1645 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
1646 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
1648 return WINED3D_OK;
1650 cpu:
1651 return surface_cpu_blt(dst_texture, dst_sub_resource_idx, dst_box,
1652 src_texture, src_sub_resource_idx, src_box, flags, fx, filter);