wined3d: Merge wined3d_surface_upload_data() into texture2d_upload_data().
[wine.git] / dlls / wined3d / surface.c
blob3760abbff948fe87193e29f30c8202917f7f8674
1 /*
2 * Copyright 1997-2000 Marcus Meissner
3 * Copyright 1998-2000 Lionel Ulmer
4 * Copyright 2000-2001 TransGaming Technologies Inc.
5 * Copyright 2002-2005 Jason Edmeades
6 * Copyright 2002-2003 Raphael Junqueira
7 * Copyright 2004 Christian Costa
8 * Copyright 2005 Oliver Stieber
9 * Copyright 2006-2011, 2013-2014 Stefan Dösinger for CodeWeavers
10 * Copyright 2007-2008 Henri Verbeet
11 * Copyright 2006-2008 Roderick Colenbrander
12 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
36 static const DWORD surface_simple_locations = WINED3D_LOCATION_SYSMEM
37 | WINED3D_LOCATION_USER_MEMORY | WINED3D_LOCATION_BUFFER;
39 struct blt_info
41 GLenum binding;
42 GLenum bind_target;
43 enum wined3d_gl_resource_type tex_type;
44 struct wined3d_vec3 texcoords[4];
47 struct float_rect
49 float l;
50 float t;
51 float r;
52 float b;
55 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
57 f->l = ((r->left * 2.0f) / w) - 1.0f;
58 f->t = ((r->top * 2.0f) / h) - 1.0f;
59 f->r = ((r->right * 2.0f) / w) - 1.0f;
60 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
63 static void texture2d_get_blt_info(const struct wined3d_texture *texture,
64 unsigned int sub_resource_idx, const RECT *rect, struct blt_info *info)
66 struct wined3d_vec3 *coords = info->texcoords;
67 struct float_rect f;
68 unsigned int level;
69 GLenum target;
70 GLsizei w, h;
72 level = sub_resource_idx % texture->level_count;
73 w = wined3d_texture_get_level_pow2_width(texture, level);
74 h = wined3d_texture_get_level_pow2_height(texture, level);
75 target = wined3d_texture_get_sub_resource_target(texture, sub_resource_idx);
77 switch (target)
79 default:
80 FIXME("Unsupported texture target %#x.\n", target);
81 /* Fall back to GL_TEXTURE_2D */
82 case GL_TEXTURE_2D:
83 info->binding = GL_TEXTURE_BINDING_2D;
84 info->bind_target = GL_TEXTURE_2D;
85 info->tex_type = WINED3D_GL_RES_TYPE_TEX_2D;
86 coords[0].x = (float)rect->left / w;
87 coords[0].y = (float)rect->top / h;
88 coords[0].z = 0.0f;
90 coords[1].x = (float)rect->right / w;
91 coords[1].y = (float)rect->top / h;
92 coords[1].z = 0.0f;
94 coords[2].x = (float)rect->left / w;
95 coords[2].y = (float)rect->bottom / h;
96 coords[2].z = 0.0f;
98 coords[3].x = (float)rect->right / w;
99 coords[3].y = (float)rect->bottom / h;
100 coords[3].z = 0.0f;
101 break;
103 case GL_TEXTURE_RECTANGLE_ARB:
104 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
105 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
106 info->tex_type = WINED3D_GL_RES_TYPE_TEX_RECT;
107 coords[0].x = rect->left; coords[0].y = rect->top; coords[0].z = 0.0f;
108 coords[1].x = rect->right; coords[1].y = rect->top; coords[1].z = 0.0f;
109 coords[2].x = rect->left; coords[2].y = rect->bottom; coords[2].z = 0.0f;
110 coords[3].x = rect->right; coords[3].y = rect->bottom; coords[3].z = 0.0f;
111 break;
113 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
114 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
115 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
116 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
117 cube_coords_float(rect, w, h, &f);
119 coords[0].x = 1.0f; coords[0].y = -f.t; coords[0].z = -f.l;
120 coords[1].x = 1.0f; coords[1].y = -f.t; coords[1].z = -f.r;
121 coords[2].x = 1.0f; coords[2].y = -f.b; coords[2].z = -f.l;
122 coords[3].x = 1.0f; coords[3].y = -f.b; coords[3].z = -f.r;
123 break;
125 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
126 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
127 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
128 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
129 cube_coords_float(rect, w, h, &f);
131 coords[0].x = -1.0f; coords[0].y = -f.t; coords[0].z = f.l;
132 coords[1].x = -1.0f; coords[1].y = -f.t; coords[1].z = f.r;
133 coords[2].x = -1.0f; coords[2].y = -f.b; coords[2].z = f.l;
134 coords[3].x = -1.0f; coords[3].y = -f.b; coords[3].z = f.r;
135 break;
137 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
138 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
139 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
140 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
141 cube_coords_float(rect, w, h, &f);
143 coords[0].x = f.l; coords[0].y = 1.0f; coords[0].z = f.t;
144 coords[1].x = f.r; coords[1].y = 1.0f; coords[1].z = f.t;
145 coords[2].x = f.l; coords[2].y = 1.0f; coords[2].z = f.b;
146 coords[3].x = f.r; coords[3].y = 1.0f; coords[3].z = f.b;
147 break;
149 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
150 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
151 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
152 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
153 cube_coords_float(rect, w, h, &f);
155 coords[0].x = f.l; coords[0].y = -1.0f; coords[0].z = -f.t;
156 coords[1].x = f.r; coords[1].y = -1.0f; coords[1].z = -f.t;
157 coords[2].x = f.l; coords[2].y = -1.0f; coords[2].z = -f.b;
158 coords[3].x = f.r; coords[3].y = -1.0f; coords[3].z = -f.b;
159 break;
161 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
162 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
163 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
164 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
165 cube_coords_float(rect, w, h, &f);
167 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1.0f;
168 coords[1].x = f.r; coords[1].y = -f.t; coords[1].z = 1.0f;
169 coords[2].x = f.l; coords[2].y = -f.b; coords[2].z = 1.0f;
170 coords[3].x = f.r; coords[3].y = -f.b; coords[3].z = 1.0f;
171 break;
173 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
174 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
175 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
176 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
177 cube_coords_float(rect, w, h, &f);
179 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1.0f;
180 coords[1].x = -f.r; coords[1].y = -f.t; coords[1].z = -1.0f;
181 coords[2].x = -f.l; coords[2].y = -f.b; coords[2].z = -1.0f;
182 coords[3].x = -f.r; coords[3].y = -f.b; coords[3].z = -1.0f;
183 break;
187 /* Context activation is done by the caller. */
188 void draw_textured_quad(struct wined3d_texture *texture, unsigned int sub_resource_idx,
189 struct wined3d_context *context, const RECT *src_rect, const RECT *dst_rect,
190 enum wined3d_texture_filter_type filter)
192 const struct wined3d_gl_info *gl_info = context->gl_info;
193 struct blt_info info;
195 texture2d_get_blt_info(texture, sub_resource_idx, src_rect, &info);
197 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
198 checkGLcall("glEnable(bind_target)");
200 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
202 /* Filtering for StretchRect */
203 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
204 checkGLcall("glTexParameteri");
205 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
206 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
207 checkGLcall("glTexParameteri");
208 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
209 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
210 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
211 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
212 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
213 checkGLcall("glTexEnvi");
215 /* Draw a quad */
216 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
217 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[0].x);
218 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
220 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[1].x);
221 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
223 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[2].x);
224 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
226 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[3].x);
227 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
228 gl_info->gl_ops.gl.p_glEnd();
230 /* Unbind the texture */
231 context_bind_texture(context, info.bind_target, 0);
233 /* We changed the filtering settings on the texture. Inform the
234 * container about this to get the filters reset properly next draw. */
235 texture->texture_rgb.sampler_desc.mag_filter = WINED3D_TEXF_POINT;
236 texture->texture_rgb.sampler_desc.min_filter = WINED3D_TEXF_POINT;
237 texture->texture_rgb.sampler_desc.mip_filter = WINED3D_TEXF_NONE;
238 texture->texture_rgb.sampler_desc.srgb_decode = FALSE;
241 /* Works correctly only for <= 4 bpp formats. */
242 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
244 masks[0] = ((1u << format->red_size) - 1) << format->red_offset;
245 masks[1] = ((1u << format->green_size) - 1) << format->green_offset;
246 masks[2] = ((1u << format->blue_size) - 1) << format->blue_offset;
249 static BOOL texture2d_is_full_rect(const struct wined3d_texture *texture, unsigned int level, const RECT *r)
251 unsigned int t;
253 t = wined3d_texture_get_level_width(texture, level);
254 if ((r->left && r->right) || abs(r->right - r->left) != t)
255 return FALSE;
256 t = wined3d_texture_get_level_height(texture, level);
257 if ((r->top && r->bottom) || abs(r->bottom - r->top) != t)
258 return FALSE;
259 return TRUE;
262 static void texture2d_depth_blt_fbo(const struct wined3d_device *device, struct wined3d_context *context,
263 struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, DWORD src_location,
264 const RECT *src_rect, struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
265 DWORD dst_location, const RECT *dst_rect)
267 const struct wined3d_gl_info *gl_info = context->gl_info;
268 DWORD src_mask, dst_mask;
269 GLbitfield gl_mask;
271 TRACE("device %p, src_texture %p, src_sub_resource_idx %u, src_location %s, src_rect %s, "
272 "dst_texture %p, dst_sub_resource_idx %u, dst_location %s, dst_rect %s.\n", device,
273 src_texture, src_sub_resource_idx, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect),
274 dst_texture, dst_sub_resource_idx, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
276 src_mask = src_texture->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
277 dst_mask = dst_texture->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
279 if (src_mask != dst_mask)
281 ERR("Incompatible formats %s and %s.\n",
282 debug_d3dformat(src_texture->resource.format->id),
283 debug_d3dformat(dst_texture->resource.format->id));
284 return;
287 if (!src_mask)
289 ERR("Not a depth / stencil format: %s.\n",
290 debug_d3dformat(src_texture->resource.format->id));
291 return;
294 gl_mask = 0;
295 if (src_mask & WINED3DFMT_FLAG_DEPTH)
296 gl_mask |= GL_DEPTH_BUFFER_BIT;
297 if (src_mask & WINED3DFMT_FLAG_STENCIL)
298 gl_mask |= GL_STENCIL_BUFFER_BIT;
300 /* Make sure the locations are up-to-date. Loading the destination
301 * surface isn't required if the entire surface is overwritten. */
302 wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, src_location);
303 if (!texture2d_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, dst_rect))
304 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
305 else
306 wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, dst_location);
308 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, 0,
309 &src_texture->resource, src_sub_resource_idx, src_location);
310 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
312 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, 0,
313 &dst_texture->resource, dst_sub_resource_idx, dst_location);
314 context_set_draw_buffer(context, GL_NONE);
315 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
316 context_invalidate_state(context, STATE_FRAMEBUFFER);
318 if (gl_mask & GL_DEPTH_BUFFER_BIT)
320 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
321 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
323 if (gl_mask & GL_STENCIL_BUFFER_BIT)
325 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
327 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
328 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
330 gl_info->gl_ops.gl.p_glStencilMask(~0U);
331 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
334 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
335 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
337 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
338 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
339 checkGLcall("glBlitFramebuffer()");
341 if (wined3d_settings.strict_draw_ordering)
342 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
345 static BOOL is_multisample_location(const struct wined3d_texture *texture, DWORD location)
347 if (location == WINED3D_LOCATION_RB_MULTISAMPLE)
348 return TRUE;
349 if (location != WINED3D_LOCATION_TEXTURE_RGB && location != WINED3D_LOCATION_TEXTURE_SRGB)
350 return FALSE;
351 return texture->target == GL_TEXTURE_2D_MULTISAMPLE || texture->target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
354 /* Blit between surface locations. Onscreen on different swapchains is not supported.
355 * Depth / stencil is not supported. Context activation is done by the caller. */
356 static void texture2d_blt_fbo(const struct wined3d_device *device, struct wined3d_context *context,
357 enum wined3d_texture_filter_type filter, struct wined3d_texture *src_texture,
358 unsigned int src_sub_resource_idx, DWORD src_location, const RECT *src_rect,
359 struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, DWORD dst_location,
360 const RECT *dst_rect)
362 struct wined3d_texture *required_texture, *restore_texture;
363 unsigned int required_idx, restore_idx;
364 const struct wined3d_gl_info *gl_info;
365 GLenum gl_filter;
366 GLenum buffer;
367 RECT s, d;
369 TRACE("device %p, context %p, filter %s, src_texture %p, src_sub_resource_idx %u, src_location %s, "
370 "src_rect %s, dst_texture %p, dst_sub_resource_idx %u, dst_location %s, dst_rect %s.\n",
371 device, context, debug_d3dtexturefiltertype(filter), src_texture, src_sub_resource_idx,
372 wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect), dst_texture,
373 dst_sub_resource_idx, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
375 switch (filter)
377 case WINED3D_TEXF_LINEAR:
378 gl_filter = GL_LINEAR;
379 break;
381 default:
382 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
383 case WINED3D_TEXF_NONE:
384 case WINED3D_TEXF_POINT:
385 gl_filter = GL_NEAREST;
386 break;
389 /* Resolve the source surface first if needed. */
390 if (is_multisample_location(src_texture, src_location)
391 && (src_texture->resource.format->id != dst_texture->resource.format->id
392 || abs(src_rect->bottom - src_rect->top) != abs(dst_rect->bottom - dst_rect->top)
393 || abs(src_rect->right - src_rect->left) != abs(dst_rect->right - dst_rect->left)))
394 src_location = WINED3D_LOCATION_RB_RESOLVED;
396 /* Make sure the locations are up-to-date. Loading the destination
397 * surface isn't required if the entire surface is overwritten. (And is
398 * in fact harmful if we're being called by surface_load_location() with
399 * the purpose of loading the destination surface.) */
400 wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, src_location);
401 if (!texture2d_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, dst_rect))
402 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
403 else
404 wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, dst_location);
407 if (src_location == WINED3D_LOCATION_DRAWABLE)
409 required_texture = src_texture;
410 required_idx = src_sub_resource_idx;
412 else if (dst_location == WINED3D_LOCATION_DRAWABLE)
414 required_texture = dst_texture;
415 required_idx = dst_sub_resource_idx;
417 else
419 required_texture = NULL;
420 required_idx = 0;
423 restore_texture = context->current_rt.texture;
424 restore_idx = context->current_rt.sub_resource_idx;
425 if (restore_texture != required_texture || restore_idx != required_idx)
426 context = context_acquire(device, required_texture, required_idx);
427 else
428 restore_texture = NULL;
430 if (!context->valid)
432 context_release(context);
433 WARN("Invalid context, skipping blit.\n");
434 return;
437 gl_info = context->gl_info;
439 if (src_location == WINED3D_LOCATION_DRAWABLE)
441 TRACE("Source texture %p is onscreen.\n", src_texture);
442 buffer = wined3d_texture_get_gl_buffer(src_texture);
443 s = *src_rect;
444 wined3d_texture_translate_drawable_coords(src_texture, context->win_handle, &s);
445 src_rect = &s;
447 else
449 TRACE("Source texture %p is offscreen.\n", src_texture);
450 buffer = GL_COLOR_ATTACHMENT0;
453 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER,
454 &src_texture->resource, src_sub_resource_idx, NULL, 0, src_location);
455 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
456 checkGLcall("glReadBuffer()");
457 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
459 if (dst_location == WINED3D_LOCATION_DRAWABLE)
461 TRACE("Destination texture %p is onscreen.\n", dst_texture);
462 buffer = wined3d_texture_get_gl_buffer(dst_texture);
463 d = *dst_rect;
464 wined3d_texture_translate_drawable_coords(dst_texture, context->win_handle, &d);
465 dst_rect = &d;
467 else
469 TRACE("Destination texture %p is offscreen.\n", dst_texture);
470 buffer = GL_COLOR_ATTACHMENT0;
473 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER,
474 &dst_texture->resource, dst_sub_resource_idx, NULL, 0, dst_location);
475 context_set_draw_buffer(context, buffer);
476 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
477 context_invalidate_state(context, STATE_FRAMEBUFFER);
479 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
480 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
481 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
482 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
483 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
485 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
486 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
488 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
489 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, GL_COLOR_BUFFER_BIT, gl_filter);
490 checkGLcall("glBlitFramebuffer()");
492 if (wined3d_settings.strict_draw_ordering || (dst_location == WINED3D_LOCATION_DRAWABLE
493 && dst_texture->swapchain->front_buffer == dst_texture))
494 gl_info->gl_ops.gl.p_glFlush();
496 if (restore_texture)
497 context_restore(context, restore_texture, restore_idx);
500 static BOOL fbo_blitter_supported(enum wined3d_blit_op blit_op, const struct wined3d_gl_info *gl_info,
501 const struct wined3d_resource *src_resource, DWORD src_location,
502 const struct wined3d_resource *dst_resource, DWORD dst_location)
504 const struct wined3d_format *src_format = src_resource->format;
505 const struct wined3d_format *dst_format = dst_resource->format;
507 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
508 return FALSE;
510 /* Source and/or destination need to be on the GL side */
511 if (!(src_resource->access & dst_resource->access & WINED3D_RESOURCE_ACCESS_GPU))
512 return FALSE;
514 switch (blit_op)
516 case WINED3D_BLIT_OP_COLOR_BLIT:
517 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
518 || (src_resource->usage & WINED3DUSAGE_RENDERTARGET)))
519 return FALSE;
520 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
521 || (dst_resource->usage & WINED3DUSAGE_RENDERTARGET)))
522 return FALSE;
523 if ((src_format->id != dst_format->id || dst_location == WINED3D_LOCATION_DRAWABLE)
524 && (!is_identity_fixup(src_format->color_fixup) || !is_identity_fixup(dst_format->color_fixup)))
525 return FALSE;
526 break;
528 case WINED3D_BLIT_OP_DEPTH_BLIT:
529 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
530 return FALSE;
531 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
532 return FALSE;
533 /* Accept pure swizzle fixups for depth formats. In general we
534 * ignore the stencil component (if present) at the moment and the
535 * swizzle is not relevant with just the depth component. */
536 if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup)
537 || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup))
538 return FALSE;
539 break;
541 default:
542 return FALSE;
545 return TRUE;
548 /* This call just downloads data, the caller is responsible for binding the
549 * correct texture. */
550 /* Context activation is done by the caller. */
551 static void texture2d_download_data(struct wined3d_texture *texture, unsigned int sub_resource_idx,
552 const struct wined3d_context *context, DWORD dst_location)
554 const struct wined3d_format *format = texture->resource.format;
555 const struct wined3d_gl_info *gl_info = context->gl_info;
556 struct wined3d_texture_sub_resource *sub_resource;
557 unsigned int dst_row_pitch, dst_slice_pitch;
558 unsigned int src_row_pitch, src_slice_pitch;
559 struct wined3d_bo_address data;
560 BYTE *temporary_mem = NULL;
561 unsigned int level;
562 GLenum target;
563 void *mem;
565 /* Only support read back of converted P8 textures. */
566 if (texture->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT && !format->download)
568 ERR("Trying to read back converted texture %p, %u with format %s.\n",
569 texture, sub_resource_idx, debug_d3dformat(format->id));
570 return;
573 sub_resource = &texture->sub_resources[sub_resource_idx];
574 target = wined3d_texture_get_sub_resource_target(texture, sub_resource_idx);
575 level = sub_resource_idx % texture->level_count;
577 if (target == GL_TEXTURE_2D_ARRAY)
579 if (format->download)
581 FIXME("Reading back converted array texture %p is not supported.\n", texture);
582 return;
585 /* NP2 emulation is not allowed on array textures. */
586 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
587 ERR("Array texture %p uses NP2 emulation.\n", texture);
589 WARN_(d3d_perf)("Downloading all miplevel layers to get the data for a single sub-resource.\n");
591 if (!(temporary_mem = heap_calloc(texture->layer_count, sub_resource->size)))
593 ERR("Out of memory.\n");
594 return;
598 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
600 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
602 if (format->download)
604 FIXME("Reading back converted texture %p with NP2 emulation is not supported.\n", texture);
605 return;
608 wined3d_texture_get_pitch(texture, level, &dst_row_pitch, &dst_slice_pitch);
609 wined3d_format_calculate_pitch(format, texture->resource.device->surface_alignment,
610 wined3d_texture_get_level_pow2_width(texture, level),
611 wined3d_texture_get_level_pow2_height(texture, level),
612 &src_row_pitch, &src_slice_pitch);
613 if (!(temporary_mem = heap_alloc(src_slice_pitch)))
615 ERR("Out of memory.\n");
616 return;
619 if (data.buffer_object)
620 ERR("NP2 emulated texture uses PBO unexpectedly.\n");
621 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
622 ERR("Unexpected compressed format for NP2 emulated texture.\n");
625 if (format->download)
627 struct wined3d_format f;
629 if (data.buffer_object)
630 ERR("Converted texture %p uses PBO unexpectedly.\n", texture);
632 WARN_(d3d_perf)("Downloading converted texture %p, %u with format %s.\n",
633 texture, sub_resource_idx, debug_d3dformat(format->id));
635 f = *format;
636 f.byte_count = format->conv_byte_count;
637 wined3d_texture_get_pitch(texture, level, &dst_row_pitch, &dst_slice_pitch);
638 wined3d_format_calculate_pitch(&f, texture->resource.device->surface_alignment,
639 wined3d_texture_get_level_width(texture, level),
640 wined3d_texture_get_level_height(texture, level),
641 &src_row_pitch, &src_slice_pitch);
643 if (!(temporary_mem = heap_alloc(src_slice_pitch)))
645 ERR("Failed to allocate memory.\n");
646 return;
650 if (temporary_mem)
652 mem = temporary_mem;
654 else if (data.buffer_object)
656 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
657 checkGLcall("glBindBuffer");
658 mem = data.addr;
660 else
662 mem = data.addr;
665 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
667 TRACE("Downloading compressed texture %p, %u, level %u, format %#x, type %#x, data %p.\n",
668 texture, sub_resource_idx, level, format->glFormat, format->glType, mem);
670 GL_EXTCALL(glGetCompressedTexImage(target, level, mem));
671 checkGLcall("glGetCompressedTexImage");
673 else
675 TRACE("Downloading texture %p, %u, level %u, format %#x, type %#x, data %p.\n",
676 texture, sub_resource_idx, level, format->glFormat, format->glType, mem);
678 gl_info->gl_ops.gl.p_glGetTexImage(target, level, format->glFormat, format->glType, mem);
679 checkGLcall("glGetTexImage");
682 if (format->download)
684 format->download(mem, data.addr, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch,
685 wined3d_texture_get_level_width(texture, level),
686 wined3d_texture_get_level_height(texture, level), 1);
688 else if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
690 const BYTE *src_data;
691 unsigned int h, y;
692 BYTE *dst_data;
694 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
695 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
696 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
698 * We're doing this...
700 * instead of boxing the texture :
701 * |<-texture width ->| -->pow2width| /\
702 * |111111111111111111| | |
703 * |222 Texture 222222| boxed empty | texture height
704 * |3333 Data 33333333| | |
705 * |444444444444444444| | \/
706 * ----------------------------------- |
707 * | boxed empty | boxed empty | pow2height
708 * | | | \/
709 * -----------------------------------
712 * we're repacking the data to the expected texture width
714 * |<-texture width ->| -->pow2width| /\
715 * |111111111111111111222222222222222| |
716 * |222333333333333333333444444444444| texture height
717 * |444444 | |
718 * | | \/
719 * | | |
720 * | empty | pow2height
721 * | | \/
722 * -----------------------------------
724 * == is the same as
726 * |<-texture width ->| /\
727 * |111111111111111111|
728 * |222222222222222222|texture height
729 * |333333333333333333|
730 * |444444444444444444| \/
731 * --------------------
733 * This also means that any references to surface memory should work with the data as if it were a
734 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
736 * internally the texture is still stored in a boxed format so any references to textureName will
737 * get a boxed texture with width pow2width and not a texture of width resource.width. */
738 src_data = mem;
739 dst_data = data.addr;
740 TRACE("Repacking the surface data from pitch %u to pitch %u.\n", src_row_pitch, dst_row_pitch);
741 h = wined3d_texture_get_level_height(texture, level);
742 for (y = 0; y < h; ++y)
744 memcpy(dst_data, src_data, dst_row_pitch);
745 src_data += src_row_pitch;
746 dst_data += dst_row_pitch;
749 else if (temporary_mem)
751 unsigned int layer = sub_resource_idx / texture->level_count;
752 void *src_data = temporary_mem + layer * sub_resource->size;
753 if (data.buffer_object)
755 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
756 checkGLcall("glBindBuffer");
757 GL_EXTCALL(glBufferSubData(GL_PIXEL_PACK_BUFFER, 0, sub_resource->size, src_data));
758 checkGLcall("glBufferSubData");
760 else
762 memcpy(data.addr, src_data, sub_resource->size);
766 if (data.buffer_object)
768 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
769 checkGLcall("glBindBuffer");
772 heap_free(temporary_mem);
775 static HRESULT texture2d_upload_from_surface(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
776 unsigned int dst_x, unsigned int dst_y, struct wined3d_texture *src_texture,
777 unsigned int src_sub_resource_idx, const struct wined3d_box *src_box)
779 unsigned int src_row_pitch, src_slice_pitch;
780 unsigned int src_level, dst_level;
781 struct wined3d_context *context;
782 struct wined3d_bo_address data;
783 UINT update_w, update_h;
785 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_x %u, dst_y %u, "
786 "src_texture %p, src_sub_resource_idx %u, src_box %s.\n",
787 dst_texture, dst_sub_resource_idx, dst_x, dst_y,
788 src_texture, src_sub_resource_idx, debug_box(src_box));
790 context = context_acquire(dst_texture->resource.device, NULL, 0);
792 /* Only load the sub-resource for partial updates. For newly allocated
793 * textures the texture wouldn't be the current location, and we'd upload
794 * zeroes just to overwrite them again. */
795 update_w = src_box->right - src_box->left;
796 update_h = src_box->bottom - src_box->top;
797 dst_level = dst_sub_resource_idx % dst_texture->level_count;
798 if (update_w == wined3d_texture_get_level_width(dst_texture, dst_level)
799 && update_h == wined3d_texture_get_level_height(dst_texture, dst_level))
800 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
801 else
802 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
803 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
805 src_level = src_sub_resource_idx % src_texture->level_count;
806 wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &data,
807 src_texture->sub_resources[src_sub_resource_idx].locations);
808 wined3d_texture_get_pitch(src_texture, src_level, &src_row_pitch, &src_slice_pitch);
810 wined3d_texture_upload_data(dst_texture, dst_sub_resource_idx, context, src_texture->resource.format,
811 src_box, wined3d_const_bo_address(&data), src_row_pitch, src_slice_pitch, dst_x, dst_y, 0, FALSE);
813 context_release(context);
815 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
816 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
818 return WINED3D_OK;
821 /* See also float_16_to_32() in wined3d_private.h */
822 static inline unsigned short float_32_to_16(const float *in)
824 int exp = 0;
825 float tmp = fabsf(*in);
826 unsigned int mantissa;
827 unsigned short ret;
829 /* Deal with special numbers */
830 if (*in == 0.0f)
831 return 0x0000;
832 if (isnan(*in))
833 return 0x7c01;
834 if (isinf(*in))
835 return (*in < 0.0f ? 0xfc00 : 0x7c00);
837 if (tmp < (float)(1u << 10))
841 tmp = tmp * 2.0f;
842 exp--;
843 } while (tmp < (float)(1u << 10));
845 else if (tmp >= (float)(1u << 11))
849 tmp /= 2.0f;
850 exp++;
851 } while (tmp >= (float)(1u << 11));
854 mantissa = (unsigned int)tmp;
855 if (tmp - mantissa >= 0.5f)
856 ++mantissa; /* Round to nearest, away from zero. */
858 exp += 10; /* Normalize the mantissa. */
859 exp += 15; /* Exponent is encoded with excess 15. */
861 if (exp > 30) /* too big */
863 ret = 0x7c00; /* INF */
865 else if (exp <= 0)
867 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
868 while (exp <= 0)
870 mantissa = mantissa >> 1;
871 ++exp;
873 ret = mantissa & 0x3ff;
875 else
877 ret = (exp << 10) | (mantissa & 0x3ff);
880 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
881 return ret;
884 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
885 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
887 unsigned short *dst_s;
888 const float *src_f;
889 unsigned int x, y;
891 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
893 for (y = 0; y < h; ++y)
895 src_f = (const float *)(src + y * pitch_in);
896 dst_s = (unsigned short *) (dst + y * pitch_out);
897 for (x = 0; x < w; ++x)
899 dst_s[x] = float_32_to_16(src_f + x);
904 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
905 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
907 static const unsigned char convert_5to8[] =
909 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
910 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
911 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
912 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
914 static const unsigned char convert_6to8[] =
916 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
917 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
918 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
919 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
920 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
921 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
922 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
923 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
925 unsigned int x, y;
927 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
929 for (y = 0; y < h; ++y)
931 const WORD *src_line = (const WORD *)(src + y * pitch_in);
932 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
933 for (x = 0; x < w; ++x)
935 WORD pixel = src_line[x];
936 dst_line[x] = 0xff000000u
937 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
938 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
939 | convert_5to8[(pixel & 0x001fu)];
944 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
945 * in both cases we're just setting the X / Alpha channel to 0xff. */
946 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
947 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
949 unsigned int x, y;
951 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
953 for (y = 0; y < h; ++y)
955 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
956 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
958 for (x = 0; x < w; ++x)
960 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
965 static inline BYTE cliptobyte(int x)
967 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
970 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
971 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
973 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
974 unsigned int x, y;
976 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
978 for (y = 0; y < h; ++y)
980 const BYTE *src_line = src + y * pitch_in;
981 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
982 for (x = 0; x < w; ++x)
984 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
985 * C = Y - 16; D = U - 128; E = V - 128;
986 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
987 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
988 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
989 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
990 * U and V are shared between the pixels. */
991 if (!(x & 1)) /* For every even pixel, read new U and V. */
993 d = (int) src_line[1] - 128;
994 e = (int) src_line[3] - 128;
995 r2 = 409 * e + 128;
996 g2 = - 100 * d - 208 * e + 128;
997 b2 = 516 * d + 128;
999 c2 = 298 * ((int) src_line[0] - 16);
1000 dst_line[x] = 0xff000000
1001 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
1002 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
1003 | cliptobyte((c2 + b2) >> 8); /* blue */
1004 /* Scale RGB values to 0..255 range,
1005 * then clip them if still not in range (may be negative),
1006 * then shift them within DWORD if necessary. */
1007 src_line += 2;
1012 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
1013 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1015 unsigned int x, y;
1016 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1018 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
1020 for (y = 0; y < h; ++y)
1022 const BYTE *src_line = src + y * pitch_in;
1023 WORD *dst_line = (WORD *)(dst + y * pitch_out);
1024 for (x = 0; x < w; ++x)
1026 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1027 * C = Y - 16; D = U - 128; E = V - 128;
1028 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1029 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1030 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1031 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1032 * U and V are shared between the pixels. */
1033 if (!(x & 1)) /* For every even pixel, read new U and V. */
1035 d = (int) src_line[1] - 128;
1036 e = (int) src_line[3] - 128;
1037 r2 = 409 * e + 128;
1038 g2 = - 100 * d - 208 * e + 128;
1039 b2 = 516 * d + 128;
1041 c2 = 298 * ((int) src_line[0] - 16);
1042 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
1043 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
1044 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
1045 /* Scale RGB values to 0..255 range,
1046 * then clip them if still not in range (may be negative),
1047 * then shift them within DWORD if necessary. */
1048 src_line += 2;
1053 struct d3dfmt_converter_desc
1055 enum wined3d_format_id from, to;
1056 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
1059 static const struct d3dfmt_converter_desc converters[] =
1061 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
1062 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
1063 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1064 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1065 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
1066 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
1069 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
1070 enum wined3d_format_id to)
1072 unsigned int i;
1074 for (i = 0; i < ARRAY_SIZE(converters); ++i)
1076 if (converters[i].from == from && converters[i].to == to)
1077 return &converters[i];
1080 return NULL;
1083 static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture,
1084 unsigned int sub_resource_idx, const struct wined3d_format *dst_format)
1086 unsigned int texture_level = sub_resource_idx % src_texture->level_count;
1087 const struct wined3d_format *src_format = src_texture->resource.format;
1088 struct wined3d_device *device = src_texture->resource.device;
1089 const struct d3dfmt_converter_desc *conv = NULL;
1090 unsigned int src_row_pitch, src_slice_pitch;
1091 struct wined3d_context *context = NULL;
1092 struct wined3d_texture *dst_texture;
1093 struct wined3d_bo_address src_data;
1094 struct wined3d_resource_desc desc;
1095 DWORD map_binding;
1097 if (!(conv = find_converter(src_format->id, dst_format->id)) && (!device->d3d_initialized
1098 || !is_identity_fixup(src_format->color_fixup) || src_format->conv_byte_count
1099 || !is_identity_fixup(dst_format->color_fixup) || dst_format->conv_byte_count
1100 || (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)))
1102 FIXME("Cannot find a conversion function from format %s to %s.\n",
1103 debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
1104 return NULL;
1107 /* FIXME: Multisampled conversion? */
1108 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
1109 desc.format = dst_format->id;
1110 desc.multisample_type = WINED3D_MULTISAMPLE_NONE;
1111 desc.multisample_quality = 0;
1112 desc.usage = WINED3DUSAGE_SCRATCH | WINED3DUSAGE_PRIVATE;
1113 desc.access = WINED3D_RESOURCE_ACCESS_CPU | WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W;
1114 desc.width = wined3d_texture_get_level_width(src_texture, texture_level);
1115 desc.height = wined3d_texture_get_level_height(src_texture, texture_level);
1116 desc.depth = 1;
1117 desc.size = 0;
1118 if (FAILED(wined3d_texture_create(device, &desc, 1, 1,
1119 WINED3D_TEXTURE_CREATE_MAPPABLE | WINED3D_TEXTURE_CREATE_DISCARD,
1120 NULL, NULL, &wined3d_null_parent_ops, &dst_texture)))
1122 ERR("Failed to create a destination texture for conversion.\n");
1123 return NULL;
1126 if (device->d3d_initialized)
1127 context = context_acquire(device, NULL, 0);
1129 map_binding = src_texture->resource.map_binding;
1130 if (!wined3d_texture_load_location(src_texture, sub_resource_idx, context, map_binding))
1131 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
1132 wined3d_texture_get_pitch(src_texture, texture_level, &src_row_pitch, &src_slice_pitch);
1133 wined3d_texture_get_memory(src_texture, sub_resource_idx, &src_data, map_binding);
1135 if (conv)
1137 unsigned int dst_row_pitch, dst_slice_pitch;
1138 struct wined3d_bo_address dst_data;
1139 const BYTE *src;
1140 BYTE *dst;
1142 map_binding = dst_texture->resource.map_binding;
1143 if (!wined3d_texture_load_location(dst_texture, 0, context, map_binding))
1144 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
1145 wined3d_texture_get_pitch(dst_texture, 0, &dst_row_pitch, &dst_slice_pitch);
1146 wined3d_texture_get_memory(dst_texture, 0, &dst_data, map_binding);
1148 src = context_map_bo_address(context, &src_data,
1149 src_texture->sub_resources[sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ);
1150 dst = context_map_bo_address(context,
1151 &dst_data, dst_texture->sub_resources[0].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_WRITE);
1153 conv->convert(src, dst, src_row_pitch, dst_row_pitch, desc.width, desc.height);
1155 wined3d_texture_invalidate_location(dst_texture, 0, ~map_binding);
1156 context_unmap_bo_address(context, &dst_data, GL_PIXEL_UNPACK_BUFFER);
1157 context_unmap_bo_address(context, &src_data, GL_PIXEL_UNPACK_BUFFER);
1159 else
1161 struct wined3d_box src_box = {0, 0, desc.width, desc.height, 0, 1};
1163 TRACE("Using upload conversion.\n");
1165 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
1166 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
1167 wined3d_texture_upload_data(dst_texture, 0, context, src_format, &src_box,
1168 wined3d_const_bo_address(&src_data), src_row_pitch, src_slice_pitch, 0, 0, 0, FALSE);
1170 wined3d_texture_validate_location(dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB);
1171 wined3d_texture_invalidate_location(dst_texture, 0, ~WINED3D_LOCATION_TEXTURE_RGB);
1174 if (context)
1175 context_release(context);
1177 return dst_texture;
1180 static void texture2d_read_from_framebuffer(struct wined3d_texture *texture, unsigned int sub_resource_idx,
1181 struct wined3d_context *context, DWORD src_location, DWORD dst_location)
1183 struct wined3d_device *device = texture->resource.device;
1184 struct wined3d_texture *restore_texture;
1185 const struct wined3d_gl_info *gl_info;
1186 unsigned int row_pitch, slice_pitch;
1187 unsigned int width, height, level;
1188 struct wined3d_bo_address data;
1189 unsigned int restore_idx;
1190 BYTE *row, *top, *bottom;
1191 BOOL src_is_upside_down;
1192 unsigned int i;
1193 BYTE *mem;
1195 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
1197 restore_texture = context->current_rt.texture;
1198 restore_idx = context->current_rt.sub_resource_idx;
1199 if (restore_texture != texture || restore_idx != sub_resource_idx)
1200 context = context_acquire(device, texture, sub_resource_idx);
1201 else
1202 restore_texture = NULL;
1203 gl_info = context->gl_info;
1205 if (src_location != texture->resource.draw_binding)
1207 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER,
1208 &texture->resource, sub_resource_idx, NULL, 0, src_location);
1209 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1210 context_invalidate_state(context, STATE_FRAMEBUFFER);
1212 else
1214 context_apply_blit_state(context, device);
1217 /* Select the correct read buffer, and give some debug output.
1218 * There is no need to keep track of the current read buffer or reset it,
1219 * every part of the code that reads sets the read buffer as desired.
1221 if (src_location != WINED3D_LOCATION_DRAWABLE || wined3d_resource_is_offscreen(&texture->resource))
1223 /* Mapping the primary render target which is not on a swapchain.
1224 * Read from the back buffer. */
1225 TRACE("Mapping offscreen render target.\n");
1226 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1227 src_is_upside_down = TRUE;
1229 else
1231 /* Onscreen surfaces are always part of a swapchain */
1232 GLenum buffer = wined3d_texture_get_gl_buffer(texture);
1233 TRACE("Mapping %#x buffer.\n", buffer);
1234 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
1235 src_is_upside_down = FALSE;
1237 checkGLcall("glReadBuffer");
1239 if (data.buffer_object)
1241 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1242 checkGLcall("glBindBuffer");
1245 level = sub_resource_idx % texture->level_count;
1246 wined3d_texture_get_pitch(texture, level, &row_pitch, &slice_pitch);
1248 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1249 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / texture->resource.format->byte_count);
1250 checkGLcall("glPixelStorei");
1252 width = wined3d_texture_get_level_width(texture, level);
1253 height = wined3d_texture_get_level_height(texture, level);
1254 gl_info->gl_ops.gl.p_glReadPixels(0, 0, width, height,
1255 texture->resource.format->glFormat,
1256 texture->resource.format->glType, data.addr);
1257 checkGLcall("glReadPixels");
1259 /* Reset previous pixel store pack state */
1260 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
1261 checkGLcall("glPixelStorei");
1263 if (!src_is_upside_down)
1265 /* glReadPixels returns the image upside down, and there is no way to
1266 * prevent this. Flip the lines in software. */
1268 if (!(row = heap_alloc(row_pitch)))
1269 goto error;
1271 if (data.buffer_object)
1273 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
1274 checkGLcall("glMapBuffer");
1276 else
1277 mem = data.addr;
1279 top = mem;
1280 bottom = mem + row_pitch * (height - 1);
1281 for (i = 0; i < height / 2; i++)
1283 memcpy(row, top, row_pitch);
1284 memcpy(top, bottom, row_pitch);
1285 memcpy(bottom, row, row_pitch);
1286 top += row_pitch;
1287 bottom -= row_pitch;
1289 heap_free(row);
1291 if (data.buffer_object)
1292 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
1295 error:
1296 if (data.buffer_object)
1298 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1299 checkGLcall("glBindBuffer");
1302 if (restore_texture)
1303 context_restore(context, restore_texture, restore_idx);
1306 /* Read the framebuffer contents into a texture. Note that this function
1307 * doesn't do any kind of flipping. Using this on an onscreen surface will
1308 * result in a flipped D3D texture.
1310 * Context activation is done by the caller. This function may temporarily
1311 * switch to a different context and restore the original one before return. */
1312 void texture2d_load_fb_texture(struct wined3d_texture *texture,
1313 unsigned int sub_resource_idx, BOOL srgb, struct wined3d_context *context)
1315 struct wined3d_device *device = texture->resource.device;
1316 struct wined3d_texture *restore_texture;
1317 const struct wined3d_gl_info *gl_info;
1318 unsigned int restore_idx, level;
1319 GLenum target;
1321 restore_texture = context->current_rt.texture;
1322 restore_idx = context->current_rt.sub_resource_idx;
1323 if (restore_texture != texture || restore_idx != sub_resource_idx)
1324 context = context_acquire(device, texture, sub_resource_idx);
1325 else
1326 restore_texture = NULL;
1328 gl_info = context->gl_info;
1329 device_invalidate_state(device, STATE_FRAMEBUFFER);
1331 wined3d_texture_prepare_texture(texture, context, srgb);
1332 wined3d_texture_bind_and_dirtify(texture, context, srgb);
1334 TRACE("Reading back offscreen render target %p, %u.\n", texture, sub_resource_idx);
1336 if (wined3d_resource_is_offscreen(&texture->resource))
1337 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1338 else
1339 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(texture));
1340 checkGLcall("glReadBuffer");
1342 level = sub_resource_idx % texture->level_count;
1343 target = wined3d_texture_get_sub_resource_target(texture, sub_resource_idx);
1344 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(target, level, 0, 0, 0, 0,
1345 wined3d_texture_get_level_width(texture, level),
1346 wined3d_texture_get_level_height(texture, level));
1347 checkGLcall("glCopyTexSubImage2D");
1349 if (restore_texture)
1350 context_restore(context, restore_texture, restore_idx);
1353 /* Does a direct frame buffer -> texture copy. Stretching is done with single
1354 * pixel copy calls. */
1355 static void fb_copy_to_texture_direct(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
1356 const RECT *dst_rect_in, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1357 const RECT *src_rect, enum wined3d_texture_filter_type filter)
1359 struct wined3d_device *device = dst_texture->resource.device;
1360 unsigned int src_height, src_level, dst_level;
1361 const struct wined3d_gl_info *gl_info;
1362 float xrel, yrel;
1363 struct wined3d_context *context;
1364 BOOL upsidedown = FALSE;
1365 RECT dst_rect = *dst_rect_in;
1366 GLenum dst_target;
1368 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1369 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1371 if(dst_rect.top > dst_rect.bottom) {
1372 UINT tmp = dst_rect.bottom;
1373 dst_rect.bottom = dst_rect.top;
1374 dst_rect.top = tmp;
1375 upsidedown = TRUE;
1378 context = context_acquire(device, src_texture, src_sub_resource_idx);
1379 gl_info = context->gl_info;
1380 context_apply_blit_state(context, device);
1381 wined3d_texture_load(dst_texture, context, FALSE);
1383 /* Bind the target texture */
1384 context_bind_texture(context, dst_texture->target, dst_texture->texture_rgb.name);
1385 if (wined3d_resource_is_offscreen(&src_texture->resource))
1387 TRACE("Reading from an offscreen target\n");
1388 upsidedown = !upsidedown;
1389 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1391 else
1393 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
1395 checkGLcall("glReadBuffer");
1397 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
1398 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
1400 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1402 FIXME_(d3d_perf)("Doing a pixel by pixel copy from the framebuffer to a texture.\n");
1404 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1405 ERR("Texture filtering not supported in direct blit.\n");
1407 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1408 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1410 ERR("Texture filtering not supported in direct blit\n");
1413 src_level = src_sub_resource_idx % src_texture->level_count;
1414 dst_level = dst_sub_resource_idx % dst_texture->level_count;
1416 src_height = wined3d_texture_get_level_height(src_texture, src_level);
1417 dst_target = wined3d_texture_get_sub_resource_target(dst_texture, dst_sub_resource_idx);
1418 if (upsidedown
1419 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1420 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1422 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
1423 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_target, dst_level,
1424 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
1425 src_rect->left, src_height - src_rect->bottom,
1426 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1428 else
1430 LONG row;
1431 UINT yoffset = src_height - src_rect->top + dst_rect.top - 1;
1432 /* I have to process this row by row to swap the image,
1433 * otherwise it would be upside down, so stretching in y direction
1434 * doesn't cost extra time
1436 * However, stretching in x direction can be avoided if not necessary
1438 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
1439 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1441 /* Well, that stuff works, but it's very slow.
1442 * find a better way instead
1444 LONG col;
1446 for (col = dst_rect.left; col < dst_rect.right; ++col)
1448 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_target, dst_level,
1449 dst_rect.left + col /* x offset */, row /* y offset */,
1450 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
1453 else
1455 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_target, dst_level,
1456 dst_rect.left /* x offset */, row /* y offset */,
1457 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
1461 checkGLcall("glCopyTexSubImage2D");
1463 context_release(context);
1465 /* The texture is now most up to date - If the surface is a render target
1466 * and has a drawable, this path is never entered. */
1467 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1468 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1471 /* Uses the hardware to stretch and flip the image */
1472 static void fb_copy_to_texture_hwstretch(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
1473 const RECT *dst_rect_in, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1474 const RECT *src_rect, enum wined3d_texture_filter_type filter)
1476 unsigned int src_width, src_height, src_pow2_width, src_pow2_height, src_level;
1477 struct wined3d_device *device = dst_texture->resource.device;
1478 GLenum src_target, dst_target, texture_target;
1479 GLuint src, backup = 0;
1480 float left, right, top, bottom; /* Texture coordinates */
1481 const struct wined3d_gl_info *gl_info;
1482 struct wined3d_context *context;
1483 GLenum drawBuffer = GL_BACK;
1484 GLenum offscreen_buffer;
1485 BOOL noBackBufferBackup;
1486 BOOL src_offscreen;
1487 BOOL upsidedown = FALSE;
1488 RECT dst_rect = *dst_rect_in;
1490 TRACE("Using hwstretch blit\n");
1492 src_target = wined3d_texture_get_sub_resource_target(src_texture, src_sub_resource_idx);
1493 dst_target = wined3d_texture_get_sub_resource_target(dst_texture, dst_sub_resource_idx);
1495 /* Activate the Proper context for reading from the source surface, set it up for blitting */
1496 context = context_acquire(device, src_texture, src_sub_resource_idx);
1497 gl_info = context->gl_info;
1498 context_apply_ffp_blit_state(context, device);
1499 wined3d_texture_load(dst_texture, context, FALSE);
1501 offscreen_buffer = context_get_offscreen_gl_buffer(context);
1502 src_level = src_sub_resource_idx % src_texture->level_count;
1503 src_width = wined3d_texture_get_level_width(src_texture, src_level);
1504 src_height = wined3d_texture_get_level_height(src_texture, src_level);
1505 src_pow2_width = wined3d_texture_get_level_pow2_width(src_texture, src_level);
1506 src_pow2_height = wined3d_texture_get_level_pow2_height(src_texture, src_level);
1508 src_offscreen = wined3d_resource_is_offscreen(&src_texture->resource);
1509 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
1510 if (!noBackBufferBackup && !src_texture->texture_rgb.name)
1512 /* Get it a description */
1513 wined3d_texture_load(src_texture, context, FALSE);
1516 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
1517 * This way we don't have to wait for the 2nd readback to finish to leave this function.
1519 if (context->aux_buffers >= 2)
1521 /* Got more than one aux buffer? Use the 2nd aux buffer */
1522 drawBuffer = GL_AUX1;
1524 else if ((!src_offscreen || offscreen_buffer == GL_BACK) && context->aux_buffers >= 1)
1526 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
1527 drawBuffer = GL_AUX0;
1530 if (noBackBufferBackup)
1532 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
1533 checkGLcall("glGenTextures");
1534 context_bind_texture(context, GL_TEXTURE_2D, backup);
1535 texture_target = GL_TEXTURE_2D;
1537 else
1539 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
1540 * we are reading from the back buffer, the backup can be used as source texture
1542 texture_target = src_target;
1543 context_bind_texture(context, texture_target, src_texture->texture_rgb.name);
1544 gl_info->gl_ops.gl.p_glEnable(texture_target);
1545 checkGLcall("glEnable(texture_target)");
1547 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
1548 src_texture->sub_resources[src_sub_resource_idx].locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
1551 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1552 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1554 if(dst_rect.top > dst_rect.bottom) {
1555 UINT tmp = dst_rect.bottom;
1556 dst_rect.bottom = dst_rect.top;
1557 dst_rect.top = tmp;
1558 upsidedown = TRUE;
1561 if (src_offscreen)
1563 TRACE("Reading from an offscreen target\n");
1564 upsidedown = !upsidedown;
1565 gl_info->gl_ops.gl.p_glReadBuffer(offscreen_buffer);
1567 else
1569 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
1572 /* TODO: Only back up the part that will be overwritten */
1573 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, src_width, src_height);
1575 checkGLcall("glCopyTexSubImage2D");
1577 /* No issue with overriding these - the sampler is dirty due to blit usage */
1578 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
1579 checkGLcall("glTexParameteri");
1580 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
1581 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
1582 checkGLcall("glTexParameteri");
1584 if (!src_texture->swapchain || src_texture == src_texture->swapchain->back_buffers[0])
1586 src = backup ? backup : src_texture->texture_rgb.name;
1588 else
1590 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
1591 checkGLcall("glReadBuffer(GL_FRONT)");
1593 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
1594 checkGLcall("glGenTextures(1, &src)");
1595 context_bind_texture(context, GL_TEXTURE_2D, src);
1597 /* TODO: Only copy the part that will be read. Use src_rect->left,
1598 * src_rect->bottom as origin, but with the width watch out for power
1599 * of 2 sizes. */
1600 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_pow2_width,
1601 src_pow2_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
1602 checkGLcall("glTexImage2D");
1603 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, src_width, src_height);
1605 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1606 checkGLcall("glTexParameteri");
1607 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1608 checkGLcall("glTexParameteri");
1610 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
1611 checkGLcall("glReadBuffer(GL_BACK)");
1613 if (texture_target != GL_TEXTURE_2D)
1615 gl_info->gl_ops.gl.p_glDisable(texture_target);
1616 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
1617 texture_target = GL_TEXTURE_2D;
1620 checkGLcall("glEnd and previous");
1622 left = src_rect->left;
1623 right = src_rect->right;
1625 if (!upsidedown)
1627 top = src_height - src_rect->top;
1628 bottom = src_height - src_rect->bottom;
1630 else
1632 top = src_height - src_rect->bottom;
1633 bottom = src_height - src_rect->top;
1636 if (src_texture->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
1638 left /= src_pow2_width;
1639 right /= src_pow2_width;
1640 top /= src_pow2_height;
1641 bottom /= src_pow2_height;
1644 /* draw the source texture stretched and upside down. The correct surface is bound already */
1645 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1646 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1648 context_set_draw_buffer(context, drawBuffer);
1649 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
1651 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
1652 /* bottom left */
1653 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
1654 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
1656 /* top left */
1657 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
1658 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
1660 /* top right */
1661 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
1662 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1664 /* bottom right */
1665 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
1666 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
1667 gl_info->gl_ops.gl.p_glEnd();
1668 checkGLcall("glEnd and previous");
1670 if (texture_target != dst_target)
1672 gl_info->gl_ops.gl.p_glDisable(texture_target);
1673 gl_info->gl_ops.gl.p_glEnable(dst_target);
1674 texture_target = dst_target;
1677 /* Now read the stretched and upside down image into the destination texture */
1678 context_bind_texture(context, texture_target, dst_texture->texture_rgb.name);
1679 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
1681 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
1682 0, 0, /* We blitted the image to the origin */
1683 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1684 checkGLcall("glCopyTexSubImage2D");
1686 if (drawBuffer == GL_BACK)
1688 /* Write the back buffer backup back. */
1689 if (backup)
1691 if (texture_target != GL_TEXTURE_2D)
1693 gl_info->gl_ops.gl.p_glDisable(texture_target);
1694 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
1695 texture_target = GL_TEXTURE_2D;
1697 context_bind_texture(context, GL_TEXTURE_2D, backup);
1699 else
1701 if (texture_target != src_target)
1703 gl_info->gl_ops.gl.p_glDisable(texture_target);
1704 gl_info->gl_ops.gl.p_glEnable(src_target);
1705 texture_target = src_target;
1707 context_bind_texture(context, src_target, src_texture->texture_rgb.name);
1710 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
1711 /* top left */
1712 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
1713 gl_info->gl_ops.gl.p_glVertex2i(0, src_height);
1715 /* bottom left */
1716 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)src_height / (float)src_pow2_height);
1717 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
1719 /* bottom right */
1720 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width,
1721 (float)src_height / (float)src_pow2_height);
1722 gl_info->gl_ops.gl.p_glVertex2i(src_width, 0);
1724 /* top right */
1725 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width, 0.0f);
1726 gl_info->gl_ops.gl.p_glVertex2i(src_width, src_height);
1727 gl_info->gl_ops.gl.p_glEnd();
1729 gl_info->gl_ops.gl.p_glDisable(texture_target);
1730 checkGLcall("glDisable(texture_target)");
1732 /* Cleanup */
1733 if (src != src_texture->texture_rgb.name && src != backup)
1735 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
1736 checkGLcall("glDeleteTextures(1, &src)");
1738 if (backup)
1740 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
1741 checkGLcall("glDeleteTextures(1, &backup)");
1744 if (wined3d_settings.strict_draw_ordering)
1745 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
1747 context_release(context);
1749 /* The texture is now most up to date - If the surface is a render target
1750 * and has a drawable, this path is never entered. */
1751 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1752 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1755 static HRESULT wined3d_texture_blt_special(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
1756 const RECT *dst_rect, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1757 const RECT *src_rect, DWORD flags, const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
1759 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1760 const struct wined3d_rendertarget_view *rtv;
1762 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_rect %s, src_texture %p, "
1763 "src_sub_resource_idx %u, src_rect %s, flags %#x, fx %p, filter %s.\n",
1764 dst_texture, dst_sub_resource_idx, wine_dbgstr_rect(dst_rect), src_texture, src_sub_resource_idx,
1765 wine_dbgstr_rect(src_rect), flags, fx, debug_d3dtexturefiltertype(filter));
1767 /* Get the swapchain. One of the surfaces has to be a primary surface. */
1768 if (!(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1770 WARN("Destination resource is not GPU accessible, rejecting GL blit.\n");
1771 return WINED3DERR_INVALIDCALL;
1774 if (!(src_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1776 WARN("Source resource is not GPU accessible, rejecting GL blit.\n");
1777 return WINED3DERR_INVALIDCALL;
1780 src_swapchain = src_texture->swapchain;
1781 dst_swapchain = dst_texture->swapchain;
1783 /* Early sort out of cases where no render target is used */
1784 if (!(rtv = dst_texture->resource.device->fb.render_targets[0]) || (!src_swapchain && !dst_swapchain
1785 && (&src_texture->resource != rtv->resource || src_sub_resource_idx != rtv->sub_resource_idx)
1786 && (&dst_texture->resource != rtv->resource || dst_sub_resource_idx != rtv->sub_resource_idx)))
1788 TRACE("No surface is render target, not using hardware blit.\n");
1789 return WINED3DERR_INVALIDCALL;
1792 /* No destination color keying supported */
1793 if (flags & (WINED3D_BLT_DST_CKEY | WINED3D_BLT_DST_CKEY_OVERRIDE))
1795 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
1796 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
1797 return WINED3DERR_INVALIDCALL;
1800 if (dst_swapchain && dst_swapchain == src_swapchain)
1802 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
1803 return WINED3DERR_INVALIDCALL;
1806 if (dst_swapchain && src_swapchain)
1808 FIXME("Implement hardware blit between two different swapchains\n");
1809 return WINED3DERR_INVALIDCALL;
1812 if (dst_swapchain)
1814 /* Handled with regular texture -> swapchain blit */
1815 if (&src_texture->resource == rtv->resource && src_sub_resource_idx == rtv->sub_resource_idx)
1816 TRACE("Blit from active render target to a swapchain\n");
1818 else if (src_swapchain && &dst_texture->resource == rtv->resource
1819 && dst_sub_resource_idx == rtv->sub_resource_idx)
1821 FIXME("Implement blit from a swapchain to the active render target\n");
1822 return WINED3DERR_INVALIDCALL;
1825 if (!dst_swapchain && (src_swapchain || (&src_texture->resource == rtv->resource
1826 && src_sub_resource_idx == rtv->sub_resource_idx)))
1828 unsigned int src_level, src_width, src_height;
1829 /* Blit from render target to texture */
1830 BOOL stretchx;
1832 /* P8 read back is not implemented */
1833 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT
1834 || dst_texture->resource.format->id == WINED3DFMT_P8_UINT)
1836 TRACE("P8 read back not supported by frame buffer to texture blit\n");
1837 return WINED3DERR_INVALIDCALL;
1840 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_SRC_CKEY_OVERRIDE))
1842 TRACE("Color keying not supported by frame buffer to texture blit\n");
1843 return WINED3DERR_INVALIDCALL;
1844 /* Destination color key is checked above */
1847 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
1848 stretchx = TRUE;
1849 else
1850 stretchx = FALSE;
1852 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
1853 * flip the image nor scale it.
1855 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
1856 * -> If the app wants an image width an unscaled width, copy it line per line
1857 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
1858 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
1859 * back buffer. This is slower than reading line per line, thus not used for flipping
1860 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
1861 * pixel by pixel. */
1862 src_level = src_sub_resource_idx % src_texture->level_count;
1863 src_width = wined3d_texture_get_level_width(src_texture, src_level);
1864 src_height = wined3d_texture_get_level_height(src_texture, src_level);
1865 if (!stretchx || dst_rect->right - dst_rect->left > src_width
1866 || dst_rect->bottom - dst_rect->top > src_height)
1868 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
1869 fb_copy_to_texture_direct(dst_texture, dst_sub_resource_idx, dst_rect,
1870 src_texture, src_sub_resource_idx, src_rect, filter);
1872 else
1874 TRACE("Using hardware stretching to flip / stretch the texture.\n");
1875 fb_copy_to_texture_hwstretch(dst_texture, dst_sub_resource_idx, dst_rect,
1876 src_texture, src_sub_resource_idx, src_rect, filter);
1879 return WINED3D_OK;
1882 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
1883 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
1884 return WINED3DERR_INVALIDCALL;
1887 /* Context activation is done by the caller. */
1888 BOOL texture2d_load_sysmem(struct wined3d_texture *texture, unsigned int sub_resource_idx,
1889 struct wined3d_context *context, DWORD dst_location)
1891 struct wined3d_texture_sub_resource *sub_resource;
1893 sub_resource = &texture->sub_resources[sub_resource_idx];
1894 wined3d_texture_prepare_location(texture, sub_resource_idx, context, dst_location);
1896 /* We cannot download data from multisample textures directly. */
1897 if (is_multisample_location(texture, WINED3D_LOCATION_TEXTURE_RGB))
1899 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_RB_RESOLVED);
1900 texture2d_read_from_framebuffer(texture, sub_resource_idx, context,
1901 WINED3D_LOCATION_RB_RESOLVED, dst_location);
1902 return TRUE;
1904 else
1906 if (sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
1907 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
1909 /* Download the sub-resource to system memory. */
1910 if (sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
1912 wined3d_texture_bind_and_dirtify(texture, context,
1913 !(sub_resource->locations & WINED3D_LOCATION_TEXTURE_RGB));
1914 texture2d_download_data(texture, sub_resource_idx, context, dst_location);
1915 ++texture->download_count;
1917 return TRUE;
1921 if (!(texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
1922 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
1924 texture2d_read_from_framebuffer(texture, sub_resource_idx, context,
1925 texture->resource.draw_binding, dst_location);
1926 return TRUE;
1929 FIXME("Can't load texture %p, %u with location flags %s into sysmem.\n",
1930 texture, sub_resource_idx, wined3d_debug_location(sub_resource->locations));
1931 return FALSE;
1934 /* Context activation is done by the caller. */
1935 BOOL texture2d_load_drawable(struct wined3d_texture *texture,
1936 unsigned int sub_resource_idx, struct wined3d_context *context)
1938 struct wined3d_texture *restore_texture;
1939 struct wined3d_device *device;
1940 unsigned int restore_idx;
1941 unsigned int level;
1942 RECT r;
1944 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
1946 DWORD current = texture->sub_resources[sub_resource_idx].locations;
1947 FIXME("Unimplemented copy from %s for depth/stencil buffers.\n",
1948 wined3d_debug_location(current));
1949 return FALSE;
1952 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
1953 && wined3d_resource_is_offscreen(&texture->resource))
1955 ERR("Trying to load offscreen texture into WINED3D_LOCATION_DRAWABLE.\n");
1956 return FALSE;
1959 device = texture->resource.device;
1960 restore_texture = context->current_rt.texture;
1961 restore_idx = context->current_rt.sub_resource_idx;
1962 if (restore_texture != texture || restore_idx != sub_resource_idx)
1963 context = context_acquire(device, texture, sub_resource_idx);
1964 else
1965 restore_texture = NULL;
1967 level = sub_resource_idx % texture->level_count;
1968 SetRect(&r, 0, 0, wined3d_texture_get_level_width(texture, level),
1969 wined3d_texture_get_level_height(texture, level));
1970 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
1971 device->blitter->ops->blitter_blit(device->blitter, WINED3D_BLIT_OP_COLOR_BLIT, context,
1972 texture, sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB, &r,
1973 texture, sub_resource_idx, WINED3D_LOCATION_DRAWABLE, &r,
1974 NULL, WINED3D_TEXF_POINT);
1976 if (restore_texture)
1977 context_restore(context, restore_texture, restore_idx);
1979 return TRUE;
1982 BOOL texture2d_load_texture(struct wined3d_texture *texture, unsigned int sub_resource_idx,
1983 struct wined3d_context *context, BOOL srgb)
1985 unsigned int width, height, level, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch;
1986 const struct wined3d_gl_info *gl_info = context->gl_info;
1987 struct wined3d_device *device = texture->resource.device;
1988 const struct wined3d_color_key_conversion *conversion;
1989 struct wined3d_texture_sub_resource *sub_resource;
1990 struct wined3d_bo_address data;
1991 BYTE *src_mem, *dst_mem = NULL;
1992 struct wined3d_format format;
1993 struct wined3d_box src_box;
1994 BOOL depth;
1996 depth = texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL;
1997 sub_resource = &texture->sub_resources[sub_resource_idx];
1999 if (!depth && wined3d_settings.offscreen_rendering_mode != ORM_FBO
2000 && wined3d_resource_is_offscreen(&texture->resource)
2001 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
2003 texture2d_load_fb_texture(texture, sub_resource_idx, srgb, context);
2005 return TRUE;
2008 level = sub_resource_idx % texture->level_count;
2009 width = wined3d_texture_get_level_width(texture, level);
2010 height = wined3d_texture_get_level_height(texture, level);
2011 wined3d_box_set(&src_box, 0, 0, width, height, 0, 1);
2013 if (!depth && sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
2014 && (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
2015 && fbo_blitter_supported(WINED3D_BLIT_OP_COLOR_BLIT, gl_info,
2016 &texture->resource, WINED3D_LOCATION_TEXTURE_RGB,
2017 &texture->resource, WINED3D_LOCATION_TEXTURE_SRGB))
2019 RECT src_rect;
2021 SetRect(&src_rect, 0, 0, width, height);
2022 if (srgb)
2023 texture2d_blt_fbo(device, context, WINED3D_TEXF_POINT,
2024 texture, sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB, &src_rect,
2025 texture, sub_resource_idx, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
2026 else
2027 texture2d_blt_fbo(device, context, WINED3D_TEXF_POINT,
2028 texture, sub_resource_idx, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect,
2029 texture, sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
2031 return TRUE;
2034 if (!depth && sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
2035 && (!srgb || (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)))
2037 DWORD src_location = sub_resource->locations & WINED3D_LOCATION_RB_RESOLVED ?
2038 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
2039 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2040 RECT src_rect;
2042 SetRect(&src_rect, 0, 0, width, height);
2043 if (fbo_blitter_supported(WINED3D_BLIT_OP_COLOR_BLIT, gl_info,
2044 &texture->resource, src_location, &texture->resource, dst_location))
2045 texture2d_blt_fbo(device, context, WINED3D_TEXF_POINT, texture, sub_resource_idx,
2046 src_location, &src_rect, texture, sub_resource_idx, dst_location, &src_rect);
2048 return TRUE;
2051 /* Upload from system memory */
2053 if (srgb)
2055 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | texture->resource.map_binding))
2056 == WINED3D_LOCATION_TEXTURE_RGB)
2058 FIXME_(d3d_perf)("Downloading RGB texture %p, %u to reload it as sRGB.\n", texture, sub_resource_idx);
2059 wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding);
2062 else
2064 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | texture->resource.map_binding))
2065 == WINED3D_LOCATION_TEXTURE_SRGB)
2067 FIXME_(d3d_perf)("Downloading sRGB texture %p, %u to reload it as RGB.\n", texture, sub_resource_idx);
2068 wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding);
2072 if (!(sub_resource->locations & surface_simple_locations))
2074 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
2075 /* Lets hope we get it from somewhere... */
2076 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM);
2079 wined3d_texture_prepare_texture(texture, context, srgb);
2080 wined3d_texture_bind_and_dirtify(texture, context, srgb);
2081 wined3d_texture_get_pitch(texture, level, &src_row_pitch, &src_slice_pitch);
2083 format = *texture->resource.format;
2084 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
2085 format = *wined3d_get_format(gl_info, conversion->dst_format, texture->resource.usage);
2087 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
2088 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
2089 * getting called. */
2090 if ((format.conv_byte_count || conversion) && texture->sub_resources[sub_resource_idx].buffer_object)
2092 TRACE("Removing the pbo attached to texture %p, %u.\n", texture, sub_resource_idx);
2094 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM);
2095 wined3d_texture_set_map_binding(texture, WINED3D_LOCATION_SYSMEM);
2098 wined3d_texture_get_memory(texture, sub_resource_idx, &data, sub_resource->locations);
2099 if (format.conv_byte_count)
2101 /* This code is entered for texture formats which need a fixup. */
2102 format.byte_count = format.conv_byte_count;
2103 wined3d_format_calculate_pitch(&format, 1, width, height, &dst_row_pitch, &dst_slice_pitch);
2105 src_mem = context_map_bo_address(context, &data, src_slice_pitch,
2106 GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ);
2107 if (!(dst_mem = heap_alloc(dst_slice_pitch)))
2109 ERR("Out of memory (%u).\n", dst_slice_pitch);
2110 context_release(context);
2111 return FALSE;
2113 format.upload(src_mem, dst_mem, src_row_pitch, src_slice_pitch,
2114 dst_row_pitch, dst_slice_pitch, width, height, 1);
2115 src_row_pitch = dst_row_pitch;
2116 src_slice_pitch = dst_slice_pitch;
2117 context_unmap_bo_address(context, &data, GL_PIXEL_UNPACK_BUFFER);
2119 data.buffer_object = 0;
2120 data.addr = dst_mem;
2122 else if (conversion)
2124 /* This code is only entered for color keying fixups */
2125 struct wined3d_palette *palette = NULL;
2127 wined3d_format_calculate_pitch(&format, device->surface_alignment,
2128 width, height, &dst_row_pitch, &dst_slice_pitch);
2130 src_mem = context_map_bo_address(context, &data, src_slice_pitch,
2131 GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ);
2132 if (!(dst_mem = heap_alloc(dst_slice_pitch)))
2134 ERR("Out of memory (%u).\n", dst_slice_pitch);
2135 context_release(context);
2136 return FALSE;
2138 if (texture->swapchain && texture->swapchain->palette)
2139 palette = texture->swapchain->palette;
2140 conversion->convert(src_mem, src_row_pitch, dst_mem, dst_row_pitch,
2141 width, height, palette, &texture->async.gl_color_key);
2142 src_row_pitch = dst_row_pitch;
2143 src_slice_pitch = dst_slice_pitch;
2144 context_unmap_bo_address(context, &data, GL_PIXEL_UNPACK_BUFFER);
2146 data.buffer_object = 0;
2147 data.addr = dst_mem;
2150 wined3d_texture_upload_data(texture, sub_resource_idx, context, &format, &src_box,
2151 wined3d_const_bo_address(&data), src_row_pitch, src_slice_pitch, 0, 0, 0, srgb);
2153 heap_free(dst_mem);
2155 return TRUE;
2158 /* Context activation is done by the caller. */
2159 BOOL texture2d_load_renderbuffer(struct wined3d_texture *texture, unsigned int sub_resource_idx,
2160 struct wined3d_context *context, DWORD dst_location)
2162 unsigned int level = sub_resource_idx % texture->level_count;
2163 const RECT rect = {0, 0,
2164 wined3d_texture_get_level_width(texture, level),
2165 wined3d_texture_get_level_height(texture, level)};
2166 struct wined3d_texture_sub_resource *sub_resource;
2167 DWORD src_location, locations;
2169 sub_resource = &texture->sub_resources[sub_resource_idx];
2170 locations = sub_resource->locations;
2171 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2173 FIXME("Unimplemented copy from %s for depth/stencil buffers.\n",
2174 wined3d_debug_location(locations));
2175 return FALSE;
2178 if (locations & WINED3D_LOCATION_RB_MULTISAMPLE)
2179 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
2180 else if (locations & WINED3D_LOCATION_RB_RESOLVED)
2181 src_location = WINED3D_LOCATION_RB_RESOLVED;
2182 else if (locations & WINED3D_LOCATION_TEXTURE_SRGB)
2183 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
2184 else if (locations & WINED3D_LOCATION_TEXTURE_RGB)
2185 src_location = WINED3D_LOCATION_TEXTURE_RGB;
2186 else if (locations & WINED3D_LOCATION_DRAWABLE)
2187 src_location = WINED3D_LOCATION_DRAWABLE;
2188 else /* texture2d_blt_fbo() will load the source location if necessary. */
2189 src_location = WINED3D_LOCATION_TEXTURE_RGB;
2191 texture2d_blt_fbo(texture->resource.device, context, WINED3D_TEXF_POINT, texture,
2192 sub_resource_idx, src_location, &rect, texture, sub_resource_idx, dst_location, &rect);
2194 return TRUE;
2197 /* Context activation is done by the caller. */
2198 static void fbo_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2200 struct wined3d_blitter *next;
2202 if ((next = blitter->next))
2203 next->ops->blitter_destroy(next, context);
2205 heap_free(blitter);
2208 static void fbo_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
2209 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
2210 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
2212 struct wined3d_blitter *next;
2214 if ((next = blitter->next))
2215 next->ops->blitter_clear(next, device, rt_count, fb, rect_count,
2216 clear_rects, draw_rect, flags, colour, depth, stencil);
2219 static DWORD fbo_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
2220 struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
2221 DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture,
2222 unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect,
2223 const struct wined3d_color_key *colour_key, enum wined3d_texture_filter_type filter)
2225 struct wined3d_resource *src_resource, *dst_resource;
2226 enum wined3d_blit_op blit_op = op;
2227 struct wined3d_device *device;
2228 struct wined3d_blitter *next;
2230 src_resource = &src_texture->resource;
2231 dst_resource = &dst_texture->resource;
2233 device = dst_resource->device;
2235 if (blit_op == WINED3D_BLIT_OP_RAW_BLIT && dst_resource->format->id == src_resource->format->id)
2237 if (dst_resource->format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
2238 blit_op = WINED3D_BLIT_OP_DEPTH_BLIT;
2239 else
2240 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
2243 if (!fbo_blitter_supported(blit_op, context->gl_info,
2244 src_resource, src_location, dst_resource, dst_location))
2246 if ((next = blitter->next))
2247 return next->ops->blitter_blit(next, op, context, src_texture, src_sub_resource_idx, src_location,
2248 src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect, colour_key, filter);
2251 if (blit_op == WINED3D_BLIT_OP_COLOR_BLIT)
2253 TRACE("Colour blit.\n");
2254 texture2d_blt_fbo(device, context, filter, src_texture, src_sub_resource_idx, src_location,
2255 src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect);
2256 return dst_location;
2259 if (blit_op == WINED3D_BLIT_OP_DEPTH_BLIT)
2261 TRACE("Depth/stencil blit.\n");
2262 texture2d_depth_blt_fbo(device, context, src_texture, src_sub_resource_idx, src_location,
2263 src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect);
2264 return dst_location;
2267 ERR("This blitter does not implement blit op %#x.\n", blit_op);
2268 return dst_location;
2271 static const struct wined3d_blitter_ops fbo_blitter_ops =
2273 fbo_blitter_destroy,
2274 fbo_blitter_clear,
2275 fbo_blitter_blit,
2278 void wined3d_fbo_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
2280 struct wined3d_blitter *blitter;
2282 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
2283 return;
2285 if (!(blitter = heap_alloc(sizeof(*blitter))))
2286 return;
2288 TRACE("Created blitter %p.\n", blitter);
2290 blitter->ops = &fbo_blitter_ops;
2291 blitter->next = *next;
2292 *next = blitter;
2295 /* Context activation is done by the caller. */
2296 static void raw_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2298 struct wined3d_blitter *next;
2300 if ((next = blitter->next))
2301 next->ops->blitter_destroy(next, context);
2303 heap_free(blitter);
2306 /* Context activation is done by the caller. */
2307 static void raw_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
2308 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
2309 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
2311 struct wined3d_blitter *next;
2313 if (!(next = blitter->next))
2315 ERR("No blitter to handle clear.\n");
2316 return;
2319 TRACE("Forwarding to blitter %p.\n", next);
2320 next->ops->blitter_clear(next, device, rt_count, fb, rect_count,
2321 clear_rects, draw_rect, flags, colour, depth, stencil);
2324 /* Context activation is done by the caller. */
2325 static DWORD raw_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
2326 struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
2327 DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture,
2328 unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect,
2329 const struct wined3d_color_key *colour_key, enum wined3d_texture_filter_type filter)
2331 const struct wined3d_gl_info *gl_info = context->gl_info;
2332 unsigned int src_level, src_layer, dst_level, dst_layer;
2333 struct wined3d_blitter *next;
2334 GLuint src_name, dst_name;
2335 DWORD location;
2337 /* If we would need to copy from a renderbuffer or drawable, we'd probably
2338 * be better of using the FBO blitter directly, since we'd need to use it
2339 * to copy the resource contents to the texture anyway. */
2340 if (op != WINED3D_BLIT_OP_RAW_BLIT
2341 || (src_texture->resource.format->id == dst_texture->resource.format->id
2342 && (!(src_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
2343 || !(dst_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB)))))
2345 if (!(next = blitter->next))
2347 ERR("No blitter to handle blit op %#x.\n", op);
2348 return dst_location;
2351 TRACE("Forwarding to blitter %p.\n", next);
2352 return next->ops->blitter_blit(next, op, context, src_texture, src_sub_resource_idx, src_location,
2353 src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect, colour_key, filter);
2356 TRACE("Blit using ARB_copy_image.\n");
2358 src_level = src_sub_resource_idx % src_texture->level_count;
2359 src_layer = src_sub_resource_idx / src_texture->level_count;
2361 dst_level = dst_sub_resource_idx % dst_texture->level_count;
2362 dst_layer = dst_sub_resource_idx / dst_texture->level_count;
2364 location = src_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB);
2365 if (!location)
2366 location = src_texture->flags & WINED3D_TEXTURE_IS_SRGB
2367 ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2368 if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, location))
2369 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(location));
2370 src_name = wined3d_texture_get_texture_name(src_texture, context, location == WINED3D_LOCATION_TEXTURE_SRGB);
2372 location = dst_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB);
2373 if (!location)
2374 location = dst_texture->flags & WINED3D_TEXTURE_IS_SRGB
2375 ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2376 if (texture2d_is_full_rect(dst_texture, dst_level, dst_rect))
2378 if (!wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, location))
2379 ERR("Failed to prepare the destination sub-resource into %s.\n", wined3d_debug_location(location));
2381 else
2383 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, location))
2384 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(location));
2386 dst_name = wined3d_texture_get_texture_name(dst_texture, context, location == WINED3D_LOCATION_TEXTURE_SRGB);
2388 GL_EXTCALL(glCopyImageSubData(src_name, src_texture->target, src_level,
2389 src_rect->left, src_rect->top, src_layer, dst_name, dst_texture->target, dst_level,
2390 dst_rect->left, dst_rect->top, dst_layer, src_rect->right - src_rect->left,
2391 src_rect->bottom - src_rect->top, 1));
2392 checkGLcall("copy image data");
2394 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, location);
2395 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~location);
2396 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location))
2397 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(dst_location));
2399 return dst_location | location;
2402 static const struct wined3d_blitter_ops raw_blitter_ops =
2404 raw_blitter_destroy,
2405 raw_blitter_clear,
2406 raw_blitter_blit,
2409 void wined3d_raw_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
2411 struct wined3d_blitter *blitter;
2413 if (!gl_info->supported[ARB_COPY_IMAGE])
2414 return;
2416 if (!(blitter = heap_alloc(sizeof(*blitter))))
2417 return;
2419 TRACE("Created blitter %p.\n", blitter);
2421 blitter->ops = &raw_blitter_ops;
2422 blitter->next = *next;
2423 *next = blitter;
2426 /* Context activation is done by the caller. */
2427 static void ffp_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2429 struct wined3d_blitter *next;
2431 if ((next = blitter->next))
2432 next->ops->blitter_destroy(next, context);
2434 heap_free(blitter);
2437 static BOOL ffp_blit_supported(enum wined3d_blit_op blit_op, const struct wined3d_context *context,
2438 const struct wined3d_resource *src_resource, DWORD src_location,
2439 const struct wined3d_resource *dst_resource, DWORD dst_location)
2441 const struct wined3d_format *src_format = src_resource->format;
2442 const struct wined3d_format *dst_format = dst_resource->format;
2443 BOOL decompress;
2445 decompress = src_format && (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
2446 && !(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED);
2447 if (!decompress && !(src_resource->access & dst_resource->access & WINED3D_RESOURCE_ACCESS_GPU))
2449 TRACE("Source or destination resource is not GPU accessible.\n");
2450 return FALSE;
2453 if (blit_op == WINED3D_BLIT_OP_RAW_BLIT && dst_format->id == src_format->id)
2455 if (dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
2456 blit_op = WINED3D_BLIT_OP_DEPTH_BLIT;
2457 else
2458 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
2461 switch (blit_op)
2463 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
2464 if (context->d3d_info->shader_color_key)
2466 TRACE("Color keying requires converted textures.\n");
2467 return FALSE;
2469 case WINED3D_BLIT_OP_COLOR_BLIT:
2470 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
2471 if (!context->gl_info->supported[WINED3D_GL_LEGACY_CONTEXT])
2472 return FALSE;
2474 if (TRACE_ON(d3d))
2476 TRACE("Checking support for fixup:\n");
2477 dump_color_fixup_desc(src_format->color_fixup);
2480 /* We only support identity conversions. */
2481 if (!is_identity_fixup(src_format->color_fixup)
2482 || !is_identity_fixup(dst_format->color_fixup))
2484 if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER
2485 && dst_format->id == src_format->id && dst_location == WINED3D_LOCATION_DRAWABLE)
2487 WARN("Claiming fixup support because of ORM_BACKBUFFER.\n");
2489 else
2491 TRACE("Fixups are not supported.\n");
2492 return FALSE;
2496 if (!(dst_resource->usage & WINED3DUSAGE_RENDERTARGET))
2498 TRACE("Can only blit to render targets.\n");
2499 return FALSE;
2501 return TRUE;
2503 default:
2504 TRACE("Unsupported blit operation %#x.\n", blit_op);
2505 return FALSE;
2509 static BOOL ffp_blitter_use_cpu_clear(struct wined3d_rendertarget_view *view)
2511 struct wined3d_resource *resource;
2512 struct wined3d_texture *texture;
2513 DWORD locations;
2515 resource = view->resource;
2516 if (resource->type == WINED3D_RTYPE_BUFFER)
2517 return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU);
2519 texture = texture_from_resource(resource);
2520 locations = texture->sub_resources[view->sub_resource_idx].locations;
2521 if (locations & (resource->map_binding | WINED3D_LOCATION_DISCARDED))
2522 return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU)
2523 || (texture->flags & WINED3D_TEXTURE_PIN_SYSMEM);
2525 return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU)
2526 && !(texture->flags & WINED3D_TEXTURE_CONVERTED);
2529 static void ffp_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
2530 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
2531 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
2533 struct wined3d_rendertarget_view *view;
2534 struct wined3d_blitter *next;
2535 DWORD next_flags = 0;
2536 unsigned int i;
2538 if (flags & WINED3DCLEAR_TARGET)
2540 for (i = 0; i < rt_count; ++i)
2542 if (!(view = fb->render_targets[i]))
2543 continue;
2545 if (ffp_blitter_use_cpu_clear(view)
2546 || (!(view->resource->usage & WINED3DUSAGE_RENDERTARGET)
2547 && (wined3d_settings.offscreen_rendering_mode != ORM_FBO
2548 || !(view->format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE))))
2550 next_flags |= WINED3DCLEAR_TARGET;
2551 flags &= ~WINED3DCLEAR_TARGET;
2552 break;
2555 /* FIXME: We should reject colour fills on formats with fixups,
2556 * but this would break P8 colour fills for example. */
2560 if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil)
2561 && (!view->format->depth_size || (flags & WINED3DCLEAR_ZBUFFER))
2562 && (!view->format->stencil_size || (flags & WINED3DCLEAR_STENCIL))
2563 && ffp_blitter_use_cpu_clear(view))
2565 next_flags |= flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL);
2566 flags &= ~(WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL);
2569 if (flags)
2570 device_clear_render_targets(device, rt_count, fb, rect_count,
2571 clear_rects, draw_rect, flags, colour, depth, stencil);
2573 if (next_flags && (next = blitter->next))
2574 next->ops->blitter_clear(next, device, rt_count, fb, rect_count,
2575 clear_rects, draw_rect, next_flags, colour, depth, stencil);
2578 static DWORD ffp_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
2579 struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
2580 DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture,
2581 unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect,
2582 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter)
2584 const struct wined3d_gl_info *gl_info = context->gl_info;
2585 struct wined3d_resource *src_resource, *dst_resource;
2586 struct wined3d_color_key old_blt_key;
2587 struct wined3d_device *device;
2588 struct wined3d_blitter *next;
2589 DWORD old_color_key_flags;
2590 RECT r;
2592 src_resource = &src_texture->resource;
2593 dst_resource = &dst_texture->resource;
2594 device = dst_resource->device;
2596 if (!ffp_blit_supported(op, context, src_resource, src_location, dst_resource, dst_location))
2598 if ((next = blitter->next))
2599 return next->ops->blitter_blit(next, op, context, src_texture, src_sub_resource_idx, src_location,
2600 src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect, color_key, filter);
2603 TRACE("Blt from texture %p, %u to rendertarget %p, %u.\n",
2604 src_texture, src_sub_resource_idx, dst_texture, dst_sub_resource_idx);
2606 old_blt_key = src_texture->async.src_blt_color_key;
2607 old_color_key_flags = src_texture->async.color_key_flags;
2608 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT, color_key);
2610 /* Make sure the surface is up-to-date. This should probably use
2611 * surface_load_location() and worry about the destination surface too,
2612 * unless we're overwriting it completely. */
2613 wined3d_texture_load(src_texture, context, FALSE);
2615 context_apply_ffp_blit_state(context, device);
2617 if (dst_location == WINED3D_LOCATION_DRAWABLE)
2619 r = *dst_rect;
2620 wined3d_texture_translate_drawable_coords(dst_texture, context->win_handle, &r);
2621 dst_rect = &r;
2624 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
2626 GLenum buffer;
2628 if (dst_location == WINED3D_LOCATION_DRAWABLE)
2630 TRACE("Destination texture %p is onscreen.\n", dst_texture);
2631 buffer = wined3d_texture_get_gl_buffer(dst_texture);
2633 else
2635 TRACE("Destination texture %p is offscreen.\n", dst_texture);
2636 buffer = GL_COLOR_ATTACHMENT0;
2638 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER,
2639 dst_resource, dst_sub_resource_idx, NULL, 0, dst_location);
2640 context_set_draw_buffer(context, buffer);
2641 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
2642 context_invalidate_state(context, STATE_FRAMEBUFFER);
2645 gl_info->gl_ops.gl.p_glEnable(src_texture->target);
2646 checkGLcall("glEnable(target)");
2648 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST || color_key)
2650 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
2651 checkGLcall("glEnable(GL_ALPHA_TEST)");
2654 if (color_key)
2656 /* For P8 surfaces, the alpha component contains the palette index.
2657 * Which means that the colorkey is one of the palette entries. In
2658 * other cases pixels that should be masked away have alpha set to 0. */
2659 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT)
2660 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
2661 (float)src_texture->async.src_blt_color_key.color_space_low_value / 255.0f);
2662 else
2663 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
2664 checkGLcall("glAlphaFunc");
2667 draw_textured_quad(src_texture, src_sub_resource_idx, context, src_rect, dst_rect, filter);
2669 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST || color_key)
2671 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2672 checkGLcall("glDisable(GL_ALPHA_TEST)");
2675 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
2676 checkGLcall("glDisable(GL_TEXTURE_2D)");
2677 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
2679 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
2680 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
2682 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
2684 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
2685 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
2688 if (wined3d_settings.strict_draw_ordering
2689 || (dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture))
2690 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2692 /* Restore the color key parameters */
2693 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT,
2694 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
2696 return dst_location;
2699 static const struct wined3d_blitter_ops ffp_blitter_ops =
2701 ffp_blitter_destroy,
2702 ffp_blitter_clear,
2703 ffp_blitter_blit,
2706 void wined3d_ffp_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
2708 struct wined3d_blitter *blitter;
2710 if (!(blitter = heap_alloc(sizeof(*blitter))))
2711 return;
2713 TRACE("Created blitter %p.\n", blitter);
2715 blitter->ops = &ffp_blitter_ops;
2716 blitter->next = *next;
2717 *next = blitter;
2720 /* Context activation is done by the caller. */
2721 static void cpu_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2723 struct wined3d_blitter *next;
2725 if ((next = blitter->next))
2726 next->ops->blitter_destroy(next, context);
2728 heap_free(blitter);
2731 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
2732 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
2733 const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx)
2735 UINT row_block_count;
2736 const BYTE *src_row;
2737 BYTE *dst_row;
2738 UINT x, y;
2740 src_row = src_data;
2741 dst_row = dst_data;
2743 row_block_count = (update_w + format->block_width - 1) / format->block_width;
2745 if (!flags)
2747 for (y = 0; y < update_h; y += format->block_height)
2749 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
2750 src_row += src_pitch;
2751 dst_row += dst_pitch;
2754 return WINED3D_OK;
2757 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
2759 src_row += (((update_h / format->block_height) - 1) * src_pitch);
2761 switch (format->id)
2763 case WINED3DFMT_DXT1:
2764 for (y = 0; y < update_h; y += format->block_height)
2766 struct block
2768 WORD color[2];
2769 BYTE control_row[4];
2772 const struct block *s = (const struct block *)src_row;
2773 struct block *d = (struct block *)dst_row;
2775 for (x = 0; x < row_block_count; ++x)
2777 d[x].color[0] = s[x].color[0];
2778 d[x].color[1] = s[x].color[1];
2779 d[x].control_row[0] = s[x].control_row[3];
2780 d[x].control_row[1] = s[x].control_row[2];
2781 d[x].control_row[2] = s[x].control_row[1];
2782 d[x].control_row[3] = s[x].control_row[0];
2784 src_row -= src_pitch;
2785 dst_row += dst_pitch;
2787 return WINED3D_OK;
2789 case WINED3DFMT_DXT2:
2790 case WINED3DFMT_DXT3:
2791 for (y = 0; y < update_h; y += format->block_height)
2793 struct block
2795 WORD alpha_row[4];
2796 WORD color[2];
2797 BYTE control_row[4];
2800 const struct block *s = (const struct block *)src_row;
2801 struct block *d = (struct block *)dst_row;
2803 for (x = 0; x < row_block_count; ++x)
2805 d[x].alpha_row[0] = s[x].alpha_row[3];
2806 d[x].alpha_row[1] = s[x].alpha_row[2];
2807 d[x].alpha_row[2] = s[x].alpha_row[1];
2808 d[x].alpha_row[3] = s[x].alpha_row[0];
2809 d[x].color[0] = s[x].color[0];
2810 d[x].color[1] = s[x].color[1];
2811 d[x].control_row[0] = s[x].control_row[3];
2812 d[x].control_row[1] = s[x].control_row[2];
2813 d[x].control_row[2] = s[x].control_row[1];
2814 d[x].control_row[3] = s[x].control_row[0];
2816 src_row -= src_pitch;
2817 dst_row += dst_pitch;
2819 return WINED3D_OK;
2821 default:
2822 FIXME("Compressed flip not implemented for format %s.\n",
2823 debug_d3dformat(format->id));
2824 return E_NOTIMPL;
2828 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
2829 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
2831 return E_NOTIMPL;
2834 static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
2835 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
2836 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
2837 enum wined3d_texture_filter_type filter)
2839 unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count;
2840 struct wined3d_device *device = dst_texture->resource.device;
2841 const struct wined3d_format *src_format, *dst_format;
2842 struct wined3d_texture *converted_texture = NULL;
2843 struct wined3d_bo_address src_data, dst_data;
2844 unsigned int src_fmt_flags, dst_fmt_flags;
2845 struct wined3d_map_desc dst_map, src_map;
2846 struct wined3d_context *context = NULL;
2847 unsigned int x, sx, xinc, y, sy, yinc;
2848 unsigned int texture_level;
2849 HRESULT hr = WINED3D_OK;
2850 BOOL same_sub_resource;
2851 DWORD map_binding;
2852 const BYTE *sbase;
2853 const BYTE *sbuf;
2854 BYTE *dbuf;
2856 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
2857 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
2858 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture,
2859 src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
2861 if (device->d3d_initialized)
2862 context = context_acquire(device, NULL, 0);
2864 if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx)
2866 same_sub_resource = TRUE;
2868 map_binding = dst_texture->resource.map_binding;
2869 texture_level = dst_sub_resource_idx % dst_texture->level_count;
2870 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
2871 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
2872 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
2873 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
2874 wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
2875 dst_map.data = context_map_bo_address(context, &dst_data,
2876 dst_texture->sub_resources[dst_sub_resource_idx].size,
2877 GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ | WINED3D_MAP_WRITE);
2879 src_map = dst_map;
2880 src_format = dst_texture->resource.format;
2881 dst_format = src_format;
2882 dst_fmt_flags = dst_texture->resource.format_flags;
2883 src_fmt_flags = dst_fmt_flags;
2885 else
2887 same_sub_resource = FALSE;
2888 dst_format = dst_texture->resource.format;
2889 dst_fmt_flags = dst_texture->resource.format_flags;
2890 if (!(flags & WINED3D_BLT_RAW) && dst_texture->resource.format->id != src_texture->resource.format->id)
2892 if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format)))
2894 FIXME("Cannot convert %s to %s.\n", debug_d3dformat(src_texture->resource.format->id),
2895 debug_d3dformat(dst_texture->resource.format->id));
2896 if (context)
2897 context_release(context);
2898 return WINED3DERR_NOTAVAILABLE;
2900 src_texture = converted_texture;
2901 src_sub_resource_idx = 0;
2903 src_format = src_texture->resource.format;
2904 src_fmt_flags = src_texture->resource.format_flags;
2906 map_binding = src_texture->resource.map_binding;
2907 texture_level = src_sub_resource_idx % src_texture->level_count;
2908 if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, map_binding))
2909 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
2910 wined3d_texture_get_pitch(src_texture, texture_level, &src_map.row_pitch, &src_map.slice_pitch);
2911 wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &src_data, map_binding);
2912 src_map.data = context_map_bo_address(context, &src_data,
2913 src_texture->sub_resources[src_sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ);
2915 map_binding = dst_texture->resource.map_binding;
2916 texture_level = dst_sub_resource_idx % dst_texture->level_count;
2917 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
2918 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
2919 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
2920 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
2921 wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
2922 dst_map.data = context_map_bo_address(context, &dst_data,
2923 dst_texture->sub_resources[dst_sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_WRITE);
2925 flags &= ~WINED3D_BLT_RAW;
2927 bpp = dst_format->byte_count;
2928 src_height = src_box->bottom - src_box->top;
2929 src_width = src_box->right - src_box->left;
2930 dst_height = dst_box->bottom - dst_box->top;
2931 dst_width = dst_box->right - dst_box->left;
2932 row_byte_count = dst_width * bpp;
2934 sbase = (BYTE *)src_map.data
2935 + ((src_box->top / src_format->block_height) * src_map.row_pitch)
2936 + ((src_box->left / src_format->block_width) * src_format->block_byte_count);
2937 dbuf = (BYTE *)dst_map.data
2938 + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch)
2939 + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count);
2941 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
2943 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
2945 if (same_sub_resource)
2947 FIXME("Only plain blits supported on compressed surfaces.\n");
2948 hr = E_NOTIMPL;
2949 goto release;
2952 if (src_height != dst_height || src_width != dst_width)
2954 WARN("Stretching not supported on compressed surfaces.\n");
2955 hr = WINED3DERR_INVALIDCALL;
2956 goto release;
2959 hr = surface_cpu_blt_compressed(sbase, dbuf,
2960 src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height,
2961 src_format, flags, fx);
2962 goto release;
2965 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
2966 && (src_width != dst_width || src_height != dst_height))
2968 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
2969 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
2972 xinc = (src_width << 16) / dst_width;
2973 yinc = (src_height << 16) / dst_height;
2975 if (!flags)
2977 /* No effects, we can cheat here. */
2978 if (dst_width == src_width)
2980 if (dst_height == src_height)
2982 /* No stretching in either direction. This needs to be as fast
2983 * as possible. */
2984 sbuf = sbase;
2986 /* Check for overlapping surfaces. */
2987 if (!same_sub_resource || dst_box->top < src_box->top
2988 || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
2990 /* No overlap, or dst above src, so copy from top downwards. */
2991 for (y = 0; y < dst_height; ++y)
2993 memcpy(dbuf, sbuf, row_byte_count);
2994 sbuf += src_map.row_pitch;
2995 dbuf += dst_map.row_pitch;
2998 else if (dst_box->top > src_box->top)
3000 /* Copy from bottom upwards. */
3001 sbuf += src_map.row_pitch * dst_height;
3002 dbuf += dst_map.row_pitch * dst_height;
3003 for (y = 0; y < dst_height; ++y)
3005 sbuf -= src_map.row_pitch;
3006 dbuf -= dst_map.row_pitch;
3007 memcpy(dbuf, sbuf, row_byte_count);
3010 else
3012 /* Src and dst overlapping on the same line, use memmove. */
3013 for (y = 0; y < dst_height; ++y)
3015 memmove(dbuf, sbuf, row_byte_count);
3016 sbuf += src_map.row_pitch;
3017 dbuf += dst_map.row_pitch;
3021 else
3023 /* Stretching in y direction only. */
3024 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3026 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3027 memcpy(dbuf, sbuf, row_byte_count);
3028 dbuf += dst_map.row_pitch;
3032 else
3034 /* Stretching in X direction. */
3035 unsigned int last_sy = ~0u;
3036 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3038 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3040 if ((sy >> 16) == (last_sy >> 16))
3042 /* This source row is the same as last source row -
3043 * Copy the already stretched row. */
3044 memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count);
3046 else
3048 #define STRETCH_ROW(type) \
3049 do { \
3050 const type *s = (const type *)sbuf; \
3051 type *d = (type *)dbuf; \
3052 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3053 d[x] = s[sx >> 16]; \
3054 } while(0)
3056 switch(bpp)
3058 case 1:
3059 STRETCH_ROW(BYTE);
3060 break;
3061 case 2:
3062 STRETCH_ROW(WORD);
3063 break;
3064 case 4:
3065 STRETCH_ROW(DWORD);
3066 break;
3067 case 3:
3069 const BYTE *s;
3070 BYTE *d = dbuf;
3071 for (x = sx = 0; x < dst_width; x++, sx+= xinc)
3073 DWORD pixel;
3075 s = sbuf + 3 * (sx >> 16);
3076 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3077 d[0] = (pixel ) & 0xff;
3078 d[1] = (pixel >> 8) & 0xff;
3079 d[2] = (pixel >> 16) & 0xff;
3080 d += 3;
3082 break;
3084 default:
3085 FIXME("Stretched blit not implemented for bpp %u.\n", bpp * 8);
3086 hr = WINED3DERR_NOTAVAILABLE;
3087 goto error;
3089 #undef STRETCH_ROW
3091 dbuf += dst_map.row_pitch;
3092 last_sy = sy;
3096 else
3098 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
3099 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
3100 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
3101 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3102 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
3104 /* The color keying flags are checked for correctness in ddraw. */
3105 if (flags & WINED3D_BLT_SRC_CKEY)
3107 keylow = src_texture->async.src_blt_color_key.color_space_low_value;
3108 keyhigh = src_texture->async.src_blt_color_key.color_space_high_value;
3110 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3112 keylow = fx->src_color_key.color_space_low_value;
3113 keyhigh = fx->src_color_key.color_space_high_value;
3116 if (flags & WINED3D_BLT_DST_CKEY)
3118 /* Destination color keys are taken from the source surface! */
3119 destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value;
3120 destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value;
3122 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
3124 destkeylow = fx->dst_color_key.color_space_low_value;
3125 destkeyhigh = fx->dst_color_key.color_space_high_value;
3128 if (bpp == 1)
3130 keymask = 0xff;
3132 else
3134 DWORD masks[3];
3135 get_color_masks(src_format, masks);
3136 keymask = masks[0] | masks[1] | masks[2];
3138 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3139 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
3142 if (flags & WINED3D_BLT_FX)
3144 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
3145 LONG tmpxy;
3146 dTopLeft = dbuf;
3147 dTopRight = dbuf + ((dst_width - 1) * bpp);
3148 dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
3149 dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
3151 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
3153 /* I don't think we need to do anything about this flag. */
3154 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
3156 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
3158 tmp = dTopRight;
3159 dTopRight = dTopLeft;
3160 dTopLeft = tmp;
3161 tmp = dBottomRight;
3162 dBottomRight = dBottomLeft;
3163 dBottomLeft = tmp;
3164 dstxinc = dstxinc * -1;
3166 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
3168 tmp = dTopLeft;
3169 dTopLeft = dBottomLeft;
3170 dBottomLeft = tmp;
3171 tmp = dTopRight;
3172 dTopRight = dBottomRight;
3173 dBottomRight = tmp;
3174 dstyinc = dstyinc * -1;
3176 if (fx->fx & WINEDDBLTFX_NOTEARING)
3178 /* I don't think we need to do anything about this flag. */
3179 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
3181 if (fx->fx & WINEDDBLTFX_ROTATE180)
3183 tmp = dBottomRight;
3184 dBottomRight = dTopLeft;
3185 dTopLeft = tmp;
3186 tmp = dBottomLeft;
3187 dBottomLeft = dTopRight;
3188 dTopRight = tmp;
3189 dstxinc = dstxinc * -1;
3190 dstyinc = dstyinc * -1;
3192 if (fx->fx & WINEDDBLTFX_ROTATE270)
3194 tmp = dTopLeft;
3195 dTopLeft = dBottomLeft;
3196 dBottomLeft = dBottomRight;
3197 dBottomRight = dTopRight;
3198 dTopRight = tmp;
3199 tmpxy = dstxinc;
3200 dstxinc = dstyinc;
3201 dstyinc = tmpxy;
3202 dstxinc = dstxinc * -1;
3204 if (fx->fx & WINEDDBLTFX_ROTATE90)
3206 tmp = dTopLeft;
3207 dTopLeft = dTopRight;
3208 dTopRight = dBottomRight;
3209 dBottomRight = dBottomLeft;
3210 dBottomLeft = tmp;
3211 tmpxy = dstxinc;
3212 dstxinc = dstyinc;
3213 dstyinc = tmpxy;
3214 dstyinc = dstyinc * -1;
3216 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
3218 /* I don't think we need to do anything about this flag. */
3219 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
3221 dbuf = dTopLeft;
3222 flags &= ~(WINED3D_BLT_FX);
3225 #define COPY_COLORKEY_FX(type) \
3226 do { \
3227 const type *s; \
3228 type *d = (type *)dbuf, *dx, tmp; \
3229 for (y = sy = 0; y < dst_height; ++y, sy += yinc) \
3231 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
3232 dx = d; \
3233 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3235 tmp = s[sx >> 16]; \
3236 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
3237 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
3239 dx[0] = tmp; \
3241 dx = (type *)(((BYTE *)dx) + dstxinc); \
3243 d = (type *)(((BYTE *)d) + dstyinc); \
3245 } while(0)
3247 switch (bpp)
3249 case 1:
3250 COPY_COLORKEY_FX(BYTE);
3251 break;
3252 case 2:
3253 COPY_COLORKEY_FX(WORD);
3254 break;
3255 case 4:
3256 COPY_COLORKEY_FX(DWORD);
3257 break;
3258 case 3:
3260 const BYTE *s;
3261 BYTE *d = dbuf, *dx;
3262 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3264 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3265 dx = d;
3266 for (x = sx = 0; x < dst_width; ++x, sx+= xinc)
3268 DWORD pixel, dpixel = 0;
3269 s = sbuf + 3 * (sx>>16);
3270 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3271 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
3272 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
3273 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
3275 dx[0] = (pixel ) & 0xff;
3276 dx[1] = (pixel >> 8) & 0xff;
3277 dx[2] = (pixel >> 16) & 0xff;
3279 dx += dstxinc;
3281 d += dstyinc;
3283 break;
3285 default:
3286 FIXME("%s color-keyed blit not implemented for bpp %u.\n",
3287 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
3288 hr = WINED3DERR_NOTAVAILABLE;
3289 goto error;
3290 #undef COPY_COLORKEY_FX
3294 error:
3295 if (flags)
3296 FIXME(" Unsupported flags %#x.\n", flags);
3298 release:
3299 context_unmap_bo_address(context, &dst_data, GL_PIXEL_UNPACK_BUFFER);
3300 if (!same_sub_resource)
3301 context_unmap_bo_address(context, &src_data, GL_PIXEL_UNPACK_BUFFER);
3302 if (SUCCEEDED(hr) && dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture)
3304 SetRect(&dst_texture->swapchain->front_buffer_update,
3305 dst_box->left, dst_box->top, dst_box->right, dst_box->bottom);
3306 dst_texture->swapchain->swapchain_ops->swapchain_frontbuffer_updated(dst_texture->swapchain);
3308 if (converted_texture)
3309 wined3d_texture_decref(converted_texture);
3310 if (context)
3311 context_release(context);
3313 return hr;
3316 static void surface_cpu_blt_colour_fill(struct wined3d_rendertarget_view *view,
3317 const struct wined3d_box *box, const struct wined3d_color *colour)
3319 struct wined3d_device *device = view->resource->device;
3320 struct wined3d_context *context = NULL;
3321 struct wined3d_texture *texture;
3322 struct wined3d_bo_address data;
3323 unsigned int x, y, w, h, bpp;
3324 struct wined3d_map_desc map;
3325 DWORD map_binding;
3326 BYTE *row;
3327 DWORD c;
3329 TRACE("view %p, box %s, colour %s.\n", view, debug_box(box), debug_color(colour));
3331 if (view->format_flags & WINED3DFMT_FLAG_BLOCKS)
3333 FIXME("Not implemented for format %s.\n", debug_d3dformat(view->format->id));
3334 return;
3337 if (view->format->id != view->resource->format->id)
3338 FIXME("View format %s doesn't match resource format %s.\n",
3339 debug_d3dformat(view->format->id), debug_d3dformat(view->resource->format->id));
3341 if (view->resource->type == WINED3D_RTYPE_BUFFER)
3343 FIXME("Not implemented for buffers.\n");
3344 return;
3347 if (device->d3d_initialized)
3348 context = context_acquire(device, NULL, 0);
3350 c = wined3d_format_convert_from_float(view->format, colour);
3351 bpp = view->format->byte_count;
3352 w = box->right - box->left;
3353 h = box->bottom - box->top;
3355 texture = texture_from_resource(view->resource);
3356 map_binding = texture->resource.map_binding;
3357 if (!wined3d_texture_load_location(texture, view->sub_resource_idx, context, map_binding))
3358 ERR("Failed to load the sub-resource into %s.\n", wined3d_debug_location(map_binding));
3359 wined3d_texture_invalidate_location(texture, view->sub_resource_idx, ~map_binding);
3360 wined3d_texture_get_pitch(texture, view->sub_resource_idx % texture->level_count,
3361 &map.row_pitch, &map.slice_pitch);
3362 wined3d_texture_get_memory(texture, view->sub_resource_idx, &data, map_binding);
3363 map.data = context_map_bo_address(context, &data,
3364 texture->sub_resources[view->sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_WRITE);
3365 map.data = (BYTE *)map.data
3366 + (box->front * map.slice_pitch)
3367 + ((box->top / view->format->block_height) * map.row_pitch)
3368 + ((box->left / view->format->block_width) * view->format->block_byte_count);
3370 switch (bpp)
3372 case 1:
3373 for (x = 0; x < w; ++x)
3375 ((BYTE *)map.data)[x] = c;
3377 break;
3379 case 2:
3380 for (x = 0; x < w; ++x)
3382 ((WORD *)map.data)[x] = c;
3384 break;
3386 case 3:
3388 row = map.data;
3389 for (x = 0; x < w; ++x, row += 3)
3391 row[0] = (c ) & 0xff;
3392 row[1] = (c >> 8) & 0xff;
3393 row[2] = (c >> 16) & 0xff;
3395 break;
3397 case 4:
3398 for (x = 0; x < w; ++x)
3400 ((DWORD *)map.data)[x] = c;
3402 break;
3404 default:
3405 FIXME("Not implemented for bpp %u.\n", bpp);
3406 wined3d_resource_unmap(view->resource, view->sub_resource_idx);
3407 return;
3410 row = map.data;
3411 for (y = 1; y < h; ++y)
3413 row += map.row_pitch;
3414 memcpy(row, map.data, w * bpp);
3417 context_unmap_bo_address(context, &data, GL_PIXEL_UNPACK_BUFFER);
3418 if (context)
3419 context_release(context);
3422 static void cpu_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
3423 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
3424 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
3426 struct wined3d_color c = {depth, 0.0f, 0.0f, 0.0f};
3427 struct wined3d_rendertarget_view *view;
3428 struct wined3d_box box;
3429 unsigned int i, j;
3431 if (!rect_count)
3433 rect_count = 1;
3434 clear_rects = draw_rect;
3437 for (i = 0; i < rect_count; ++i)
3439 box.left = max(clear_rects[i].left, draw_rect->left);
3440 box.top = max(clear_rects[i].top, draw_rect->top);
3441 box.right = min(clear_rects[i].right, draw_rect->right);
3442 box.bottom = min(clear_rects[i].bottom, draw_rect->bottom);
3443 box.front = 0;
3444 box.back = 1;
3446 if (box.left >= box.right || box.top >= box.bottom)
3447 continue;
3449 if (flags & WINED3DCLEAR_TARGET)
3451 for (j = 0; j < rt_count; ++j)
3453 if ((view = fb->render_targets[j]))
3454 surface_cpu_blt_colour_fill(view, &box, colour);
3458 if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil))
3460 if ((view->format->depth_size && !(flags & WINED3DCLEAR_ZBUFFER))
3461 || (view->format->stencil_size && !(flags & WINED3DCLEAR_STENCIL)))
3462 FIXME("Clearing %#x on %s.\n", flags, debug_d3dformat(view->format->id));
3464 surface_cpu_blt_colour_fill(view, &box, &c);
3469 static DWORD cpu_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
3470 struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
3471 DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture,
3472 unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect,
3473 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter)
3475 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
3476 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
3477 struct wined3d_blt_fx fx;
3478 DWORD flags = 0;
3480 memset(&fx, 0, sizeof(fx));
3481 switch (op)
3483 case WINED3D_BLIT_OP_COLOR_BLIT:
3484 case WINED3D_BLIT_OP_DEPTH_BLIT:
3485 case WINED3D_BLIT_OP_RAW_BLIT:
3486 break;
3487 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
3488 flags |= WINED3D_BLT_ALPHA_TEST;
3489 break;
3490 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
3491 flags |= WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_FX;
3492 fx.src_color_key = *color_key;
3493 break;
3494 default:
3495 FIXME("Unhandled op %#x.\n", op);
3496 break;
3499 if (FAILED(surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
3500 src_texture, src_sub_resource_idx, &src_box, flags, &fx, filter)))
3501 ERR("Failed to blit.\n");
3502 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
3504 return dst_location | (dst_texture->sub_resources[dst_sub_resource_idx].locations
3505 & dst_texture->resource.map_binding);
3508 static const struct wined3d_blitter_ops cpu_blitter_ops =
3510 cpu_blitter_destroy,
3511 cpu_blitter_clear,
3512 cpu_blitter_blit,
3515 struct wined3d_blitter *wined3d_cpu_blitter_create(void)
3517 struct wined3d_blitter *blitter;
3519 if (!(blitter = heap_alloc(sizeof(*blitter))))
3520 return NULL;
3522 TRACE("Created blitter %p.\n", blitter);
3524 blitter->ops = &cpu_blitter_ops;
3525 blitter->next = NULL;
3527 return blitter;
3530 HRESULT texture2d_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
3531 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
3532 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
3533 enum wined3d_texture_filter_type filter)
3535 struct wined3d_texture_sub_resource *src_sub_resource, *dst_sub_resource;
3536 struct wined3d_device *device = dst_texture->resource.device;
3537 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3538 const struct wined3d_color_key *colour_key = NULL;
3539 DWORD dst_location, valid_locations;
3540 DWORD src_ds_flags, dst_ds_flags;
3541 struct wined3d_context *context;
3542 enum wined3d_blit_op blit_op;
3543 BOOL scale, convert, resolve;
3544 RECT src_rect, dst_rect;
3546 static const DWORD simple_blit = WINED3D_BLT_SRC_CKEY
3547 | WINED3D_BLT_SRC_CKEY_OVERRIDE
3548 | WINED3D_BLT_ALPHA_TEST
3549 | WINED3D_BLT_RAW;
3551 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
3552 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
3553 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture, src_sub_resource_idx,
3554 debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
3555 TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage));
3557 if (fx)
3559 TRACE("fx %#x.\n", fx->fx);
3560 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
3561 fx->dst_color_key.color_space_low_value,
3562 fx->dst_color_key.color_space_high_value);
3563 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
3564 fx->src_color_key.color_space_low_value,
3565 fx->src_color_key.color_space_high_value);
3568 SetRect(&src_rect, src_box->left, src_box->top, src_box->right, src_box->bottom);
3569 SetRect(&dst_rect, dst_box->left, dst_box->top, dst_box->right, dst_box->bottom);
3571 if (!fx || !(fx->fx))
3572 flags &= ~WINED3D_BLT_FX;
3574 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
3575 if (flags & WINED3D_BLT_DO_NOT_WAIT)
3577 static unsigned int once;
3579 if (!once++)
3580 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
3583 flags &= ~(WINED3D_BLT_SYNCHRONOUS | WINED3D_BLT_DO_NOT_WAIT | WINED3D_BLT_WAIT);
3585 if (!device->d3d_initialized)
3587 WARN("D3D not initialized, using fallback.\n");
3588 goto cpu;
3591 if (flags & ~simple_blit)
3593 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
3594 goto fallback;
3597 src_swapchain = src_texture->swapchain;
3598 dst_swapchain = dst_texture->swapchain;
3600 /* This isn't strictly needed. FBO blits for example could deal with
3601 * cross-swapchain blits by first downloading the source to a texture
3602 * before switching to the destination context. We just have this here to
3603 * not have to deal with the issue, since cross-swapchain blits should be
3604 * rare. */
3605 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
3607 FIXME("Using fallback for cross-swapchain blit.\n");
3608 goto fallback;
3611 scale = src_box->right - src_box->left != dst_box->right - dst_box->left
3612 || src_box->bottom - src_box->top != dst_box->bottom - dst_box->top;
3613 convert = src_texture->resource.format->id != dst_texture->resource.format->id;
3614 resolve = src_texture->resource.multisample_type != dst_texture->resource.multisample_type;
3616 dst_ds_flags = dst_texture->resource.format_flags
3617 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
3618 src_ds_flags = src_texture->resource.format_flags
3619 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
3621 if (src_ds_flags || dst_ds_flags)
3623 TRACE("Depth/stencil blit.\n");
3625 if (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)
3626 dst_location = dst_texture->resource.draw_binding;
3627 else
3628 dst_location = dst_texture->resource.map_binding;
3630 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3631 valid_locations = device->blitter->ops->blitter_blit(device->blitter,
3632 WINED3D_BLIT_OP_DEPTH_BLIT, context,
3633 src_texture, src_sub_resource_idx, src_texture->resource.draw_binding, &src_rect,
3634 dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, NULL, filter);
3635 context_release(context);
3637 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
3638 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
3640 return WINED3D_OK;
3643 TRACE("Colour blit.\n");
3645 dst_sub_resource = &dst_texture->sub_resources[dst_sub_resource_idx];
3646 src_sub_resource = &src_texture->sub_resources[src_sub_resource_idx];
3648 /* In principle this would apply to depth blits as well, but we don't
3649 * implement those in the CPU blitter at the moment. */
3650 if ((dst_sub_resource->locations & dst_texture->resource.map_binding)
3651 && (src_sub_resource->locations & src_texture->resource.map_binding))
3653 if (scale)
3654 TRACE("Not doing sysmem blit because of scaling.\n");
3655 else if (convert)
3656 TRACE("Not doing sysmem blit because of format conversion.\n");
3657 else
3658 goto cpu;
3661 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
3662 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3664 colour_key = &fx->src_color_key;
3665 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
3667 else if (flags & WINED3D_BLT_SRC_CKEY)
3669 colour_key = &src_texture->async.src_blt_color_key;
3670 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
3672 else if (flags & WINED3D_BLT_ALPHA_TEST)
3674 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
3676 else if ((src_sub_resource->locations & surface_simple_locations)
3677 && !(dst_sub_resource->locations & surface_simple_locations))
3679 /* Upload */
3680 if (scale)
3681 TRACE("Not doing upload because of scaling.\n");
3682 else if (convert)
3683 TRACE("Not doing upload because of format conversion.\n");
3684 else if (dst_texture->resource.format->conv_byte_count)
3685 TRACE("Not doing upload because the destination format needs conversion.\n");
3686 else
3688 if (SUCCEEDED(texture2d_upload_from_surface(dst_texture, dst_sub_resource_idx,
3689 dst_box->left, dst_box->top, src_texture, src_sub_resource_idx, src_box)))
3691 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
3693 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3694 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx,
3695 context, dst_texture->resource.draw_binding);
3696 context_release(context);
3698 return WINED3D_OK;
3702 else if (dst_swapchain && dst_swapchain->back_buffers
3703 && dst_texture == dst_swapchain->front_buffer
3704 && src_texture == dst_swapchain->back_buffers[0])
3706 /* Use present for back -> front blits. The idea behind this is that
3707 * present is potentially faster than a blit, in particular when FBO
3708 * blits aren't available. Some ddraw applications like Half-Life and
3709 * Prince of Persia 3D use Blt() from the backbuffer to the
3710 * frontbuffer instead of doing a Flip(). D3d8 and d3d9 applications
3711 * can't blit directly to the frontbuffer. */
3712 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
3714 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
3716 /* Set the swap effect to COPY, we don't want the backbuffer to become
3717 * undefined. */
3718 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
3719 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, 0);
3720 dst_swapchain->desc.swap_effect = swap_effect;
3722 return WINED3D_OK;
3724 else if ((flags & WINED3D_BLT_RAW) || (!scale && !convert && !resolve))
3726 blit_op = WINED3D_BLIT_OP_RAW_BLIT;
3729 if (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)
3730 dst_location = dst_texture->resource.draw_binding;
3731 else
3732 dst_location = dst_texture->resource.map_binding;
3734 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3735 valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context,
3736 src_texture, src_sub_resource_idx, src_texture->resource.draw_binding, &src_rect,
3737 dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, colour_key, filter);
3738 context_release(context);
3740 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
3741 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
3743 return WINED3D_OK;
3745 fallback:
3746 /* Special cases for render targets. */
3747 if (SUCCEEDED(wined3d_texture_blt_special(dst_texture, dst_sub_resource_idx, &dst_rect,
3748 src_texture, src_sub_resource_idx, &src_rect, flags, fx, filter)))
3749 return WINED3D_OK;
3751 cpu:
3752 return surface_cpu_blt(dst_texture, dst_sub_resource_idx, dst_box,
3753 src_texture, src_sub_resource_idx, src_box, flags, fx, filter);