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
;
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,
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
)
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;
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;
154 g2
= - 100 * d
- 208 * e
+ 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. */
170 static void convert_yuy2_r5g6b5(const BYTE
*src
, BYTE
*dst
,
171 DWORD pitch_in
, DWORD pitch_out
, unsigned int w
, unsigned int h
)
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;
196 g2
= - 100 * d
- 208 * e
+ 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. */
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
)
232 for (i
= 0; i
< ARRAY_SIZE(converters
); ++i
)
234 if (converters
[i
].from
== from
&& converters
[i
].to
== to
)
235 return &converters
[i
];
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
;
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
));
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
;
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
);
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");
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
);
295 unsigned int dst_row_pitch
, dst_slice_pitch
;
296 struct wined3d_bo_address dst_data
;
297 struct wined3d_range range
;
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
);
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
);
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
);
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
;
359 /* dst_location was already prepared by the caller. */
360 wined3d_texture_get_bo_address(texture
, sub_resource_idx
, &data
, dst_location
);
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
);
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
);
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
;
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
;
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
)))
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
;
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
);
463 if (data
.buffer_object
)
464 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER
));
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");
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
;
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
);
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
));
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");
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
);
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
;
554 row_block_count
= (update_w
+ format
->block_width
- 1) / format
->block_width
;
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
;
568 if (flags
== WINED3D_BLT_FX
&& fx
->fx
== WINEDDBLTFX_MIRRORUPDOWN
)
570 src_row
+= (((update_h
/ format
->block_height
) - 1) * src_pitch
);
574 case WINED3DFMT_DXT1
:
575 for (y
= 0; y
< update_h
; y
+= format
->block_height
)
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
;
600 case WINED3DFMT_DXT2
:
601 case WINED3DFMT_DXT3
:
602 for (y
= 0; y
< update_h
; y
+= format
->block_height
)
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
;
633 FIXME("Compressed flip not implemented for format %s.\n",
634 debug_d3dformat(format
->id
));
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);
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
;
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
);
709 same_sub_resource
= FALSE
;
710 upload
= dst_format
->attrs
& WINED3D_FORMAT_ATTR_BLOCKS
711 && (dst_width
!= src_width
|| dst_height
!= src_height
);
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
);
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
);
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");
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
);
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
));
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
;
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
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
);
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
;
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
;
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
);
884 #define STRETCH_ROW(type) \
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]; \
907 for (x
= sx
= 0; x
< dst_width
; x
++, sx
+= xinc
)
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;
921 FIXME("Stretched blit not implemented for bpp %u.\n", bpp
* 8);
922 hr
= WINED3DERR_NOTAVAILABLE
;
927 dbuf
+= dst_map
.row_pitch
;
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
;
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
;
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
)
995 dTopRight
= dTopLeft
;
998 dBottomRight
= dBottomLeft
;
1000 dstxinc
= dstxinc
* -1;
1002 if (fx
->fx
& WINEDDBLTFX_MIRRORUPDOWN
)
1005 dTopLeft
= dBottomLeft
;
1008 dTopRight
= dBottomRight
;
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
)
1020 dBottomRight
= dTopLeft
;
1023 dBottomLeft
= dTopRight
;
1025 dstxinc
= dstxinc
* -1;
1026 dstyinc
= dstyinc
* -1;
1028 if (fx
->fx
& WINEDDBLTFX_ROTATE270
)
1031 dTopLeft
= dBottomLeft
;
1032 dBottomLeft
= dBottomRight
;
1033 dBottomRight
= dTopRight
;
1038 dstxinc
= dstxinc
* -1;
1040 if (fx
->fx
& WINEDDBLTFX_ROTATE90
)
1043 dTopLeft
= dTopRight
;
1044 dTopRight
= dBottomRight
;
1045 dBottomRight
= dBottomLeft
;
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");
1058 flags
&= ~(WINED3D_BLT_FX
);
1061 #define COPY_COLORKEY_FX(type) \
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); \
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)) \
1077 dx = (type *)(((BYTE *)dx) + dstxinc); \
1079 d = (type *)(((BYTE *)d) + dstyinc); \
1086 COPY_COLORKEY_FX(BYTE
);
1089 COPY_COLORKEY_FX(WORD
);
1092 COPY_COLORKEY_FX(DWORD
);
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
;
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;
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
;
1126 #undef COPY_COLORKEY_FX
1132 FIXME(" Unsupported flags %#x.\n", flags
);
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
);
1155 heap_free(dst_map
.data
);
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
);
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
;
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
));
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");
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
);
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
;
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
);
1266 if (box
.left
>= box
.right
|| box
.top
>= box
.bottom
)
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
;
1307 memset(&fx
, 0, sizeof(fx
));
1310 case WINED3D_BLIT_OP_COLOR_BLIT
:
1311 case WINED3D_BLIT_OP_DEPTH_BLIT
:
1313 case WINED3D_BLIT_OP_RAW_BLIT
:
1314 flags
|= WINED3D_BLT_RAW
;
1316 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST
:
1317 flags
|= WINED3D_BLT_ALPHA_TEST
;
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
;
1324 FIXME("Unhandled op %#x.\n", op
);
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
,
1344 struct wined3d_blitter
*wined3d_cpu_blitter_create(void)
1346 struct wined3d_blitter
*blitter
;
1348 if (!(blitter
= heap_alloc(sizeof(*blitter
))))
1351 TRACE("Created blitter %p.\n", blitter
);
1353 blitter
->ops
= &cpu_blitter_ops
;
1354 blitter
->next
= NULL
;
1359 static bool wined3d_is_colour_blit(enum wined3d_blit_op 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
:
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
))
1380 if (!(texture
->resource
.access
& WINED3D_RESOURCE_ACCESS_GPU
) && (locations
& WINED3D_LOCATION_CLEARED
))
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
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
));
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");
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
;
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
);
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
;
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
;
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
;
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
);
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
))
1524 TRACE("Not doing sysmem blit because of scaling.\n");
1526 TRACE("Not doing sysmem blit because of format conversion.\n");
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
))
1552 TRACE("Not doing upload because of scaling.\n");
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");
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
);
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
))
1577 TRACE("Not doing download because of scaling.\n");
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");
1590 wined3d_texture_download_from_texture(dst_texture
, dst_sub_resource_idx
, src_texture
,
1591 src_sub_resource_idx
);
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
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
;
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
;
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
;
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
);
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
);