wined3d: Select the correct texture level in draw_textured_quad().
[wine.git] / dlls / wined3d / surface.c
blob6c7d6df00d6c171efe7d44738d5a24cf004b7a9f
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 bind_target;
42 struct wined3d_vec3 texcoords[4];
45 struct float_rect
47 float l;
48 float t;
49 float r;
50 float b;
53 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
55 f->l = ((r->left * 2.0f) / w) - 1.0f;
56 f->t = ((r->top * 2.0f) / h) - 1.0f;
57 f->r = ((r->right * 2.0f) / w) - 1.0f;
58 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
61 static void texture2d_get_blt_info(const struct wined3d_texture *texture,
62 unsigned int sub_resource_idx, const RECT *rect, struct blt_info *info)
64 struct wined3d_vec3 *coords = info->texcoords;
65 struct float_rect f;
66 unsigned int level;
67 GLenum target;
68 GLsizei w, h;
70 level = sub_resource_idx % texture->level_count;
71 w = wined3d_texture_get_level_pow2_width(texture, level);
72 h = wined3d_texture_get_level_pow2_height(texture, level);
73 target = wined3d_texture_get_sub_resource_target(texture, sub_resource_idx);
75 switch (target)
77 default:
78 FIXME("Unsupported texture target %#x.\n", target);
79 /* Fall back to GL_TEXTURE_2D */
80 case GL_TEXTURE_2D:
81 info->bind_target = GL_TEXTURE_2D;
82 coords[0].x = (float)rect->left / w;
83 coords[0].y = (float)rect->top / h;
84 coords[0].z = 0.0f;
86 coords[1].x = (float)rect->right / w;
87 coords[1].y = (float)rect->top / h;
88 coords[1].z = 0.0f;
90 coords[2].x = (float)rect->left / w;
91 coords[2].y = (float)rect->bottom / h;
92 coords[2].z = 0.0f;
94 coords[3].x = (float)rect->right / w;
95 coords[3].y = (float)rect->bottom / h;
96 coords[3].z = 0.0f;
97 break;
99 case GL_TEXTURE_RECTANGLE_ARB:
100 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
101 coords[0].x = rect->left; coords[0].y = rect->top; coords[0].z = 0.0f;
102 coords[1].x = rect->right; coords[1].y = rect->top; coords[1].z = 0.0f;
103 coords[2].x = rect->left; coords[2].y = rect->bottom; coords[2].z = 0.0f;
104 coords[3].x = rect->right; coords[3].y = rect->bottom; coords[3].z = 0.0f;
105 break;
107 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
108 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
109 cube_coords_float(rect, w, h, &f);
111 coords[0].x = 1.0f; coords[0].y = -f.t; coords[0].z = -f.l;
112 coords[1].x = 1.0f; coords[1].y = -f.t; coords[1].z = -f.r;
113 coords[2].x = 1.0f; coords[2].y = -f.b; coords[2].z = -f.l;
114 coords[3].x = 1.0f; coords[3].y = -f.b; coords[3].z = -f.r;
115 break;
117 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
118 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
119 cube_coords_float(rect, w, h, &f);
121 coords[0].x = -1.0f; coords[0].y = -f.t; coords[0].z = f.l;
122 coords[1].x = -1.0f; coords[1].y = -f.t; coords[1].z = f.r;
123 coords[2].x = -1.0f; coords[2].y = -f.b; coords[2].z = f.l;
124 coords[3].x = -1.0f; coords[3].y = -f.b; coords[3].z = f.r;
125 break;
127 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
128 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
129 cube_coords_float(rect, w, h, &f);
131 coords[0].x = f.l; coords[0].y = 1.0f; coords[0].z = f.t;
132 coords[1].x = f.r; coords[1].y = 1.0f; coords[1].z = f.t;
133 coords[2].x = f.l; coords[2].y = 1.0f; coords[2].z = f.b;
134 coords[3].x = f.r; coords[3].y = 1.0f; coords[3].z = f.b;
135 break;
137 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
138 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
139 cube_coords_float(rect, w, h, &f);
141 coords[0].x = f.l; coords[0].y = -1.0f; coords[0].z = -f.t;
142 coords[1].x = f.r; coords[1].y = -1.0f; coords[1].z = -f.t;
143 coords[2].x = f.l; coords[2].y = -1.0f; coords[2].z = -f.b;
144 coords[3].x = f.r; coords[3].y = -1.0f; coords[3].z = -f.b;
145 break;
147 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
148 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
149 cube_coords_float(rect, w, h, &f);
151 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1.0f;
152 coords[1].x = f.r; coords[1].y = -f.t; coords[1].z = 1.0f;
153 coords[2].x = f.l; coords[2].y = -f.b; coords[2].z = 1.0f;
154 coords[3].x = f.r; coords[3].y = -f.b; coords[3].z = 1.0f;
155 break;
157 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
158 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
159 cube_coords_float(rect, w, h, &f);
161 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1.0f;
162 coords[1].x = -f.r; coords[1].y = -f.t; coords[1].z = -1.0f;
163 coords[2].x = -f.l; coords[2].y = -f.b; coords[2].z = -1.0f;
164 coords[3].x = -f.r; coords[3].y = -f.b; coords[3].z = -1.0f;
165 break;
169 /* Context activation is done by the caller. */
170 void draw_textured_quad(struct wined3d_texture *texture, unsigned int sub_resource_idx,
171 struct wined3d_context *context, const RECT *src_rect, const RECT *dst_rect,
172 enum wined3d_texture_filter_type filter)
174 const struct wined3d_gl_info *gl_info = context->gl_info;
175 struct blt_info info;
176 unsigned int level;
178 texture2d_get_blt_info(texture, sub_resource_idx, src_rect, &info);
180 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
181 checkGLcall("glEnable(bind_target)");
183 level = sub_resource_idx % texture->level_count;
184 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
186 /* Filtering for StretchRect */
187 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
188 checkGLcall("glTexParameteri");
189 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
190 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
191 checkGLcall("glTexParameteri");
192 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
193 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
194 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
195 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
196 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_BASE_LEVEL, level);
197 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAX_LEVEL, level);
198 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
199 checkGLcall("glTexEnvi");
201 /* Draw a quad */
202 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
203 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[0].x);
204 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
206 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[1].x);
207 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
209 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[2].x);
210 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
212 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[3].x);
213 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
214 gl_info->gl_ops.gl.p_glEnd();
216 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAX_LEVEL, texture->level_count - 1);
218 /* Unbind the texture */
219 context_bind_texture(context, info.bind_target, 0);
221 /* We changed the filtering settings on the texture. Inform the
222 * container about this to get the filters reset properly next draw. */
223 texture->texture_rgb.sampler_desc.mag_filter = WINED3D_TEXF_POINT;
224 texture->texture_rgb.sampler_desc.min_filter = WINED3D_TEXF_POINT;
225 texture->texture_rgb.sampler_desc.mip_filter = WINED3D_TEXF_NONE;
226 texture->texture_rgb.sampler_desc.srgb_decode = FALSE;
227 texture->texture_rgb.base_level = level;
230 /* Works correctly only for <= 4 bpp formats. */
231 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
233 masks[0] = ((1u << format->red_size) - 1) << format->red_offset;
234 masks[1] = ((1u << format->green_size) - 1) << format->green_offset;
235 masks[2] = ((1u << format->blue_size) - 1) << format->blue_offset;
238 static BOOL texture2d_is_full_rect(const struct wined3d_texture *texture, unsigned int level, const RECT *r)
240 unsigned int t;
242 t = wined3d_texture_get_level_width(texture, level);
243 if ((r->left && r->right) || abs(r->right - r->left) != t)
244 return FALSE;
245 t = wined3d_texture_get_level_height(texture, level);
246 if ((r->top && r->bottom) || abs(r->bottom - r->top) != t)
247 return FALSE;
248 return TRUE;
251 static void texture2d_depth_blt_fbo(const struct wined3d_device *device, struct wined3d_context *context,
252 struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx, DWORD src_location,
253 const RECT *src_rect, struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
254 DWORD dst_location, const RECT *dst_rect)
256 const struct wined3d_gl_info *gl_info = context->gl_info;
257 DWORD src_mask, dst_mask;
258 GLbitfield gl_mask;
260 TRACE("device %p, src_texture %p, src_sub_resource_idx %u, src_location %s, src_rect %s, "
261 "dst_texture %p, dst_sub_resource_idx %u, dst_location %s, dst_rect %s.\n", device,
262 src_texture, src_sub_resource_idx, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect),
263 dst_texture, dst_sub_resource_idx, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
265 src_mask = src_texture->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
266 dst_mask = dst_texture->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
268 if (src_mask != dst_mask)
270 ERR("Incompatible formats %s and %s.\n",
271 debug_d3dformat(src_texture->resource.format->id),
272 debug_d3dformat(dst_texture->resource.format->id));
273 return;
276 if (!src_mask)
278 ERR("Not a depth / stencil format: %s.\n",
279 debug_d3dformat(src_texture->resource.format->id));
280 return;
283 gl_mask = 0;
284 if (src_mask & WINED3DFMT_FLAG_DEPTH)
285 gl_mask |= GL_DEPTH_BUFFER_BIT;
286 if (src_mask & WINED3DFMT_FLAG_STENCIL)
287 gl_mask |= GL_STENCIL_BUFFER_BIT;
289 /* Make sure the locations are up-to-date. Loading the destination
290 * surface isn't required if the entire surface is overwritten. */
291 wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, src_location);
292 if (!texture2d_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, dst_rect))
293 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
294 else
295 wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, dst_location);
297 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, 0,
298 &src_texture->resource, src_sub_resource_idx, src_location);
299 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
301 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, 0,
302 &dst_texture->resource, dst_sub_resource_idx, dst_location);
303 context_set_draw_buffer(context, GL_NONE);
304 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
305 context_invalidate_state(context, STATE_FRAMEBUFFER);
307 if (gl_mask & GL_DEPTH_BUFFER_BIT)
309 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
310 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
312 if (gl_mask & GL_STENCIL_BUFFER_BIT)
314 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
316 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
317 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
319 gl_info->gl_ops.gl.p_glStencilMask(~0U);
320 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
323 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
324 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
326 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
327 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
328 checkGLcall("glBlitFramebuffer()");
331 static BOOL is_multisample_location(const struct wined3d_texture *texture, DWORD location)
333 if (location == WINED3D_LOCATION_RB_MULTISAMPLE)
334 return TRUE;
335 if (location != WINED3D_LOCATION_TEXTURE_RGB && location != WINED3D_LOCATION_TEXTURE_SRGB)
336 return FALSE;
337 return texture->target == GL_TEXTURE_2D_MULTISAMPLE || texture->target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
340 /* Blit between surface locations. Onscreen on different swapchains is not supported.
341 * Depth / stencil is not supported. Context activation is done by the caller. */
342 static void texture2d_blt_fbo(const struct wined3d_device *device, struct wined3d_context *context,
343 enum wined3d_texture_filter_type filter, struct wined3d_texture *src_texture,
344 unsigned int src_sub_resource_idx, DWORD src_location, const RECT *src_rect,
345 struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx, DWORD dst_location,
346 const RECT *dst_rect)
348 struct wined3d_texture *required_texture, *restore_texture;
349 unsigned int required_idx, restore_idx;
350 const struct wined3d_gl_info *gl_info;
351 GLenum gl_filter;
352 GLenum buffer;
353 RECT s, d;
355 TRACE("device %p, context %p, filter %s, src_texture %p, src_sub_resource_idx %u, src_location %s, "
356 "src_rect %s, dst_texture %p, dst_sub_resource_idx %u, dst_location %s, dst_rect %s.\n",
357 device, context, debug_d3dtexturefiltertype(filter), src_texture, src_sub_resource_idx,
358 wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect), dst_texture,
359 dst_sub_resource_idx, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
361 switch (filter)
363 case WINED3D_TEXF_LINEAR:
364 gl_filter = GL_LINEAR;
365 break;
367 default:
368 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
369 case WINED3D_TEXF_NONE:
370 case WINED3D_TEXF_POINT:
371 gl_filter = GL_NEAREST;
372 break;
375 /* Resolve the source surface first if needed. */
376 if (is_multisample_location(src_texture, src_location)
377 && (src_texture->resource.format->id != dst_texture->resource.format->id
378 || abs(src_rect->bottom - src_rect->top) != abs(dst_rect->bottom - dst_rect->top)
379 || abs(src_rect->right - src_rect->left) != abs(dst_rect->right - dst_rect->left)))
380 src_location = WINED3D_LOCATION_RB_RESOLVED;
382 /* Make sure the locations are up-to-date. Loading the destination
383 * surface isn't required if the entire surface is overwritten. (And is
384 * in fact harmful if we're being called by surface_load_location() with
385 * the purpose of loading the destination surface.) */
386 wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, src_location);
387 if (!texture2d_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, dst_rect))
388 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
389 else
390 wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, dst_location);
393 if (src_location == WINED3D_LOCATION_DRAWABLE)
395 required_texture = src_texture;
396 required_idx = src_sub_resource_idx;
398 else if (dst_location == WINED3D_LOCATION_DRAWABLE)
400 required_texture = dst_texture;
401 required_idx = dst_sub_resource_idx;
403 else
405 required_texture = NULL;
406 required_idx = 0;
409 restore_texture = context->current_rt.texture;
410 restore_idx = context->current_rt.sub_resource_idx;
411 if (restore_texture != required_texture || restore_idx != required_idx)
412 context = context_acquire(device, required_texture, required_idx);
413 else
414 restore_texture = NULL;
416 if (!context->valid)
418 context_release(context);
419 WARN("Invalid context, skipping blit.\n");
420 return;
423 gl_info = context->gl_info;
425 if (src_location == WINED3D_LOCATION_DRAWABLE)
427 TRACE("Source texture %p is onscreen.\n", src_texture);
428 buffer = wined3d_texture_get_gl_buffer(src_texture);
429 s = *src_rect;
430 wined3d_texture_translate_drawable_coords(src_texture, context->win_handle, &s);
431 src_rect = &s;
433 else
435 TRACE("Source texture %p is offscreen.\n", src_texture);
436 buffer = GL_COLOR_ATTACHMENT0;
439 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER,
440 &src_texture->resource, src_sub_resource_idx, NULL, 0, src_location);
441 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
442 checkGLcall("glReadBuffer()");
443 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
445 if (dst_location == WINED3D_LOCATION_DRAWABLE)
447 TRACE("Destination texture %p is onscreen.\n", dst_texture);
448 buffer = wined3d_texture_get_gl_buffer(dst_texture);
449 d = *dst_rect;
450 wined3d_texture_translate_drawable_coords(dst_texture, context->win_handle, &d);
451 dst_rect = &d;
453 else
455 TRACE("Destination texture %p is offscreen.\n", dst_texture);
456 buffer = GL_COLOR_ATTACHMENT0;
459 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER,
460 &dst_texture->resource, dst_sub_resource_idx, NULL, 0, dst_location);
461 context_set_draw_buffer(context, buffer);
462 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
463 context_invalidate_state(context, STATE_FRAMEBUFFER);
465 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
466 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
467 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
468 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
469 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
471 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
472 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
474 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
475 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, GL_COLOR_BUFFER_BIT, gl_filter);
476 checkGLcall("glBlitFramebuffer()");
478 if (dst_location == WINED3D_LOCATION_DRAWABLE && dst_texture->swapchain->front_buffer == dst_texture)
479 gl_info->gl_ops.gl.p_glFlush();
481 if (restore_texture)
482 context_restore(context, restore_texture, restore_idx);
485 static BOOL fbo_blitter_supported(enum wined3d_blit_op blit_op, const struct wined3d_gl_info *gl_info,
486 const struct wined3d_resource *src_resource, DWORD src_location,
487 const struct wined3d_resource *dst_resource, DWORD dst_location)
489 const struct wined3d_format *src_format = src_resource->format;
490 const struct wined3d_format *dst_format = dst_resource->format;
492 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
493 return FALSE;
495 /* Source and/or destination need to be on the GL side */
496 if (!(src_resource->access & dst_resource->access & WINED3D_RESOURCE_ACCESS_GPU))
497 return FALSE;
499 switch (blit_op)
501 case WINED3D_BLIT_OP_COLOR_BLIT:
502 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
503 || (src_resource->usage & WINED3DUSAGE_RENDERTARGET)))
504 return FALSE;
505 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
506 || (dst_resource->usage & WINED3DUSAGE_RENDERTARGET)))
507 return FALSE;
508 if ((src_format->id != dst_format->id || dst_location == WINED3D_LOCATION_DRAWABLE)
509 && (!is_identity_fixup(src_format->color_fixup) || !is_identity_fixup(dst_format->color_fixup)))
510 return FALSE;
511 break;
513 case WINED3D_BLIT_OP_DEPTH_BLIT:
514 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
515 return FALSE;
516 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
517 return FALSE;
518 /* Accept pure swizzle fixups for depth formats. In general we
519 * ignore the stencil component (if present) at the moment and the
520 * swizzle is not relevant with just the depth component. */
521 if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup)
522 || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup))
523 return FALSE;
524 break;
526 default:
527 return FALSE;
530 return TRUE;
533 /* This call just downloads data, the caller is responsible for binding the
534 * correct texture. */
535 /* Context activation is done by the caller. */
536 static void texture2d_download_data(struct wined3d_texture *texture, unsigned int sub_resource_idx,
537 const struct wined3d_context *context, DWORD dst_location)
539 const struct wined3d_format *format = texture->resource.format;
540 const struct wined3d_gl_info *gl_info = context->gl_info;
541 struct wined3d_texture_sub_resource *sub_resource;
542 unsigned int dst_row_pitch, dst_slice_pitch;
543 unsigned int src_row_pitch, src_slice_pitch;
544 struct wined3d_bo_address data;
545 BYTE *temporary_mem = NULL;
546 unsigned int level;
547 GLenum target;
548 void *mem;
550 /* Only support read back of converted P8 textures. */
551 if (texture->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT && !format->download)
553 ERR("Trying to read back converted texture %p, %u with format %s.\n",
554 texture, sub_resource_idx, debug_d3dformat(format->id));
555 return;
558 sub_resource = &texture->sub_resources[sub_resource_idx];
559 target = wined3d_texture_get_sub_resource_target(texture, sub_resource_idx);
560 level = sub_resource_idx % texture->level_count;
562 if (target == GL_TEXTURE_2D_ARRAY)
564 if (format->download)
566 FIXME("Reading back converted array texture %p is not supported.\n", texture);
567 return;
570 /* NP2 emulation is not allowed on array textures. */
571 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
572 ERR("Array texture %p uses NP2 emulation.\n", texture);
574 WARN_(d3d_perf)("Downloading all miplevel layers to get the data for a single sub-resource.\n");
576 if (!(temporary_mem = heap_calloc(texture->layer_count, sub_resource->size)))
578 ERR("Out of memory.\n");
579 return;
583 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
585 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
587 if (format->download)
589 FIXME("Reading back converted texture %p with NP2 emulation is not supported.\n", texture);
590 return;
593 wined3d_texture_get_pitch(texture, level, &dst_row_pitch, &dst_slice_pitch);
594 wined3d_format_calculate_pitch(format, texture->resource.device->surface_alignment,
595 wined3d_texture_get_level_pow2_width(texture, level),
596 wined3d_texture_get_level_pow2_height(texture, level),
597 &src_row_pitch, &src_slice_pitch);
598 if (!(temporary_mem = heap_alloc(src_slice_pitch)))
600 ERR("Out of memory.\n");
601 return;
604 if (data.buffer_object)
605 ERR("NP2 emulated texture uses PBO unexpectedly.\n");
606 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
607 ERR("Unexpected compressed format for NP2 emulated texture.\n");
610 if (format->download)
612 struct wined3d_format f;
614 if (data.buffer_object)
615 ERR("Converted texture %p uses PBO unexpectedly.\n", texture);
617 WARN_(d3d_perf)("Downloading converted texture %p, %u with format %s.\n",
618 texture, sub_resource_idx, debug_d3dformat(format->id));
620 f = *format;
621 f.byte_count = format->conv_byte_count;
622 wined3d_texture_get_pitch(texture, level, &dst_row_pitch, &dst_slice_pitch);
623 wined3d_format_calculate_pitch(&f, texture->resource.device->surface_alignment,
624 wined3d_texture_get_level_width(texture, level),
625 wined3d_texture_get_level_height(texture, level),
626 &src_row_pitch, &src_slice_pitch);
628 if (!(temporary_mem = heap_alloc(src_slice_pitch)))
630 ERR("Failed to allocate memory.\n");
631 return;
635 if (temporary_mem)
637 mem = temporary_mem;
639 else if (data.buffer_object)
641 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
642 checkGLcall("glBindBuffer");
643 mem = data.addr;
645 else
647 mem = data.addr;
650 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
652 TRACE("Downloading compressed texture %p, %u, level %u, format %#x, type %#x, data %p.\n",
653 texture, sub_resource_idx, level, format->glFormat, format->glType, mem);
655 GL_EXTCALL(glGetCompressedTexImage(target, level, mem));
656 checkGLcall("glGetCompressedTexImage");
658 else
660 TRACE("Downloading texture %p, %u, level %u, format %#x, type %#x, data %p.\n",
661 texture, sub_resource_idx, level, format->glFormat, format->glType, mem);
663 gl_info->gl_ops.gl.p_glGetTexImage(target, level, format->glFormat, format->glType, mem);
664 checkGLcall("glGetTexImage");
667 if (format->download)
669 format->download(mem, data.addr, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch,
670 wined3d_texture_get_level_width(texture, level),
671 wined3d_texture_get_level_height(texture, level), 1);
673 else if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
675 const BYTE *src_data;
676 unsigned int h, y;
677 BYTE *dst_data;
679 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
680 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
681 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
683 * We're doing this...
685 * instead of boxing the texture :
686 * |<-texture width ->| -->pow2width| /\
687 * |111111111111111111| | |
688 * |222 Texture 222222| boxed empty | texture height
689 * |3333 Data 33333333| | |
690 * |444444444444444444| | \/
691 * ----------------------------------- |
692 * | boxed empty | boxed empty | pow2height
693 * | | | \/
694 * -----------------------------------
697 * we're repacking the data to the expected texture width
699 * |<-texture width ->| -->pow2width| /\
700 * |111111111111111111222222222222222| |
701 * |222333333333333333333444444444444| texture height
702 * |444444 | |
703 * | | \/
704 * | | |
705 * | empty | pow2height
706 * | | \/
707 * -----------------------------------
709 * == is the same as
711 * |<-texture width ->| /\
712 * |111111111111111111|
713 * |222222222222222222|texture height
714 * |333333333333333333|
715 * |444444444444444444| \/
716 * --------------------
718 * This also means that any references to surface memory should work with the data as if it were a
719 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
721 * internally the texture is still stored in a boxed format so any references to textureName will
722 * get a boxed texture with width pow2width and not a texture of width resource.width. */
723 src_data = mem;
724 dst_data = data.addr;
725 TRACE("Repacking the surface data from pitch %u to pitch %u.\n", src_row_pitch, dst_row_pitch);
726 h = wined3d_texture_get_level_height(texture, level);
727 for (y = 0; y < h; ++y)
729 memcpy(dst_data, src_data, dst_row_pitch);
730 src_data += src_row_pitch;
731 dst_data += dst_row_pitch;
734 else if (temporary_mem)
736 unsigned int layer = sub_resource_idx / texture->level_count;
737 void *src_data = temporary_mem + layer * sub_resource->size;
738 if (data.buffer_object)
740 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
741 checkGLcall("glBindBuffer");
742 GL_EXTCALL(glBufferSubData(GL_PIXEL_PACK_BUFFER, 0, sub_resource->size, src_data));
743 checkGLcall("glBufferSubData");
745 else
747 memcpy(data.addr, src_data, sub_resource->size);
751 if (data.buffer_object)
753 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
754 checkGLcall("glBindBuffer");
757 heap_free(temporary_mem);
760 static HRESULT texture2d_upload_from_surface(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
761 unsigned int dst_x, unsigned int dst_y, struct wined3d_texture *src_texture,
762 unsigned int src_sub_resource_idx, const struct wined3d_box *src_box)
764 unsigned int src_row_pitch, src_slice_pitch;
765 unsigned int src_level, dst_level;
766 struct wined3d_context *context;
767 struct wined3d_bo_address data;
768 UINT update_w, update_h;
770 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_x %u, dst_y %u, "
771 "src_texture %p, src_sub_resource_idx %u, src_box %s.\n",
772 dst_texture, dst_sub_resource_idx, dst_x, dst_y,
773 src_texture, src_sub_resource_idx, debug_box(src_box));
775 context = context_acquire(dst_texture->resource.device, NULL, 0);
777 /* Only load the sub-resource for partial updates. For newly allocated
778 * textures the texture wouldn't be the current location, and we'd upload
779 * zeroes just to overwrite them again. */
780 update_w = src_box->right - src_box->left;
781 update_h = src_box->bottom - src_box->top;
782 dst_level = dst_sub_resource_idx % dst_texture->level_count;
783 if (update_w == wined3d_texture_get_level_width(dst_texture, dst_level)
784 && update_h == wined3d_texture_get_level_height(dst_texture, dst_level))
785 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
786 else
787 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
788 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
790 src_level = src_sub_resource_idx % src_texture->level_count;
791 wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &data,
792 src_texture->sub_resources[src_sub_resource_idx].locations);
793 wined3d_texture_get_pitch(src_texture, src_level, &src_row_pitch, &src_slice_pitch);
795 wined3d_texture_upload_data(dst_texture, dst_sub_resource_idx, context, src_texture->resource.format,
796 src_box, wined3d_const_bo_address(&data), src_row_pitch, src_slice_pitch, dst_x, dst_y, 0, FALSE);
798 context_release(context);
800 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
801 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
803 return WINED3D_OK;
806 /* See also float_16_to_32() in wined3d_private.h */
807 static inline unsigned short float_32_to_16(const float *in)
809 int exp = 0;
810 float tmp = fabsf(*in);
811 unsigned int mantissa;
812 unsigned short ret;
814 /* Deal with special numbers */
815 if (*in == 0.0f)
816 return 0x0000;
817 if (isnan(*in))
818 return 0x7c01;
819 if (isinf(*in))
820 return (*in < 0.0f ? 0xfc00 : 0x7c00);
822 if (tmp < (float)(1u << 10))
826 tmp = tmp * 2.0f;
827 exp--;
828 } while (tmp < (float)(1u << 10));
830 else if (tmp >= (float)(1u << 11))
834 tmp /= 2.0f;
835 exp++;
836 } while (tmp >= (float)(1u << 11));
839 mantissa = (unsigned int)tmp;
840 if (tmp - mantissa >= 0.5f)
841 ++mantissa; /* Round to nearest, away from zero. */
843 exp += 10; /* Normalize the mantissa. */
844 exp += 15; /* Exponent is encoded with excess 15. */
846 if (exp > 30) /* too big */
848 ret = 0x7c00; /* INF */
850 else if (exp <= 0)
852 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
853 while (exp <= 0)
855 mantissa = mantissa >> 1;
856 ++exp;
858 ret = mantissa & 0x3ff;
860 else
862 ret = (exp << 10) | (mantissa & 0x3ff);
865 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
866 return ret;
869 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
870 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
872 unsigned short *dst_s;
873 const float *src_f;
874 unsigned int x, y;
876 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
878 for (y = 0; y < h; ++y)
880 src_f = (const float *)(src + y * pitch_in);
881 dst_s = (unsigned short *) (dst + y * pitch_out);
882 for (x = 0; x < w; ++x)
884 dst_s[x] = float_32_to_16(src_f + x);
889 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
890 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
892 static const unsigned char convert_5to8[] =
894 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
895 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
896 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
897 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
899 static const unsigned char convert_6to8[] =
901 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
902 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
903 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
904 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
905 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
906 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
907 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
908 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
910 unsigned int x, y;
912 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
914 for (y = 0; y < h; ++y)
916 const WORD *src_line = (const WORD *)(src + y * pitch_in);
917 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
918 for (x = 0; x < w; ++x)
920 WORD pixel = src_line[x];
921 dst_line[x] = 0xff000000u
922 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
923 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
924 | convert_5to8[(pixel & 0x001fu)];
929 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
930 * in both cases we're just setting the X / Alpha channel to 0xff. */
931 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
932 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
934 unsigned int x, y;
936 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
938 for (y = 0; y < h; ++y)
940 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
941 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
943 for (x = 0; x < w; ++x)
945 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
950 static inline BYTE cliptobyte(int x)
952 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
955 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
956 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
958 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
959 unsigned int x, y;
961 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
963 for (y = 0; y < h; ++y)
965 const BYTE *src_line = src + y * pitch_in;
966 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
967 for (x = 0; x < w; ++x)
969 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
970 * C = Y - 16; D = U - 128; E = V - 128;
971 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
972 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
973 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
974 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
975 * U and V are shared between the pixels. */
976 if (!(x & 1)) /* For every even pixel, read new U and V. */
978 d = (int) src_line[1] - 128;
979 e = (int) src_line[3] - 128;
980 r2 = 409 * e + 128;
981 g2 = - 100 * d - 208 * e + 128;
982 b2 = 516 * d + 128;
984 c2 = 298 * ((int) src_line[0] - 16);
985 dst_line[x] = 0xff000000
986 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
987 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
988 | cliptobyte((c2 + b2) >> 8); /* blue */
989 /* Scale RGB values to 0..255 range,
990 * then clip them if still not in range (may be negative),
991 * then shift them within DWORD if necessary. */
992 src_line += 2;
997 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
998 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1000 unsigned int x, y;
1001 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1003 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
1005 for (y = 0; y < h; ++y)
1007 const BYTE *src_line = src + y * pitch_in;
1008 WORD *dst_line = (WORD *)(dst + y * pitch_out);
1009 for (x = 0; x < w; ++x)
1011 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1012 * C = Y - 16; D = U - 128; E = V - 128;
1013 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1014 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1015 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1016 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1017 * U and V are shared between the pixels. */
1018 if (!(x & 1)) /* For every even pixel, read new U and V. */
1020 d = (int) src_line[1] - 128;
1021 e = (int) src_line[3] - 128;
1022 r2 = 409 * e + 128;
1023 g2 = - 100 * d - 208 * e + 128;
1024 b2 = 516 * d + 128;
1026 c2 = 298 * ((int) src_line[0] - 16);
1027 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
1028 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
1029 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
1030 /* Scale RGB values to 0..255 range,
1031 * then clip them if still not in range (may be negative),
1032 * then shift them within DWORD if necessary. */
1033 src_line += 2;
1038 struct d3dfmt_converter_desc
1040 enum wined3d_format_id from, to;
1041 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
1044 static const struct d3dfmt_converter_desc converters[] =
1046 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
1047 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
1048 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1049 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1050 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
1051 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
1054 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
1055 enum wined3d_format_id to)
1057 unsigned int i;
1059 for (i = 0; i < ARRAY_SIZE(converters); ++i)
1061 if (converters[i].from == from && converters[i].to == to)
1062 return &converters[i];
1065 return NULL;
1068 static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture,
1069 unsigned int sub_resource_idx, const struct wined3d_format *dst_format)
1071 unsigned int texture_level = sub_resource_idx % src_texture->level_count;
1072 const struct wined3d_format *src_format = src_texture->resource.format;
1073 struct wined3d_device *device = src_texture->resource.device;
1074 const struct d3dfmt_converter_desc *conv = NULL;
1075 unsigned int src_row_pitch, src_slice_pitch;
1076 struct wined3d_context *context = NULL;
1077 struct wined3d_texture *dst_texture;
1078 struct wined3d_bo_address src_data;
1079 struct wined3d_resource_desc desc;
1080 DWORD map_binding;
1082 if (!(conv = find_converter(src_format->id, dst_format->id)) && (!device->d3d_initialized
1083 || !is_identity_fixup(src_format->color_fixup) || src_format->conv_byte_count
1084 || !is_identity_fixup(dst_format->color_fixup) || dst_format->conv_byte_count
1085 || (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)))
1087 FIXME("Cannot find a conversion function from format %s to %s.\n",
1088 debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
1089 return NULL;
1092 /* FIXME: Multisampled conversion? */
1093 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
1094 desc.format = dst_format->id;
1095 desc.multisample_type = WINED3D_MULTISAMPLE_NONE;
1096 desc.multisample_quality = 0;
1097 desc.usage = WINED3DUSAGE_SCRATCH | WINED3DUSAGE_PRIVATE;
1098 desc.access = WINED3D_RESOURCE_ACCESS_CPU | WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W;
1099 desc.width = wined3d_texture_get_level_width(src_texture, texture_level);
1100 desc.height = wined3d_texture_get_level_height(src_texture, texture_level);
1101 desc.depth = 1;
1102 desc.size = 0;
1103 if (FAILED(wined3d_texture_create(device, &desc, 1, 1,
1104 WINED3D_TEXTURE_CREATE_MAPPABLE | WINED3D_TEXTURE_CREATE_DISCARD,
1105 NULL, NULL, &wined3d_null_parent_ops, &dst_texture)))
1107 ERR("Failed to create a destination texture for conversion.\n");
1108 return NULL;
1111 if (device->d3d_initialized)
1112 context = context_acquire(device, NULL, 0);
1114 map_binding = src_texture->resource.map_binding;
1115 if (!wined3d_texture_load_location(src_texture, sub_resource_idx, context, map_binding))
1116 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
1117 wined3d_texture_get_pitch(src_texture, texture_level, &src_row_pitch, &src_slice_pitch);
1118 wined3d_texture_get_memory(src_texture, sub_resource_idx, &src_data, map_binding);
1120 if (conv)
1122 unsigned int dst_row_pitch, dst_slice_pitch;
1123 struct wined3d_bo_address dst_data;
1124 const BYTE *src;
1125 BYTE *dst;
1127 map_binding = dst_texture->resource.map_binding;
1128 if (!wined3d_texture_load_location(dst_texture, 0, context, map_binding))
1129 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
1130 wined3d_texture_get_pitch(dst_texture, 0, &dst_row_pitch, &dst_slice_pitch);
1131 wined3d_texture_get_memory(dst_texture, 0, &dst_data, map_binding);
1133 src = context_map_bo_address(context, &src_data,
1134 src_texture->sub_resources[sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ);
1135 dst = context_map_bo_address(context,
1136 &dst_data, dst_texture->sub_resources[0].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_WRITE);
1138 conv->convert(src, dst, src_row_pitch, dst_row_pitch, desc.width, desc.height);
1140 wined3d_texture_invalidate_location(dst_texture, 0, ~map_binding);
1141 context_unmap_bo_address(context, &dst_data, GL_PIXEL_UNPACK_BUFFER);
1142 context_unmap_bo_address(context, &src_data, GL_PIXEL_UNPACK_BUFFER);
1144 else
1146 struct wined3d_box src_box = {0, 0, desc.width, desc.height, 0, 1};
1148 TRACE("Using upload conversion.\n");
1150 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
1151 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
1152 wined3d_texture_upload_data(dst_texture, 0, context, src_format, &src_box,
1153 wined3d_const_bo_address(&src_data), src_row_pitch, src_slice_pitch, 0, 0, 0, FALSE);
1155 wined3d_texture_validate_location(dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB);
1156 wined3d_texture_invalidate_location(dst_texture, 0, ~WINED3D_LOCATION_TEXTURE_RGB);
1159 if (context)
1160 context_release(context);
1162 return dst_texture;
1165 static void texture2d_read_from_framebuffer(struct wined3d_texture *texture, unsigned int sub_resource_idx,
1166 struct wined3d_context *context, DWORD src_location, DWORD dst_location)
1168 struct wined3d_device *device = texture->resource.device;
1169 struct wined3d_texture *restore_texture;
1170 const struct wined3d_gl_info *gl_info;
1171 unsigned int row_pitch, slice_pitch;
1172 unsigned int width, height, level;
1173 struct wined3d_bo_address data;
1174 unsigned int restore_idx;
1175 BYTE *row, *top, *bottom;
1176 BOOL src_is_upside_down;
1177 unsigned int i;
1178 BYTE *mem;
1180 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
1182 restore_texture = context->current_rt.texture;
1183 restore_idx = context->current_rt.sub_resource_idx;
1184 if (restore_texture != texture || restore_idx != sub_resource_idx)
1185 context = context_acquire(device, texture, sub_resource_idx);
1186 else
1187 restore_texture = NULL;
1188 gl_info = context->gl_info;
1190 if (src_location != texture->resource.draw_binding)
1192 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER,
1193 &texture->resource, sub_resource_idx, NULL, 0, src_location);
1194 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1195 context_invalidate_state(context, STATE_FRAMEBUFFER);
1197 else
1199 context_apply_blit_state(context, device);
1202 /* Select the correct read buffer, and give some debug output.
1203 * There is no need to keep track of the current read buffer or reset it,
1204 * every part of the code that reads sets the read buffer as desired.
1206 if (src_location != WINED3D_LOCATION_DRAWABLE || wined3d_resource_is_offscreen(&texture->resource))
1208 /* Mapping the primary render target which is not on a swapchain.
1209 * Read from the back buffer. */
1210 TRACE("Mapping offscreen render target.\n");
1211 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1212 src_is_upside_down = TRUE;
1214 else
1216 /* Onscreen surfaces are always part of a swapchain */
1217 GLenum buffer = wined3d_texture_get_gl_buffer(texture);
1218 TRACE("Mapping %#x buffer.\n", buffer);
1219 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
1220 src_is_upside_down = FALSE;
1222 checkGLcall("glReadBuffer");
1224 if (data.buffer_object)
1226 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1227 checkGLcall("glBindBuffer");
1230 level = sub_resource_idx % texture->level_count;
1231 wined3d_texture_get_pitch(texture, level, &row_pitch, &slice_pitch);
1233 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1234 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / texture->resource.format->byte_count);
1235 checkGLcall("glPixelStorei");
1237 width = wined3d_texture_get_level_width(texture, level);
1238 height = wined3d_texture_get_level_height(texture, level);
1239 gl_info->gl_ops.gl.p_glReadPixels(0, 0, width, height,
1240 texture->resource.format->glFormat,
1241 texture->resource.format->glType, data.addr);
1242 checkGLcall("glReadPixels");
1244 /* Reset previous pixel store pack state */
1245 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
1246 checkGLcall("glPixelStorei");
1248 if (!src_is_upside_down)
1250 /* glReadPixels returns the image upside down, and there is no way to
1251 * prevent this. Flip the lines in software. */
1253 if (!(row = heap_alloc(row_pitch)))
1254 goto error;
1256 if (data.buffer_object)
1258 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
1259 checkGLcall("glMapBuffer");
1261 else
1262 mem = data.addr;
1264 top = mem;
1265 bottom = mem + row_pitch * (height - 1);
1266 for (i = 0; i < height / 2; i++)
1268 memcpy(row, top, row_pitch);
1269 memcpy(top, bottom, row_pitch);
1270 memcpy(bottom, row, row_pitch);
1271 top += row_pitch;
1272 bottom -= row_pitch;
1274 heap_free(row);
1276 if (data.buffer_object)
1277 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
1280 error:
1281 if (data.buffer_object)
1283 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1284 checkGLcall("glBindBuffer");
1287 if (restore_texture)
1288 context_restore(context, restore_texture, restore_idx);
1291 /* Read the framebuffer contents into a texture. Note that this function
1292 * doesn't do any kind of flipping. Using this on an onscreen surface will
1293 * result in a flipped D3D texture.
1295 * Context activation is done by the caller. This function may temporarily
1296 * switch to a different context and restore the original one before return. */
1297 void texture2d_load_fb_texture(struct wined3d_texture *texture,
1298 unsigned int sub_resource_idx, BOOL srgb, struct wined3d_context *context)
1300 struct wined3d_device *device = texture->resource.device;
1301 struct wined3d_texture *restore_texture;
1302 const struct wined3d_gl_info *gl_info;
1303 unsigned int restore_idx, level;
1304 GLenum target;
1306 restore_texture = context->current_rt.texture;
1307 restore_idx = context->current_rt.sub_resource_idx;
1308 if (restore_texture != texture || restore_idx != sub_resource_idx)
1309 context = context_acquire(device, texture, sub_resource_idx);
1310 else
1311 restore_texture = NULL;
1313 gl_info = context->gl_info;
1314 device_invalidate_state(device, STATE_FRAMEBUFFER);
1316 wined3d_texture_prepare_texture(texture, context, srgb);
1317 wined3d_texture_bind_and_dirtify(texture, context, srgb);
1319 TRACE("Reading back offscreen render target %p, %u.\n", texture, sub_resource_idx);
1321 if (wined3d_resource_is_offscreen(&texture->resource))
1322 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1323 else
1324 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(texture));
1325 checkGLcall("glReadBuffer");
1327 level = sub_resource_idx % texture->level_count;
1328 target = wined3d_texture_get_sub_resource_target(texture, sub_resource_idx);
1329 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(target, level, 0, 0, 0, 0,
1330 wined3d_texture_get_level_width(texture, level),
1331 wined3d_texture_get_level_height(texture, level));
1332 checkGLcall("glCopyTexSubImage2D");
1334 if (restore_texture)
1335 context_restore(context, restore_texture, restore_idx);
1338 /* Does a direct frame buffer -> texture copy. Stretching is done with single
1339 * pixel copy calls. */
1340 static void fb_copy_to_texture_direct(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
1341 const RECT *dst_rect_in, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1342 const RECT *src_rect, enum wined3d_texture_filter_type filter)
1344 struct wined3d_device *device = dst_texture->resource.device;
1345 unsigned int src_height, src_level, dst_level;
1346 const struct wined3d_gl_info *gl_info;
1347 float xrel, yrel;
1348 struct wined3d_context *context;
1349 BOOL upsidedown = FALSE;
1350 RECT dst_rect = *dst_rect_in;
1351 GLenum dst_target;
1353 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1354 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1356 if(dst_rect.top > dst_rect.bottom) {
1357 UINT tmp = dst_rect.bottom;
1358 dst_rect.bottom = dst_rect.top;
1359 dst_rect.top = tmp;
1360 upsidedown = TRUE;
1363 context = context_acquire(device, src_texture, src_sub_resource_idx);
1364 gl_info = context->gl_info;
1365 context_apply_blit_state(context, device);
1366 wined3d_texture_load(dst_texture, context, FALSE);
1368 /* Bind the target texture */
1369 context_bind_texture(context, dst_texture->target, dst_texture->texture_rgb.name);
1370 if (wined3d_resource_is_offscreen(&src_texture->resource))
1372 TRACE("Reading from an offscreen target\n");
1373 upsidedown = !upsidedown;
1374 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1376 else
1378 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
1380 checkGLcall("glReadBuffer");
1382 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
1383 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
1385 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1387 FIXME_(d3d_perf)("Doing a pixel by pixel copy from the framebuffer to a texture.\n");
1389 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1390 ERR("Texture filtering not supported in direct blit.\n");
1392 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1393 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1395 ERR("Texture filtering not supported in direct blit\n");
1398 src_level = src_sub_resource_idx % src_texture->level_count;
1399 dst_level = dst_sub_resource_idx % dst_texture->level_count;
1401 src_height = wined3d_texture_get_level_height(src_texture, src_level);
1402 dst_target = wined3d_texture_get_sub_resource_target(dst_texture, dst_sub_resource_idx);
1403 if (upsidedown
1404 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1405 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1407 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
1408 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_target, dst_level,
1409 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
1410 src_rect->left, src_height - src_rect->bottom,
1411 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1413 else
1415 LONG row;
1416 UINT yoffset = src_height - src_rect->top + dst_rect.top - 1;
1417 /* I have to process this row by row to swap the image,
1418 * otherwise it would be upside down, so stretching in y direction
1419 * doesn't cost extra time
1421 * However, stretching in x direction can be avoided if not necessary
1423 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
1424 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1426 /* Well, that stuff works, but it's very slow.
1427 * find a better way instead
1429 LONG col;
1431 for (col = dst_rect.left; col < dst_rect.right; ++col)
1433 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_target, dst_level,
1434 dst_rect.left + col /* x offset */, row /* y offset */,
1435 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
1438 else
1440 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_target, dst_level,
1441 dst_rect.left /* x offset */, row /* y offset */,
1442 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
1446 checkGLcall("glCopyTexSubImage2D");
1448 context_release(context);
1450 /* The texture is now most up to date - If the surface is a render target
1451 * and has a drawable, this path is never entered. */
1452 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1453 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1456 /* Uses the hardware to stretch and flip the image */
1457 static void fb_copy_to_texture_hwstretch(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
1458 const RECT *dst_rect_in, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1459 const RECT *src_rect, enum wined3d_texture_filter_type filter)
1461 unsigned int src_width, src_height, src_pow2_width, src_pow2_height, src_level;
1462 struct wined3d_device *device = dst_texture->resource.device;
1463 GLenum src_target, dst_target, texture_target;
1464 GLuint src, backup = 0;
1465 float left, right, top, bottom; /* Texture coordinates */
1466 const struct wined3d_gl_info *gl_info;
1467 struct wined3d_context *context;
1468 GLenum drawBuffer = GL_BACK;
1469 GLenum offscreen_buffer;
1470 BOOL noBackBufferBackup;
1471 BOOL src_offscreen;
1472 BOOL upsidedown = FALSE;
1473 RECT dst_rect = *dst_rect_in;
1475 TRACE("Using hwstretch blit\n");
1477 src_target = wined3d_texture_get_sub_resource_target(src_texture, src_sub_resource_idx);
1478 dst_target = wined3d_texture_get_sub_resource_target(dst_texture, dst_sub_resource_idx);
1480 /* Activate the Proper context for reading from the source surface, set it up for blitting */
1481 context = context_acquire(device, src_texture, src_sub_resource_idx);
1482 gl_info = context->gl_info;
1483 context_apply_ffp_blit_state(context, device);
1484 wined3d_texture_load(dst_texture, context, FALSE);
1486 offscreen_buffer = context_get_offscreen_gl_buffer(context);
1487 src_level = src_sub_resource_idx % src_texture->level_count;
1488 src_width = wined3d_texture_get_level_width(src_texture, src_level);
1489 src_height = wined3d_texture_get_level_height(src_texture, src_level);
1490 src_pow2_width = wined3d_texture_get_level_pow2_width(src_texture, src_level);
1491 src_pow2_height = wined3d_texture_get_level_pow2_height(src_texture, src_level);
1493 src_offscreen = wined3d_resource_is_offscreen(&src_texture->resource);
1494 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
1495 if (!noBackBufferBackup && !src_texture->texture_rgb.name)
1497 /* Get it a description */
1498 wined3d_texture_load(src_texture, context, FALSE);
1501 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
1502 * This way we don't have to wait for the 2nd readback to finish to leave this function.
1504 if (context->aux_buffers >= 2)
1506 /* Got more than one aux buffer? Use the 2nd aux buffer */
1507 drawBuffer = GL_AUX1;
1509 else if ((!src_offscreen || offscreen_buffer == GL_BACK) && context->aux_buffers >= 1)
1511 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
1512 drawBuffer = GL_AUX0;
1515 if (noBackBufferBackup)
1517 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
1518 checkGLcall("glGenTextures");
1519 context_bind_texture(context, GL_TEXTURE_2D, backup);
1520 texture_target = GL_TEXTURE_2D;
1522 else
1524 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
1525 * we are reading from the back buffer, the backup can be used as source texture
1527 texture_target = src_target;
1528 context_bind_texture(context, texture_target, src_texture->texture_rgb.name);
1529 gl_info->gl_ops.gl.p_glEnable(texture_target);
1530 checkGLcall("glEnable(texture_target)");
1532 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
1533 src_texture->sub_resources[src_sub_resource_idx].locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
1536 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1537 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1539 if(dst_rect.top > dst_rect.bottom) {
1540 UINT tmp = dst_rect.bottom;
1541 dst_rect.bottom = dst_rect.top;
1542 dst_rect.top = tmp;
1543 upsidedown = TRUE;
1546 if (src_offscreen)
1548 TRACE("Reading from an offscreen target\n");
1549 upsidedown = !upsidedown;
1550 gl_info->gl_ops.gl.p_glReadBuffer(offscreen_buffer);
1552 else
1554 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
1557 /* TODO: Only back up the part that will be overwritten */
1558 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, src_width, src_height);
1560 checkGLcall("glCopyTexSubImage2D");
1562 /* No issue with overriding these - the sampler is dirty due to blit usage */
1563 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
1564 checkGLcall("glTexParameteri");
1565 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
1566 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
1567 checkGLcall("glTexParameteri");
1569 if (!src_texture->swapchain || src_texture == src_texture->swapchain->back_buffers[0])
1571 src = backup ? backup : src_texture->texture_rgb.name;
1573 else
1575 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
1576 checkGLcall("glReadBuffer(GL_FRONT)");
1578 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
1579 checkGLcall("glGenTextures(1, &src)");
1580 context_bind_texture(context, GL_TEXTURE_2D, src);
1582 /* TODO: Only copy the part that will be read. Use src_rect->left,
1583 * src_rect->bottom as origin, but with the width watch out for power
1584 * of 2 sizes. */
1585 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_pow2_width,
1586 src_pow2_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
1587 checkGLcall("glTexImage2D");
1588 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, src_width, src_height);
1590 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1591 checkGLcall("glTexParameteri");
1592 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1593 checkGLcall("glTexParameteri");
1595 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
1596 checkGLcall("glReadBuffer(GL_BACK)");
1598 if (texture_target != GL_TEXTURE_2D)
1600 gl_info->gl_ops.gl.p_glDisable(texture_target);
1601 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
1602 texture_target = GL_TEXTURE_2D;
1605 checkGLcall("glEnd and previous");
1607 left = src_rect->left;
1608 right = src_rect->right;
1610 if (!upsidedown)
1612 top = src_height - src_rect->top;
1613 bottom = src_height - src_rect->bottom;
1615 else
1617 top = src_height - src_rect->bottom;
1618 bottom = src_height - src_rect->top;
1621 if (src_texture->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
1623 left /= src_pow2_width;
1624 right /= src_pow2_width;
1625 top /= src_pow2_height;
1626 bottom /= src_pow2_height;
1629 /* draw the source texture stretched and upside down. The correct surface is bound already */
1630 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1631 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1633 context_set_draw_buffer(context, drawBuffer);
1634 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
1636 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
1637 /* bottom left */
1638 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
1639 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
1641 /* top left */
1642 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
1643 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
1645 /* top right */
1646 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
1647 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1649 /* bottom right */
1650 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
1651 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
1652 gl_info->gl_ops.gl.p_glEnd();
1653 checkGLcall("glEnd and previous");
1655 if (texture_target != dst_target)
1657 gl_info->gl_ops.gl.p_glDisable(texture_target);
1658 gl_info->gl_ops.gl.p_glEnable(dst_target);
1659 texture_target = dst_target;
1662 /* Now read the stretched and upside down image into the destination texture */
1663 context_bind_texture(context, texture_target, dst_texture->texture_rgb.name);
1664 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
1666 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
1667 0, 0, /* We blitted the image to the origin */
1668 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1669 checkGLcall("glCopyTexSubImage2D");
1671 if (drawBuffer == GL_BACK)
1673 /* Write the back buffer backup back. */
1674 if (backup)
1676 if (texture_target != GL_TEXTURE_2D)
1678 gl_info->gl_ops.gl.p_glDisable(texture_target);
1679 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
1680 texture_target = GL_TEXTURE_2D;
1682 context_bind_texture(context, GL_TEXTURE_2D, backup);
1684 else
1686 if (texture_target != src_target)
1688 gl_info->gl_ops.gl.p_glDisable(texture_target);
1689 gl_info->gl_ops.gl.p_glEnable(src_target);
1690 texture_target = src_target;
1692 context_bind_texture(context, src_target, src_texture->texture_rgb.name);
1695 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
1696 /* top left */
1697 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
1698 gl_info->gl_ops.gl.p_glVertex2i(0, src_height);
1700 /* bottom left */
1701 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)src_height / (float)src_pow2_height);
1702 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
1704 /* bottom right */
1705 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width,
1706 (float)src_height / (float)src_pow2_height);
1707 gl_info->gl_ops.gl.p_glVertex2i(src_width, 0);
1709 /* top right */
1710 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width, 0.0f);
1711 gl_info->gl_ops.gl.p_glVertex2i(src_width, src_height);
1712 gl_info->gl_ops.gl.p_glEnd();
1714 gl_info->gl_ops.gl.p_glDisable(texture_target);
1715 checkGLcall("glDisable(texture_target)");
1717 /* Cleanup */
1718 if (src != src_texture->texture_rgb.name && src != backup)
1720 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
1721 checkGLcall("glDeleteTextures(1, &src)");
1723 if (backup)
1725 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
1726 checkGLcall("glDeleteTextures(1, &backup)");
1729 context_release(context);
1731 /* The texture is now most up to date - If the surface is a render target
1732 * and has a drawable, this path is never entered. */
1733 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1734 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1737 static HRESULT wined3d_texture_blt_special(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
1738 const RECT *dst_rect, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
1739 const RECT *src_rect, DWORD flags, const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
1741 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1742 const struct wined3d_rendertarget_view *rtv;
1744 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_rect %s, src_texture %p, "
1745 "src_sub_resource_idx %u, src_rect %s, flags %#x, fx %p, filter %s.\n",
1746 dst_texture, dst_sub_resource_idx, wine_dbgstr_rect(dst_rect), src_texture, src_sub_resource_idx,
1747 wine_dbgstr_rect(src_rect), flags, fx, debug_d3dtexturefiltertype(filter));
1749 /* Get the swapchain. One of the surfaces has to be a primary surface. */
1750 if (!(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1752 WARN("Destination resource is not GPU accessible, rejecting GL blit.\n");
1753 return WINED3DERR_INVALIDCALL;
1756 if (!(src_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1758 WARN("Source resource is not GPU accessible, rejecting GL blit.\n");
1759 return WINED3DERR_INVALIDCALL;
1762 src_swapchain = src_texture->swapchain;
1763 dst_swapchain = dst_texture->swapchain;
1765 /* Early sort out of cases where no render target is used */
1766 if (!(rtv = dst_texture->resource.device->fb.render_targets[0]) || (!src_swapchain && !dst_swapchain
1767 && (&src_texture->resource != rtv->resource || src_sub_resource_idx != rtv->sub_resource_idx)
1768 && (&dst_texture->resource != rtv->resource || dst_sub_resource_idx != rtv->sub_resource_idx)))
1770 TRACE("No surface is render target, not using hardware blit.\n");
1771 return WINED3DERR_INVALIDCALL;
1774 /* No destination color keying supported */
1775 if (flags & (WINED3D_BLT_DST_CKEY | WINED3D_BLT_DST_CKEY_OVERRIDE))
1777 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
1778 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
1779 return WINED3DERR_INVALIDCALL;
1782 if (dst_swapchain && dst_swapchain == src_swapchain)
1784 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
1785 return WINED3DERR_INVALIDCALL;
1788 if (dst_swapchain && src_swapchain)
1790 FIXME("Implement hardware blit between two different swapchains\n");
1791 return WINED3DERR_INVALIDCALL;
1794 if (dst_swapchain)
1796 /* Handled with regular texture -> swapchain blit */
1797 if (&src_texture->resource == rtv->resource && src_sub_resource_idx == rtv->sub_resource_idx)
1798 TRACE("Blit from active render target to a swapchain\n");
1800 else if (src_swapchain && &dst_texture->resource == rtv->resource
1801 && dst_sub_resource_idx == rtv->sub_resource_idx)
1803 FIXME("Implement blit from a swapchain to the active render target\n");
1804 return WINED3DERR_INVALIDCALL;
1807 if (!dst_swapchain && (src_swapchain || (&src_texture->resource == rtv->resource
1808 && src_sub_resource_idx == rtv->sub_resource_idx)))
1810 unsigned int src_level, src_width, src_height;
1811 /* Blit from render target to texture */
1812 BOOL stretchx;
1814 /* P8 read back is not implemented */
1815 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT
1816 || dst_texture->resource.format->id == WINED3DFMT_P8_UINT)
1818 TRACE("P8 read back not supported by frame buffer to texture blit\n");
1819 return WINED3DERR_INVALIDCALL;
1822 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_SRC_CKEY_OVERRIDE))
1824 TRACE("Color keying not supported by frame buffer to texture blit\n");
1825 return WINED3DERR_INVALIDCALL;
1826 /* Destination color key is checked above */
1829 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
1830 stretchx = TRUE;
1831 else
1832 stretchx = FALSE;
1834 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
1835 * flip the image nor scale it.
1837 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
1838 * -> If the app wants an image width an unscaled width, copy it line per line
1839 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
1840 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
1841 * back buffer. This is slower than reading line per line, thus not used for flipping
1842 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
1843 * pixel by pixel. */
1844 src_level = src_sub_resource_idx % src_texture->level_count;
1845 src_width = wined3d_texture_get_level_width(src_texture, src_level);
1846 src_height = wined3d_texture_get_level_height(src_texture, src_level);
1847 if (!stretchx || dst_rect->right - dst_rect->left > src_width
1848 || dst_rect->bottom - dst_rect->top > src_height)
1850 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
1851 fb_copy_to_texture_direct(dst_texture, dst_sub_resource_idx, dst_rect,
1852 src_texture, src_sub_resource_idx, src_rect, filter);
1854 else
1856 TRACE("Using hardware stretching to flip / stretch the texture.\n");
1857 fb_copy_to_texture_hwstretch(dst_texture, dst_sub_resource_idx, dst_rect,
1858 src_texture, src_sub_resource_idx, src_rect, filter);
1861 return WINED3D_OK;
1864 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
1865 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
1866 return WINED3DERR_INVALIDCALL;
1869 /* Context activation is done by the caller. */
1870 BOOL texture2d_load_sysmem(struct wined3d_texture *texture, unsigned int sub_resource_idx,
1871 struct wined3d_context *context, DWORD dst_location)
1873 struct wined3d_texture_sub_resource *sub_resource;
1875 sub_resource = &texture->sub_resources[sub_resource_idx];
1876 wined3d_texture_prepare_location(texture, sub_resource_idx, context, dst_location);
1878 /* We cannot download data from multisample textures directly. */
1879 if (is_multisample_location(texture, WINED3D_LOCATION_TEXTURE_RGB))
1881 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_RB_RESOLVED);
1882 texture2d_read_from_framebuffer(texture, sub_resource_idx, context,
1883 WINED3D_LOCATION_RB_RESOLVED, dst_location);
1884 return TRUE;
1886 else
1888 if (sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
1889 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
1891 /* Download the sub-resource to system memory. */
1892 if (sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
1894 wined3d_texture_bind_and_dirtify(texture, context,
1895 !(sub_resource->locations & WINED3D_LOCATION_TEXTURE_RGB));
1896 texture2d_download_data(texture, sub_resource_idx, context, dst_location);
1897 ++texture->download_count;
1899 return TRUE;
1903 if (!(texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
1904 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
1906 texture2d_read_from_framebuffer(texture, sub_resource_idx, context,
1907 texture->resource.draw_binding, dst_location);
1908 return TRUE;
1911 FIXME("Can't load texture %p, %u with location flags %s into sysmem.\n",
1912 texture, sub_resource_idx, wined3d_debug_location(sub_resource->locations));
1913 return FALSE;
1916 /* Context activation is done by the caller. */
1917 BOOL texture2d_load_drawable(struct wined3d_texture *texture,
1918 unsigned int sub_resource_idx, struct wined3d_context *context)
1920 struct wined3d_texture *restore_texture;
1921 struct wined3d_device *device;
1922 unsigned int restore_idx;
1923 unsigned int level;
1924 RECT r;
1926 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
1928 DWORD current = texture->sub_resources[sub_resource_idx].locations;
1929 FIXME("Unimplemented copy from %s for depth/stencil buffers.\n",
1930 wined3d_debug_location(current));
1931 return FALSE;
1934 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
1935 && wined3d_resource_is_offscreen(&texture->resource))
1937 ERR("Trying to load offscreen texture into WINED3D_LOCATION_DRAWABLE.\n");
1938 return FALSE;
1941 device = texture->resource.device;
1942 restore_texture = context->current_rt.texture;
1943 restore_idx = context->current_rt.sub_resource_idx;
1944 if (restore_texture != texture || restore_idx != sub_resource_idx)
1945 context = context_acquire(device, texture, sub_resource_idx);
1946 else
1947 restore_texture = NULL;
1949 level = sub_resource_idx % texture->level_count;
1950 SetRect(&r, 0, 0, wined3d_texture_get_level_width(texture, level),
1951 wined3d_texture_get_level_height(texture, level));
1952 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
1953 device->blitter->ops->blitter_blit(device->blitter, WINED3D_BLIT_OP_COLOR_BLIT, context,
1954 texture, sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB, &r,
1955 texture, sub_resource_idx, WINED3D_LOCATION_DRAWABLE, &r,
1956 NULL, WINED3D_TEXF_POINT);
1958 if (restore_texture)
1959 context_restore(context, restore_texture, restore_idx);
1961 return TRUE;
1964 BOOL texture2d_load_texture(struct wined3d_texture *texture, unsigned int sub_resource_idx,
1965 struct wined3d_context *context, BOOL srgb)
1967 unsigned int width, height, level, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch;
1968 const struct wined3d_gl_info *gl_info = context->gl_info;
1969 struct wined3d_device *device = texture->resource.device;
1970 const struct wined3d_color_key_conversion *conversion;
1971 struct wined3d_texture_sub_resource *sub_resource;
1972 const struct wined3d_format *format;
1973 struct wined3d_bo_address data;
1974 BYTE *src_mem, *dst_mem = NULL;
1975 struct wined3d_box src_box;
1976 BOOL depth;
1978 depth = texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL;
1979 sub_resource = &texture->sub_resources[sub_resource_idx];
1981 if (!depth && wined3d_settings.offscreen_rendering_mode != ORM_FBO
1982 && wined3d_resource_is_offscreen(&texture->resource)
1983 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
1985 texture2d_load_fb_texture(texture, sub_resource_idx, srgb, context);
1987 return TRUE;
1990 level = sub_resource_idx % texture->level_count;
1991 width = wined3d_texture_get_level_width(texture, level);
1992 height = wined3d_texture_get_level_height(texture, level);
1993 wined3d_box_set(&src_box, 0, 0, width, height, 0, 1);
1995 if (!depth && sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
1996 && (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
1997 && fbo_blitter_supported(WINED3D_BLIT_OP_COLOR_BLIT, gl_info,
1998 &texture->resource, WINED3D_LOCATION_TEXTURE_RGB,
1999 &texture->resource, WINED3D_LOCATION_TEXTURE_SRGB))
2001 RECT src_rect;
2003 SetRect(&src_rect, 0, 0, width, height);
2004 if (srgb)
2005 texture2d_blt_fbo(device, context, WINED3D_TEXF_POINT,
2006 texture, sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB, &src_rect,
2007 texture, sub_resource_idx, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
2008 else
2009 texture2d_blt_fbo(device, context, WINED3D_TEXF_POINT,
2010 texture, sub_resource_idx, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect,
2011 texture, sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
2013 return TRUE;
2016 if (!depth && sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
2017 && (!srgb || (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)))
2019 DWORD src_location = sub_resource->locations & WINED3D_LOCATION_RB_RESOLVED ?
2020 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
2021 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2022 RECT src_rect;
2024 SetRect(&src_rect, 0, 0, width, height);
2025 if (fbo_blitter_supported(WINED3D_BLIT_OP_COLOR_BLIT, gl_info,
2026 &texture->resource, src_location, &texture->resource, dst_location))
2027 texture2d_blt_fbo(device, context, WINED3D_TEXF_POINT, texture, sub_resource_idx,
2028 src_location, &src_rect, texture, sub_resource_idx, dst_location, &src_rect);
2030 return TRUE;
2033 /* Upload from system memory */
2035 if (srgb)
2037 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | texture->resource.map_binding))
2038 == WINED3D_LOCATION_TEXTURE_RGB)
2040 FIXME_(d3d_perf)("Downloading RGB texture %p, %u to reload it as sRGB.\n", texture, sub_resource_idx);
2041 wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding);
2044 else
2046 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | texture->resource.map_binding))
2047 == WINED3D_LOCATION_TEXTURE_SRGB)
2049 FIXME_(d3d_perf)("Downloading sRGB texture %p, %u to reload it as RGB.\n", texture, sub_resource_idx);
2050 wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding);
2054 if (!(sub_resource->locations & surface_simple_locations))
2056 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
2057 /* Lets hope we get it from somewhere... */
2058 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM);
2061 wined3d_texture_prepare_texture(texture, context, srgb);
2062 wined3d_texture_bind_and_dirtify(texture, context, srgb);
2063 wined3d_texture_get_pitch(texture, level, &src_row_pitch, &src_slice_pitch);
2065 format = texture->resource.format;
2066 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
2067 format = wined3d_get_format(gl_info, conversion->dst_format, texture->resource.usage);
2069 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
2070 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
2071 * getting called. */
2072 if (conversion && sub_resource->buffer_object)
2074 TRACE("Removing the pbo attached to texture %p, %u.\n", texture, sub_resource_idx);
2076 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM);
2077 wined3d_texture_set_map_binding(texture, WINED3D_LOCATION_SYSMEM);
2080 wined3d_texture_get_memory(texture, sub_resource_idx, &data, sub_resource->locations);
2081 if (conversion)
2083 wined3d_format_calculate_pitch(format, device->surface_alignment,
2084 width, height, &dst_row_pitch, &dst_slice_pitch);
2086 src_mem = context_map_bo_address(context, &data, src_slice_pitch,
2087 GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ);
2088 if (!(dst_mem = heap_alloc(dst_slice_pitch)))
2090 ERR("Out of memory (%u).\n", dst_slice_pitch);
2091 context_release(context);
2092 return FALSE;
2094 conversion->convert(src_mem, src_row_pitch, dst_mem, dst_row_pitch,
2095 width, height, &texture->async.gl_color_key);
2096 src_row_pitch = dst_row_pitch;
2097 src_slice_pitch = dst_slice_pitch;
2098 context_unmap_bo_address(context, &data, GL_PIXEL_UNPACK_BUFFER);
2100 data.buffer_object = 0;
2101 data.addr = dst_mem;
2104 wined3d_texture_upload_data(texture, sub_resource_idx, context, format, &src_box,
2105 wined3d_const_bo_address(&data), src_row_pitch, src_slice_pitch, 0, 0, 0, srgb);
2107 heap_free(dst_mem);
2109 return TRUE;
2112 /* Context activation is done by the caller. */
2113 BOOL texture2d_load_renderbuffer(struct wined3d_texture *texture, unsigned int sub_resource_idx,
2114 struct wined3d_context *context, DWORD dst_location)
2116 unsigned int level = sub_resource_idx % texture->level_count;
2117 const RECT rect = {0, 0,
2118 wined3d_texture_get_level_width(texture, level),
2119 wined3d_texture_get_level_height(texture, level)};
2120 struct wined3d_texture_sub_resource *sub_resource;
2121 DWORD src_location, locations;
2123 sub_resource = &texture->sub_resources[sub_resource_idx];
2124 locations = sub_resource->locations;
2125 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2127 FIXME("Unimplemented copy from %s for depth/stencil buffers.\n",
2128 wined3d_debug_location(locations));
2129 return FALSE;
2132 if (locations & WINED3D_LOCATION_RB_MULTISAMPLE)
2133 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
2134 else if (locations & WINED3D_LOCATION_RB_RESOLVED)
2135 src_location = WINED3D_LOCATION_RB_RESOLVED;
2136 else if (locations & WINED3D_LOCATION_TEXTURE_SRGB)
2137 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
2138 else if (locations & WINED3D_LOCATION_TEXTURE_RGB)
2139 src_location = WINED3D_LOCATION_TEXTURE_RGB;
2140 else if (locations & WINED3D_LOCATION_DRAWABLE)
2141 src_location = WINED3D_LOCATION_DRAWABLE;
2142 else /* texture2d_blt_fbo() will load the source location if necessary. */
2143 src_location = WINED3D_LOCATION_TEXTURE_RGB;
2145 texture2d_blt_fbo(texture->resource.device, context, WINED3D_TEXF_POINT, texture,
2146 sub_resource_idx, src_location, &rect, texture, sub_resource_idx, dst_location, &rect);
2148 return TRUE;
2151 /* Context activation is done by the caller. */
2152 static void fbo_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2154 struct wined3d_blitter *next;
2156 if ((next = blitter->next))
2157 next->ops->blitter_destroy(next, context);
2159 heap_free(blitter);
2162 static void fbo_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
2163 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
2164 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
2166 struct wined3d_blitter *next;
2168 if ((next = blitter->next))
2169 next->ops->blitter_clear(next, device, rt_count, fb, rect_count,
2170 clear_rects, draw_rect, flags, colour, depth, stencil);
2173 static DWORD fbo_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
2174 struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
2175 DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture,
2176 unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect,
2177 const struct wined3d_color_key *colour_key, enum wined3d_texture_filter_type filter)
2179 struct wined3d_resource *src_resource, *dst_resource;
2180 enum wined3d_blit_op blit_op = op;
2181 struct wined3d_device *device;
2182 struct wined3d_blitter *next;
2184 TRACE("blitter %p, op %#x, context %p, src_texture %p, src_sub_resource_idx %u, src_location %s, src_rect %s, "
2185 "dst_texture %p, dst_sub_resource_idx %u, dst_location %s, dst_rect %s, colour_key %p, filter %s.\n",
2186 blitter, op, context, src_texture, src_sub_resource_idx, wined3d_debug_location(src_location),
2187 wine_dbgstr_rect(src_rect), dst_texture, dst_sub_resource_idx, wined3d_debug_location(dst_location),
2188 wine_dbgstr_rect(dst_rect), colour_key, debug_d3dtexturefiltertype(filter));
2190 src_resource = &src_texture->resource;
2191 dst_resource = &dst_texture->resource;
2193 device = dst_resource->device;
2195 if (blit_op == WINED3D_BLIT_OP_RAW_BLIT && dst_resource->format->id == src_resource->format->id)
2197 if (dst_resource->format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
2198 blit_op = WINED3D_BLIT_OP_DEPTH_BLIT;
2199 else
2200 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
2203 if (!fbo_blitter_supported(blit_op, context->gl_info,
2204 src_resource, src_location, dst_resource, dst_location))
2206 if (!(next = blitter->next))
2208 ERR("No blitter to handle blit op %#x.\n", op);
2209 return dst_location;
2212 TRACE("Forwarding to blitter %p.\n", next);
2213 return next->ops->blitter_blit(next, op, context, src_texture, src_sub_resource_idx, src_location,
2214 src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect, colour_key, filter);
2217 if (blit_op == WINED3D_BLIT_OP_COLOR_BLIT)
2219 TRACE("Colour blit.\n");
2220 texture2d_blt_fbo(device, context, filter, src_texture, src_sub_resource_idx, src_location,
2221 src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect);
2222 return dst_location;
2225 if (blit_op == WINED3D_BLIT_OP_DEPTH_BLIT)
2227 TRACE("Depth/stencil blit.\n");
2228 texture2d_depth_blt_fbo(device, context, src_texture, src_sub_resource_idx, src_location,
2229 src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect);
2230 return dst_location;
2233 ERR("This blitter does not implement blit op %#x.\n", blit_op);
2234 return dst_location;
2237 static const struct wined3d_blitter_ops fbo_blitter_ops =
2239 fbo_blitter_destroy,
2240 fbo_blitter_clear,
2241 fbo_blitter_blit,
2244 void wined3d_fbo_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
2246 struct wined3d_blitter *blitter;
2248 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
2249 return;
2251 if (!(blitter = heap_alloc(sizeof(*blitter))))
2252 return;
2254 TRACE("Created blitter %p.\n", blitter);
2256 blitter->ops = &fbo_blitter_ops;
2257 blitter->next = *next;
2258 *next = blitter;
2261 /* Context activation is done by the caller. */
2262 static void raw_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2264 struct wined3d_blitter *next;
2266 if ((next = blitter->next))
2267 next->ops->blitter_destroy(next, context);
2269 heap_free(blitter);
2272 /* Context activation is done by the caller. */
2273 static void raw_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
2274 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
2275 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
2277 struct wined3d_blitter *next;
2279 if (!(next = blitter->next))
2281 ERR("No blitter to handle clear.\n");
2282 return;
2285 TRACE("Forwarding to blitter %p.\n", next);
2286 next->ops->blitter_clear(next, device, rt_count, fb, rect_count,
2287 clear_rects, draw_rect, flags, colour, depth, stencil);
2290 /* Context activation is done by the caller. */
2291 static DWORD raw_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
2292 struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
2293 DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture,
2294 unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect,
2295 const struct wined3d_color_key *colour_key, enum wined3d_texture_filter_type filter)
2297 const struct wined3d_gl_info *gl_info = context->gl_info;
2298 unsigned int src_level, src_layer, dst_level, dst_layer;
2299 struct wined3d_blitter *next;
2300 GLuint src_name, dst_name;
2301 DWORD location;
2303 /* If we would need to copy from a renderbuffer or drawable, we'd probably
2304 * be better of using the FBO blitter directly, since we'd need to use it
2305 * to copy the resource contents to the texture anyway. */
2306 if (op != WINED3D_BLIT_OP_RAW_BLIT
2307 || (src_texture->resource.format->id == dst_texture->resource.format->id
2308 && (!(src_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
2309 || !(dst_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB)))))
2311 if (!(next = blitter->next))
2313 ERR("No blitter to handle blit op %#x.\n", op);
2314 return dst_location;
2317 TRACE("Forwarding to blitter %p.\n", next);
2318 return next->ops->blitter_blit(next, op, context, src_texture, src_sub_resource_idx, src_location,
2319 src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect, colour_key, filter);
2322 TRACE("Blit using ARB_copy_image.\n");
2324 src_level = src_sub_resource_idx % src_texture->level_count;
2325 src_layer = src_sub_resource_idx / src_texture->level_count;
2327 dst_level = dst_sub_resource_idx % dst_texture->level_count;
2328 dst_layer = dst_sub_resource_idx / dst_texture->level_count;
2330 location = src_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB);
2331 if (!location)
2332 location = src_texture->flags & WINED3D_TEXTURE_IS_SRGB
2333 ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2334 if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, location))
2335 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(location));
2336 src_name = wined3d_texture_get_texture_name(src_texture, context, location == WINED3D_LOCATION_TEXTURE_SRGB);
2338 location = dst_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB);
2339 if (!location)
2340 location = dst_texture->flags & WINED3D_TEXTURE_IS_SRGB
2341 ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2342 if (texture2d_is_full_rect(dst_texture, dst_level, dst_rect))
2344 if (!wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, location))
2345 ERR("Failed to prepare the destination sub-resource into %s.\n", wined3d_debug_location(location));
2347 else
2349 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, location))
2350 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(location));
2352 dst_name = wined3d_texture_get_texture_name(dst_texture, context, location == WINED3D_LOCATION_TEXTURE_SRGB);
2354 GL_EXTCALL(glCopyImageSubData(src_name, src_texture->target, src_level,
2355 src_rect->left, src_rect->top, src_layer, dst_name, dst_texture->target, dst_level,
2356 dst_rect->left, dst_rect->top, dst_layer, src_rect->right - src_rect->left,
2357 src_rect->bottom - src_rect->top, 1));
2358 checkGLcall("copy image data");
2360 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, location);
2361 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~location);
2362 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location))
2363 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(dst_location));
2365 return dst_location | location;
2368 static const struct wined3d_blitter_ops raw_blitter_ops =
2370 raw_blitter_destroy,
2371 raw_blitter_clear,
2372 raw_blitter_blit,
2375 void wined3d_raw_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
2377 struct wined3d_blitter *blitter;
2379 if (!gl_info->supported[ARB_COPY_IMAGE])
2380 return;
2382 if (!(blitter = heap_alloc(sizeof(*blitter))))
2383 return;
2385 TRACE("Created blitter %p.\n", blitter);
2387 blitter->ops = &raw_blitter_ops;
2388 blitter->next = *next;
2389 *next = blitter;
2392 /* Context activation is done by the caller. */
2393 static void ffp_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2395 struct wined3d_blitter *next;
2397 if ((next = blitter->next))
2398 next->ops->blitter_destroy(next, context);
2400 heap_free(blitter);
2403 static BOOL ffp_blit_supported(enum wined3d_blit_op blit_op, const struct wined3d_context *context,
2404 const struct wined3d_resource *src_resource, DWORD src_location,
2405 const struct wined3d_resource *dst_resource, DWORD dst_location)
2407 const struct wined3d_format *src_format = src_resource->format;
2408 const struct wined3d_format *dst_format = dst_resource->format;
2409 BOOL decompress;
2411 decompress = src_format && (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
2412 && !(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED);
2413 if (!decompress && !(src_resource->access & dst_resource->access & WINED3D_RESOURCE_ACCESS_GPU))
2415 TRACE("Source or destination resource is not GPU accessible.\n");
2416 return FALSE;
2419 if (blit_op == WINED3D_BLIT_OP_RAW_BLIT && dst_format->id == src_format->id)
2421 if (dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
2422 blit_op = WINED3D_BLIT_OP_DEPTH_BLIT;
2423 else
2424 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
2427 switch (blit_op)
2429 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
2430 if (context->d3d_info->shader_color_key)
2432 TRACE("Color keying requires converted textures.\n");
2433 return FALSE;
2435 case WINED3D_BLIT_OP_COLOR_BLIT:
2436 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
2437 if (!context->gl_info->supported[WINED3D_GL_LEGACY_CONTEXT])
2438 return FALSE;
2440 if (TRACE_ON(d3d))
2442 TRACE("Checking support for fixup:\n");
2443 dump_color_fixup_desc(src_format->color_fixup);
2446 /* We only support identity conversions. */
2447 if (!is_identity_fixup(src_format->color_fixup)
2448 || !is_identity_fixup(dst_format->color_fixup))
2450 if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER
2451 && dst_format->id == src_format->id && dst_location == WINED3D_LOCATION_DRAWABLE)
2453 WARN("Claiming fixup support because of ORM_BACKBUFFER.\n");
2455 else
2457 TRACE("Fixups are not supported.\n");
2458 return FALSE;
2462 if (!(dst_resource->usage & WINED3DUSAGE_RENDERTARGET))
2464 TRACE("Can only blit to render targets.\n");
2465 return FALSE;
2467 return TRUE;
2469 default:
2470 TRACE("Unsupported blit operation %#x.\n", blit_op);
2471 return FALSE;
2475 static BOOL ffp_blitter_use_cpu_clear(struct wined3d_rendertarget_view *view)
2477 struct wined3d_resource *resource;
2478 struct wined3d_texture *texture;
2479 DWORD locations;
2481 resource = view->resource;
2482 if (resource->type == WINED3D_RTYPE_BUFFER)
2483 return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU);
2485 texture = texture_from_resource(resource);
2486 locations = texture->sub_resources[view->sub_resource_idx].locations;
2487 if (locations & (resource->map_binding | WINED3D_LOCATION_DISCARDED))
2488 return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU)
2489 || (texture->flags & WINED3D_TEXTURE_PIN_SYSMEM);
2491 return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU)
2492 && !(texture->flags & WINED3D_TEXTURE_CONVERTED);
2495 static void ffp_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
2496 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
2497 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
2499 struct wined3d_rendertarget_view *view;
2500 struct wined3d_blitter *next;
2501 DWORD next_flags = 0;
2502 unsigned int i;
2504 if (flags & WINED3DCLEAR_TARGET)
2506 for (i = 0; i < rt_count; ++i)
2508 if (!(view = fb->render_targets[i]))
2509 continue;
2511 if (ffp_blitter_use_cpu_clear(view)
2512 || (!(view->resource->usage & WINED3DUSAGE_RENDERTARGET)
2513 && (wined3d_settings.offscreen_rendering_mode != ORM_FBO
2514 || !(view->format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE))))
2516 next_flags |= WINED3DCLEAR_TARGET;
2517 flags &= ~WINED3DCLEAR_TARGET;
2518 break;
2521 /* FIXME: We should reject colour fills on formats with fixups,
2522 * but this would break P8 colour fills for example. */
2526 if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil)
2527 && (!view->format->depth_size || (flags & WINED3DCLEAR_ZBUFFER))
2528 && (!view->format->stencil_size || (flags & WINED3DCLEAR_STENCIL))
2529 && ffp_blitter_use_cpu_clear(view))
2531 next_flags |= flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL);
2532 flags &= ~(WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL);
2535 if (flags)
2536 device_clear_render_targets(device, rt_count, fb, rect_count,
2537 clear_rects, draw_rect, flags, colour, depth, stencil);
2539 if (next_flags && (next = blitter->next))
2540 next->ops->blitter_clear(next, device, rt_count, fb, rect_count,
2541 clear_rects, draw_rect, next_flags, colour, depth, stencil);
2544 static DWORD ffp_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
2545 struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
2546 DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture,
2547 unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect,
2548 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter)
2550 const struct wined3d_gl_info *gl_info = context->gl_info;
2551 struct wined3d_resource *src_resource, *dst_resource;
2552 struct wined3d_color_key old_blt_key;
2553 struct wined3d_device *device;
2554 struct wined3d_blitter *next;
2555 DWORD old_color_key_flags;
2556 RECT r;
2558 src_resource = &src_texture->resource;
2559 dst_resource = &dst_texture->resource;
2560 device = dst_resource->device;
2562 if (!ffp_blit_supported(op, context, src_resource, src_location, dst_resource, dst_location))
2564 if ((next = blitter->next))
2565 return next->ops->blitter_blit(next, op, context, src_texture, src_sub_resource_idx, src_location,
2566 src_rect, dst_texture, dst_sub_resource_idx, dst_location, dst_rect, color_key, filter);
2569 TRACE("Blt from texture %p, %u to rendertarget %p, %u.\n",
2570 src_texture, src_sub_resource_idx, dst_texture, dst_sub_resource_idx);
2572 old_blt_key = src_texture->async.src_blt_color_key;
2573 old_color_key_flags = src_texture->async.color_key_flags;
2574 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT, color_key);
2576 /* Make sure the surface is up-to-date. This should probably use
2577 * surface_load_location() and worry about the destination surface too,
2578 * unless we're overwriting it completely. */
2579 wined3d_texture_load(src_texture, context, FALSE);
2581 context_apply_ffp_blit_state(context, device);
2583 if (dst_location == WINED3D_LOCATION_DRAWABLE)
2585 r = *dst_rect;
2586 wined3d_texture_translate_drawable_coords(dst_texture, context->win_handle, &r);
2587 dst_rect = &r;
2590 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
2592 GLenum buffer;
2594 if (dst_location == WINED3D_LOCATION_DRAWABLE)
2596 TRACE("Destination texture %p is onscreen.\n", dst_texture);
2597 buffer = wined3d_texture_get_gl_buffer(dst_texture);
2599 else
2601 TRACE("Destination texture %p is offscreen.\n", dst_texture);
2602 buffer = GL_COLOR_ATTACHMENT0;
2604 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER,
2605 dst_resource, dst_sub_resource_idx, NULL, 0, dst_location);
2606 context_set_draw_buffer(context, buffer);
2607 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
2608 context_invalidate_state(context, STATE_FRAMEBUFFER);
2611 gl_info->gl_ops.gl.p_glEnable(src_texture->target);
2612 checkGLcall("glEnable(target)");
2614 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST || color_key)
2616 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
2617 checkGLcall("glEnable(GL_ALPHA_TEST)");
2620 if (color_key)
2622 /* For P8 surfaces, the alpha component contains the palette index.
2623 * Which means that the colorkey is one of the palette entries. In
2624 * other cases pixels that should be masked away have alpha set to 0. */
2625 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT)
2626 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
2627 (float)src_texture->async.src_blt_color_key.color_space_low_value / 255.0f);
2628 else
2629 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
2630 checkGLcall("glAlphaFunc");
2633 draw_textured_quad(src_texture, src_sub_resource_idx, context, src_rect, dst_rect, filter);
2635 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST || color_key)
2637 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2638 checkGLcall("glDisable(GL_ALPHA_TEST)");
2641 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
2642 checkGLcall("glDisable(GL_TEXTURE_2D)");
2643 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
2645 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
2646 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
2648 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
2650 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
2651 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
2654 if (dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture)
2655 gl_info->gl_ops.gl.p_glFlush();
2657 /* Restore the color key parameters */
2658 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT,
2659 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
2661 return dst_location;
2664 static const struct wined3d_blitter_ops ffp_blitter_ops =
2666 ffp_blitter_destroy,
2667 ffp_blitter_clear,
2668 ffp_blitter_blit,
2671 void wined3d_ffp_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
2673 struct wined3d_blitter *blitter;
2675 if (!(blitter = heap_alloc(sizeof(*blitter))))
2676 return;
2678 TRACE("Created blitter %p.\n", blitter);
2680 blitter->ops = &ffp_blitter_ops;
2681 blitter->next = *next;
2682 *next = blitter;
2685 /* Context activation is done by the caller. */
2686 static void cpu_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2688 struct wined3d_blitter *next;
2690 if ((next = blitter->next))
2691 next->ops->blitter_destroy(next, context);
2693 heap_free(blitter);
2696 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
2697 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
2698 const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx)
2700 UINT row_block_count;
2701 const BYTE *src_row;
2702 BYTE *dst_row;
2703 UINT x, y;
2705 src_row = src_data;
2706 dst_row = dst_data;
2708 row_block_count = (update_w + format->block_width - 1) / format->block_width;
2710 if (!flags)
2712 for (y = 0; y < update_h; y += format->block_height)
2714 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
2715 src_row += src_pitch;
2716 dst_row += dst_pitch;
2719 return WINED3D_OK;
2722 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
2724 src_row += (((update_h / format->block_height) - 1) * src_pitch);
2726 switch (format->id)
2728 case WINED3DFMT_DXT1:
2729 for (y = 0; y < update_h; y += format->block_height)
2731 struct block
2733 WORD color[2];
2734 BYTE control_row[4];
2737 const struct block *s = (const struct block *)src_row;
2738 struct block *d = (struct block *)dst_row;
2740 for (x = 0; x < row_block_count; ++x)
2742 d[x].color[0] = s[x].color[0];
2743 d[x].color[1] = s[x].color[1];
2744 d[x].control_row[0] = s[x].control_row[3];
2745 d[x].control_row[1] = s[x].control_row[2];
2746 d[x].control_row[2] = s[x].control_row[1];
2747 d[x].control_row[3] = s[x].control_row[0];
2749 src_row -= src_pitch;
2750 dst_row += dst_pitch;
2752 return WINED3D_OK;
2754 case WINED3DFMT_DXT2:
2755 case WINED3DFMT_DXT3:
2756 for (y = 0; y < update_h; y += format->block_height)
2758 struct block
2760 WORD alpha_row[4];
2761 WORD color[2];
2762 BYTE control_row[4];
2765 const struct block *s = (const struct block *)src_row;
2766 struct block *d = (struct block *)dst_row;
2768 for (x = 0; x < row_block_count; ++x)
2770 d[x].alpha_row[0] = s[x].alpha_row[3];
2771 d[x].alpha_row[1] = s[x].alpha_row[2];
2772 d[x].alpha_row[2] = s[x].alpha_row[1];
2773 d[x].alpha_row[3] = s[x].alpha_row[0];
2774 d[x].color[0] = s[x].color[0];
2775 d[x].color[1] = s[x].color[1];
2776 d[x].control_row[0] = s[x].control_row[3];
2777 d[x].control_row[1] = s[x].control_row[2];
2778 d[x].control_row[2] = s[x].control_row[1];
2779 d[x].control_row[3] = s[x].control_row[0];
2781 src_row -= src_pitch;
2782 dst_row += dst_pitch;
2784 return WINED3D_OK;
2786 default:
2787 FIXME("Compressed flip not implemented for format %s.\n",
2788 debug_d3dformat(format->id));
2789 return E_NOTIMPL;
2793 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
2794 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
2796 return E_NOTIMPL;
2799 static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
2800 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
2801 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
2802 enum wined3d_texture_filter_type filter)
2804 unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count;
2805 struct wined3d_device *device = dst_texture->resource.device;
2806 const struct wined3d_format *src_format, *dst_format;
2807 struct wined3d_texture *converted_texture = NULL;
2808 struct wined3d_bo_address src_data, dst_data;
2809 unsigned int src_fmt_flags, dst_fmt_flags;
2810 struct wined3d_map_desc dst_map, src_map;
2811 struct wined3d_context *context = NULL;
2812 unsigned int x, sx, xinc, y, sy, yinc;
2813 unsigned int texture_level;
2814 HRESULT hr = WINED3D_OK;
2815 BOOL same_sub_resource;
2816 DWORD map_binding;
2817 const BYTE *sbase;
2818 const BYTE *sbuf;
2819 BYTE *dbuf;
2821 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
2822 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
2823 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture,
2824 src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
2826 if (device->d3d_initialized)
2827 context = context_acquire(device, NULL, 0);
2829 if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx)
2831 same_sub_resource = TRUE;
2833 map_binding = dst_texture->resource.map_binding;
2834 texture_level = dst_sub_resource_idx % dst_texture->level_count;
2835 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
2836 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
2837 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
2838 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
2839 wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
2840 dst_map.data = context_map_bo_address(context, &dst_data,
2841 dst_texture->sub_resources[dst_sub_resource_idx].size,
2842 GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ | WINED3D_MAP_WRITE);
2844 src_map = dst_map;
2845 src_format = dst_texture->resource.format;
2846 dst_format = src_format;
2847 dst_fmt_flags = dst_texture->resource.format_flags;
2848 src_fmt_flags = dst_fmt_flags;
2850 else
2852 same_sub_resource = FALSE;
2853 dst_format = dst_texture->resource.format;
2854 dst_fmt_flags = dst_texture->resource.format_flags;
2855 if (!(flags & WINED3D_BLT_RAW) && dst_texture->resource.format->id != src_texture->resource.format->id)
2857 if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format)))
2859 FIXME("Cannot convert %s to %s.\n", debug_d3dformat(src_texture->resource.format->id),
2860 debug_d3dformat(dst_texture->resource.format->id));
2861 if (context)
2862 context_release(context);
2863 return WINED3DERR_NOTAVAILABLE;
2865 src_texture = converted_texture;
2866 src_sub_resource_idx = 0;
2868 src_format = src_texture->resource.format;
2869 src_fmt_flags = src_texture->resource.format_flags;
2871 map_binding = src_texture->resource.map_binding;
2872 texture_level = src_sub_resource_idx % src_texture->level_count;
2873 if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, map_binding))
2874 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
2875 wined3d_texture_get_pitch(src_texture, texture_level, &src_map.row_pitch, &src_map.slice_pitch);
2876 wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &src_data, map_binding);
2877 src_map.data = context_map_bo_address(context, &src_data,
2878 src_texture->sub_resources[src_sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ);
2880 map_binding = dst_texture->resource.map_binding;
2881 texture_level = dst_sub_resource_idx % dst_texture->level_count;
2882 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
2883 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
2884 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
2885 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
2886 wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
2887 dst_map.data = context_map_bo_address(context, &dst_data,
2888 dst_texture->sub_resources[dst_sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_WRITE);
2890 flags &= ~WINED3D_BLT_RAW;
2892 bpp = dst_format->byte_count;
2893 src_height = src_box->bottom - src_box->top;
2894 src_width = src_box->right - src_box->left;
2895 dst_height = dst_box->bottom - dst_box->top;
2896 dst_width = dst_box->right - dst_box->left;
2897 row_byte_count = dst_width * bpp;
2899 sbase = (BYTE *)src_map.data
2900 + ((src_box->top / src_format->block_height) * src_map.row_pitch)
2901 + ((src_box->left / src_format->block_width) * src_format->block_byte_count);
2902 dbuf = (BYTE *)dst_map.data
2903 + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch)
2904 + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count);
2906 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
2908 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
2910 if (same_sub_resource)
2912 FIXME("Only plain blits supported on compressed surfaces.\n");
2913 hr = E_NOTIMPL;
2914 goto release;
2917 if (src_height != dst_height || src_width != dst_width)
2919 WARN("Stretching not supported on compressed surfaces.\n");
2920 hr = WINED3DERR_INVALIDCALL;
2921 goto release;
2924 hr = surface_cpu_blt_compressed(sbase, dbuf,
2925 src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height,
2926 src_format, flags, fx);
2927 goto release;
2930 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
2931 && (src_width != dst_width || src_height != dst_height))
2933 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
2934 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
2937 xinc = (src_width << 16) / dst_width;
2938 yinc = (src_height << 16) / dst_height;
2940 if (!flags)
2942 /* No effects, we can cheat here. */
2943 if (dst_width == src_width)
2945 if (dst_height == src_height)
2947 /* No stretching in either direction. This needs to be as fast
2948 * as possible. */
2949 sbuf = sbase;
2951 /* Check for overlapping surfaces. */
2952 if (!same_sub_resource || dst_box->top < src_box->top
2953 || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
2955 /* No overlap, or dst above src, so copy from top downwards. */
2956 for (y = 0; y < dst_height; ++y)
2958 memcpy(dbuf, sbuf, row_byte_count);
2959 sbuf += src_map.row_pitch;
2960 dbuf += dst_map.row_pitch;
2963 else if (dst_box->top > src_box->top)
2965 /* Copy from bottom upwards. */
2966 sbuf += src_map.row_pitch * dst_height;
2967 dbuf += dst_map.row_pitch * dst_height;
2968 for (y = 0; y < dst_height; ++y)
2970 sbuf -= src_map.row_pitch;
2971 dbuf -= dst_map.row_pitch;
2972 memcpy(dbuf, sbuf, row_byte_count);
2975 else
2977 /* Src and dst overlapping on the same line, use memmove. */
2978 for (y = 0; y < dst_height; ++y)
2980 memmove(dbuf, sbuf, row_byte_count);
2981 sbuf += src_map.row_pitch;
2982 dbuf += dst_map.row_pitch;
2986 else
2988 /* Stretching in y direction only. */
2989 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
2991 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
2992 memcpy(dbuf, sbuf, row_byte_count);
2993 dbuf += dst_map.row_pitch;
2997 else
2999 /* Stretching in X direction. */
3000 unsigned int last_sy = ~0u;
3001 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3003 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3005 if ((sy >> 16) == (last_sy >> 16))
3007 /* This source row is the same as last source row -
3008 * Copy the already stretched row. */
3009 memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count);
3011 else
3013 #define STRETCH_ROW(type) \
3014 do { \
3015 const type *s = (const type *)sbuf; \
3016 type *d = (type *)dbuf; \
3017 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3018 d[x] = s[sx >> 16]; \
3019 } while(0)
3021 switch(bpp)
3023 case 1:
3024 STRETCH_ROW(BYTE);
3025 break;
3026 case 2:
3027 STRETCH_ROW(WORD);
3028 break;
3029 case 4:
3030 STRETCH_ROW(DWORD);
3031 break;
3032 case 3:
3034 const BYTE *s;
3035 BYTE *d = dbuf;
3036 for (x = sx = 0; x < dst_width; x++, sx+= xinc)
3038 DWORD pixel;
3040 s = sbuf + 3 * (sx >> 16);
3041 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3042 d[0] = (pixel ) & 0xff;
3043 d[1] = (pixel >> 8) & 0xff;
3044 d[2] = (pixel >> 16) & 0xff;
3045 d += 3;
3047 break;
3049 default:
3050 FIXME("Stretched blit not implemented for bpp %u.\n", bpp * 8);
3051 hr = WINED3DERR_NOTAVAILABLE;
3052 goto error;
3054 #undef STRETCH_ROW
3056 dbuf += dst_map.row_pitch;
3057 last_sy = sy;
3061 else
3063 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
3064 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
3065 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
3066 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3067 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
3069 /* The color keying flags are checked for correctness in ddraw. */
3070 if (flags & WINED3D_BLT_SRC_CKEY)
3072 keylow = src_texture->async.src_blt_color_key.color_space_low_value;
3073 keyhigh = src_texture->async.src_blt_color_key.color_space_high_value;
3075 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3077 keylow = fx->src_color_key.color_space_low_value;
3078 keyhigh = fx->src_color_key.color_space_high_value;
3081 if (flags & WINED3D_BLT_DST_CKEY)
3083 /* Destination color keys are taken from the source surface! */
3084 destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value;
3085 destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value;
3087 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
3089 destkeylow = fx->dst_color_key.color_space_low_value;
3090 destkeyhigh = fx->dst_color_key.color_space_high_value;
3093 if (bpp == 1)
3095 keymask = 0xff;
3097 else
3099 DWORD masks[3];
3100 get_color_masks(src_format, masks);
3101 keymask = masks[0] | masks[1] | masks[2];
3103 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3104 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
3107 if (flags & WINED3D_BLT_FX)
3109 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
3110 LONG tmpxy;
3111 dTopLeft = dbuf;
3112 dTopRight = dbuf + ((dst_width - 1) * bpp);
3113 dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
3114 dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
3116 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
3118 /* I don't think we need to do anything about this flag. */
3119 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
3121 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
3123 tmp = dTopRight;
3124 dTopRight = dTopLeft;
3125 dTopLeft = tmp;
3126 tmp = dBottomRight;
3127 dBottomRight = dBottomLeft;
3128 dBottomLeft = tmp;
3129 dstxinc = dstxinc * -1;
3131 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
3133 tmp = dTopLeft;
3134 dTopLeft = dBottomLeft;
3135 dBottomLeft = tmp;
3136 tmp = dTopRight;
3137 dTopRight = dBottomRight;
3138 dBottomRight = tmp;
3139 dstyinc = dstyinc * -1;
3141 if (fx->fx & WINEDDBLTFX_NOTEARING)
3143 /* I don't think we need to do anything about this flag. */
3144 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
3146 if (fx->fx & WINEDDBLTFX_ROTATE180)
3148 tmp = dBottomRight;
3149 dBottomRight = dTopLeft;
3150 dTopLeft = tmp;
3151 tmp = dBottomLeft;
3152 dBottomLeft = dTopRight;
3153 dTopRight = tmp;
3154 dstxinc = dstxinc * -1;
3155 dstyinc = dstyinc * -1;
3157 if (fx->fx & WINEDDBLTFX_ROTATE270)
3159 tmp = dTopLeft;
3160 dTopLeft = dBottomLeft;
3161 dBottomLeft = dBottomRight;
3162 dBottomRight = dTopRight;
3163 dTopRight = tmp;
3164 tmpxy = dstxinc;
3165 dstxinc = dstyinc;
3166 dstyinc = tmpxy;
3167 dstxinc = dstxinc * -1;
3169 if (fx->fx & WINEDDBLTFX_ROTATE90)
3171 tmp = dTopLeft;
3172 dTopLeft = dTopRight;
3173 dTopRight = dBottomRight;
3174 dBottomRight = dBottomLeft;
3175 dBottomLeft = tmp;
3176 tmpxy = dstxinc;
3177 dstxinc = dstyinc;
3178 dstyinc = tmpxy;
3179 dstyinc = dstyinc * -1;
3181 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
3183 /* I don't think we need to do anything about this flag. */
3184 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
3186 dbuf = dTopLeft;
3187 flags &= ~(WINED3D_BLT_FX);
3190 #define COPY_COLORKEY_FX(type) \
3191 do { \
3192 const type *s; \
3193 type *d = (type *)dbuf, *dx, tmp; \
3194 for (y = sy = 0; y < dst_height; ++y, sy += yinc) \
3196 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
3197 dx = d; \
3198 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3200 tmp = s[sx >> 16]; \
3201 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
3202 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
3204 dx[0] = tmp; \
3206 dx = (type *)(((BYTE *)dx) + dstxinc); \
3208 d = (type *)(((BYTE *)d) + dstyinc); \
3210 } while(0)
3212 switch (bpp)
3214 case 1:
3215 COPY_COLORKEY_FX(BYTE);
3216 break;
3217 case 2:
3218 COPY_COLORKEY_FX(WORD);
3219 break;
3220 case 4:
3221 COPY_COLORKEY_FX(DWORD);
3222 break;
3223 case 3:
3225 const BYTE *s;
3226 BYTE *d = dbuf, *dx;
3227 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3229 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3230 dx = d;
3231 for (x = sx = 0; x < dst_width; ++x, sx+= xinc)
3233 DWORD pixel, dpixel = 0;
3234 s = sbuf + 3 * (sx>>16);
3235 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3236 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
3237 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
3238 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
3240 dx[0] = (pixel ) & 0xff;
3241 dx[1] = (pixel >> 8) & 0xff;
3242 dx[2] = (pixel >> 16) & 0xff;
3244 dx += dstxinc;
3246 d += dstyinc;
3248 break;
3250 default:
3251 FIXME("%s color-keyed blit not implemented for bpp %u.\n",
3252 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
3253 hr = WINED3DERR_NOTAVAILABLE;
3254 goto error;
3255 #undef COPY_COLORKEY_FX
3259 error:
3260 if (flags)
3261 FIXME(" Unsupported flags %#x.\n", flags);
3263 release:
3264 context_unmap_bo_address(context, &dst_data, GL_PIXEL_UNPACK_BUFFER);
3265 if (!same_sub_resource)
3266 context_unmap_bo_address(context, &src_data, GL_PIXEL_UNPACK_BUFFER);
3267 if (SUCCEEDED(hr) && dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture)
3269 SetRect(&dst_texture->swapchain->front_buffer_update,
3270 dst_box->left, dst_box->top, dst_box->right, dst_box->bottom);
3271 dst_texture->swapchain->swapchain_ops->swapchain_frontbuffer_updated(dst_texture->swapchain);
3273 if (converted_texture)
3274 wined3d_texture_decref(converted_texture);
3275 if (context)
3276 context_release(context);
3278 return hr;
3281 static void surface_cpu_blt_colour_fill(struct wined3d_rendertarget_view *view,
3282 const struct wined3d_box *box, const struct wined3d_color *colour)
3284 struct wined3d_device *device = view->resource->device;
3285 struct wined3d_context *context = NULL;
3286 struct wined3d_texture *texture;
3287 struct wined3d_bo_address data;
3288 unsigned int x, y, w, h, bpp;
3289 struct wined3d_map_desc map;
3290 DWORD map_binding;
3291 BYTE *row;
3292 DWORD c;
3294 TRACE("view %p, box %s, colour %s.\n", view, debug_box(box), debug_color(colour));
3296 if (view->format_flags & WINED3DFMT_FLAG_BLOCKS)
3298 FIXME("Not implemented for format %s.\n", debug_d3dformat(view->format->id));
3299 return;
3302 if (view->format->id != view->resource->format->id)
3303 FIXME("View format %s doesn't match resource format %s.\n",
3304 debug_d3dformat(view->format->id), debug_d3dformat(view->resource->format->id));
3306 if (view->resource->type == WINED3D_RTYPE_BUFFER)
3308 FIXME("Not implemented for buffers.\n");
3309 return;
3312 if (device->d3d_initialized)
3313 context = context_acquire(device, NULL, 0);
3315 c = wined3d_format_convert_from_float(view->format, colour);
3316 bpp = view->format->byte_count;
3317 w = box->right - box->left;
3318 h = box->bottom - box->top;
3320 texture = texture_from_resource(view->resource);
3321 map_binding = texture->resource.map_binding;
3322 if (!wined3d_texture_load_location(texture, view->sub_resource_idx, context, map_binding))
3323 ERR("Failed to load the sub-resource into %s.\n", wined3d_debug_location(map_binding));
3324 wined3d_texture_invalidate_location(texture, view->sub_resource_idx, ~map_binding);
3325 wined3d_texture_get_pitch(texture, view->sub_resource_idx % texture->level_count,
3326 &map.row_pitch, &map.slice_pitch);
3327 wined3d_texture_get_memory(texture, view->sub_resource_idx, &data, map_binding);
3328 map.data = context_map_bo_address(context, &data,
3329 texture->sub_resources[view->sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_WRITE);
3330 map.data = (BYTE *)map.data
3331 + (box->front * map.slice_pitch)
3332 + ((box->top / view->format->block_height) * map.row_pitch)
3333 + ((box->left / view->format->block_width) * view->format->block_byte_count);
3335 switch (bpp)
3337 case 1:
3338 for (x = 0; x < w; ++x)
3340 ((BYTE *)map.data)[x] = c;
3342 break;
3344 case 2:
3345 for (x = 0; x < w; ++x)
3347 ((WORD *)map.data)[x] = c;
3349 break;
3351 case 3:
3353 row = map.data;
3354 for (x = 0; x < w; ++x, row += 3)
3356 row[0] = (c ) & 0xff;
3357 row[1] = (c >> 8) & 0xff;
3358 row[2] = (c >> 16) & 0xff;
3360 break;
3362 case 4:
3363 for (x = 0; x < w; ++x)
3365 ((DWORD *)map.data)[x] = c;
3367 break;
3369 default:
3370 FIXME("Not implemented for bpp %u.\n", bpp);
3371 wined3d_resource_unmap(view->resource, view->sub_resource_idx);
3372 return;
3375 row = map.data;
3376 for (y = 1; y < h; ++y)
3378 row += map.row_pitch;
3379 memcpy(row, map.data, w * bpp);
3382 context_unmap_bo_address(context, &data, GL_PIXEL_UNPACK_BUFFER);
3383 if (context)
3384 context_release(context);
3387 static void cpu_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
3388 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
3389 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
3391 struct wined3d_color c = {depth, 0.0f, 0.0f, 0.0f};
3392 struct wined3d_rendertarget_view *view;
3393 struct wined3d_box box;
3394 unsigned int i, j;
3396 if (!rect_count)
3398 rect_count = 1;
3399 clear_rects = draw_rect;
3402 for (i = 0; i < rect_count; ++i)
3404 box.left = max(clear_rects[i].left, draw_rect->left);
3405 box.top = max(clear_rects[i].top, draw_rect->top);
3406 box.right = min(clear_rects[i].right, draw_rect->right);
3407 box.bottom = min(clear_rects[i].bottom, draw_rect->bottom);
3408 box.front = 0;
3409 box.back = 1;
3411 if (box.left >= box.right || box.top >= box.bottom)
3412 continue;
3414 if (flags & WINED3DCLEAR_TARGET)
3416 for (j = 0; j < rt_count; ++j)
3418 if ((view = fb->render_targets[j]))
3419 surface_cpu_blt_colour_fill(view, &box, colour);
3423 if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil))
3425 if ((view->format->depth_size && !(flags & WINED3DCLEAR_ZBUFFER))
3426 || (view->format->stencil_size && !(flags & WINED3DCLEAR_STENCIL)))
3427 FIXME("Clearing %#x on %s.\n", flags, debug_d3dformat(view->format->id));
3429 surface_cpu_blt_colour_fill(view, &box, &c);
3434 static DWORD cpu_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
3435 struct wined3d_context *context, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
3436 DWORD src_location, const RECT *src_rect, struct wined3d_texture *dst_texture,
3437 unsigned int dst_sub_resource_idx, DWORD dst_location, const RECT *dst_rect,
3438 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter)
3440 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
3441 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
3442 struct wined3d_blt_fx fx;
3443 DWORD flags = 0;
3445 memset(&fx, 0, sizeof(fx));
3446 switch (op)
3448 case WINED3D_BLIT_OP_COLOR_BLIT:
3449 case WINED3D_BLIT_OP_DEPTH_BLIT:
3450 case WINED3D_BLIT_OP_RAW_BLIT:
3451 break;
3452 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
3453 flags |= WINED3D_BLT_ALPHA_TEST;
3454 break;
3455 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
3456 flags |= WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_FX;
3457 fx.src_color_key = *color_key;
3458 break;
3459 default:
3460 FIXME("Unhandled op %#x.\n", op);
3461 break;
3464 if (FAILED(surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
3465 src_texture, src_sub_resource_idx, &src_box, flags, &fx, filter)))
3466 ERR("Failed to blit.\n");
3467 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
3469 return dst_location | (dst_texture->sub_resources[dst_sub_resource_idx].locations
3470 & dst_texture->resource.map_binding);
3473 static const struct wined3d_blitter_ops cpu_blitter_ops =
3475 cpu_blitter_destroy,
3476 cpu_blitter_clear,
3477 cpu_blitter_blit,
3480 struct wined3d_blitter *wined3d_cpu_blitter_create(void)
3482 struct wined3d_blitter *blitter;
3484 if (!(blitter = heap_alloc(sizeof(*blitter))))
3485 return NULL;
3487 TRACE("Created blitter %p.\n", blitter);
3489 blitter->ops = &cpu_blitter_ops;
3490 blitter->next = NULL;
3492 return blitter;
3495 HRESULT texture2d_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
3496 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
3497 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
3498 enum wined3d_texture_filter_type filter)
3500 struct wined3d_texture_sub_resource *src_sub_resource, *dst_sub_resource;
3501 struct wined3d_device *device = dst_texture->resource.device;
3502 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3503 const struct wined3d_color_key *colour_key = NULL;
3504 DWORD dst_location, valid_locations;
3505 DWORD src_ds_flags, dst_ds_flags;
3506 struct wined3d_context *context;
3507 enum wined3d_blit_op blit_op;
3508 BOOL scale, convert, resolve;
3509 RECT src_rect, dst_rect;
3511 static const DWORD simple_blit = WINED3D_BLT_SRC_CKEY
3512 | WINED3D_BLT_SRC_CKEY_OVERRIDE
3513 | WINED3D_BLT_ALPHA_TEST
3514 | WINED3D_BLT_RAW;
3516 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
3517 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
3518 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture, src_sub_resource_idx,
3519 debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
3520 TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage));
3522 if (fx)
3524 TRACE("fx %#x.\n", fx->fx);
3525 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
3526 fx->dst_color_key.color_space_low_value,
3527 fx->dst_color_key.color_space_high_value);
3528 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
3529 fx->src_color_key.color_space_low_value,
3530 fx->src_color_key.color_space_high_value);
3533 SetRect(&src_rect, src_box->left, src_box->top, src_box->right, src_box->bottom);
3534 SetRect(&dst_rect, dst_box->left, dst_box->top, dst_box->right, dst_box->bottom);
3536 if (!fx || !(fx->fx))
3537 flags &= ~WINED3D_BLT_FX;
3539 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
3540 if (flags & WINED3D_BLT_DO_NOT_WAIT)
3542 static unsigned int once;
3544 if (!once++)
3545 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
3548 flags &= ~(WINED3D_BLT_SYNCHRONOUS | WINED3D_BLT_DO_NOT_WAIT | WINED3D_BLT_WAIT);
3550 if (!device->d3d_initialized)
3552 WARN("D3D not initialized, using fallback.\n");
3553 goto cpu;
3556 if (flags & ~simple_blit)
3558 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
3559 goto fallback;
3562 src_swapchain = src_texture->swapchain;
3563 dst_swapchain = dst_texture->swapchain;
3565 /* This isn't strictly needed. FBO blits for example could deal with
3566 * cross-swapchain blits by first downloading the source to a texture
3567 * before switching to the destination context. We just have this here to
3568 * not have to deal with the issue, since cross-swapchain blits should be
3569 * rare. */
3570 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
3572 FIXME("Using fallback for cross-swapchain blit.\n");
3573 goto fallback;
3576 scale = src_box->right - src_box->left != dst_box->right - dst_box->left
3577 || src_box->bottom - src_box->top != dst_box->bottom - dst_box->top;
3578 convert = src_texture->resource.format->id != dst_texture->resource.format->id;
3579 resolve = src_texture->resource.multisample_type != dst_texture->resource.multisample_type;
3581 dst_ds_flags = dst_texture->resource.format_flags
3582 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
3583 src_ds_flags = src_texture->resource.format_flags
3584 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
3586 if (src_ds_flags || dst_ds_flags)
3588 TRACE("Depth/stencil blit.\n");
3590 if (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)
3591 dst_location = dst_texture->resource.draw_binding;
3592 else
3593 dst_location = dst_texture->resource.map_binding;
3595 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3596 valid_locations = device->blitter->ops->blitter_blit(device->blitter,
3597 WINED3D_BLIT_OP_DEPTH_BLIT, context,
3598 src_texture, src_sub_resource_idx, src_texture->resource.draw_binding, &src_rect,
3599 dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, NULL, filter);
3600 context_release(context);
3602 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
3603 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
3605 return WINED3D_OK;
3608 TRACE("Colour blit.\n");
3610 dst_sub_resource = &dst_texture->sub_resources[dst_sub_resource_idx];
3611 src_sub_resource = &src_texture->sub_resources[src_sub_resource_idx];
3613 /* In principle this would apply to depth blits as well, but we don't
3614 * implement those in the CPU blitter at the moment. */
3615 if ((dst_sub_resource->locations & dst_texture->resource.map_binding)
3616 && (src_sub_resource->locations & src_texture->resource.map_binding))
3618 if (scale)
3619 TRACE("Not doing sysmem blit because of scaling.\n");
3620 else if (convert)
3621 TRACE("Not doing sysmem blit because of format conversion.\n");
3622 else
3623 goto cpu;
3626 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
3627 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3629 colour_key = &fx->src_color_key;
3630 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
3632 else if (flags & WINED3D_BLT_SRC_CKEY)
3634 colour_key = &src_texture->async.src_blt_color_key;
3635 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
3637 else if (flags & WINED3D_BLT_ALPHA_TEST)
3639 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
3641 else if ((src_sub_resource->locations & surface_simple_locations)
3642 && !(dst_sub_resource->locations & surface_simple_locations))
3644 /* Upload */
3645 if (scale)
3646 TRACE("Not doing upload because of scaling.\n");
3647 else if (convert)
3648 TRACE("Not doing upload because of format conversion.\n");
3649 else if (dst_texture->resource.format->conv_byte_count)
3650 TRACE("Not doing upload because the destination format needs conversion.\n");
3651 else
3653 if (SUCCEEDED(texture2d_upload_from_surface(dst_texture, dst_sub_resource_idx,
3654 dst_box->left, dst_box->top, src_texture, src_sub_resource_idx, src_box)))
3656 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
3658 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3659 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx,
3660 context, dst_texture->resource.draw_binding);
3661 context_release(context);
3663 return WINED3D_OK;
3667 else if (dst_swapchain && dst_swapchain->back_buffers
3668 && dst_texture == dst_swapchain->front_buffer
3669 && src_texture == dst_swapchain->back_buffers[0])
3671 /* Use present for back -> front blits. The idea behind this is that
3672 * present is potentially faster than a blit, in particular when FBO
3673 * blits aren't available. Some ddraw applications like Half-Life and
3674 * Prince of Persia 3D use Blt() from the backbuffer to the
3675 * frontbuffer instead of doing a Flip(). D3d8 and d3d9 applications
3676 * can't blit directly to the frontbuffer. */
3677 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
3679 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
3681 /* Set the swap effect to COPY, we don't want the backbuffer to become
3682 * undefined. */
3683 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
3684 wined3d_swapchain_present(dst_swapchain, NULL, NULL,
3685 dst_swapchain->win_handle, dst_swapchain->swap_interval, 0);
3686 dst_swapchain->desc.swap_effect = swap_effect;
3688 return WINED3D_OK;
3690 else if ((flags & WINED3D_BLT_RAW) || (!scale && !convert && !resolve))
3692 blit_op = WINED3D_BLIT_OP_RAW_BLIT;
3695 if (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)
3696 dst_location = dst_texture->resource.draw_binding;
3697 else
3698 dst_location = dst_texture->resource.map_binding;
3700 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3701 valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context,
3702 src_texture, src_sub_resource_idx, src_texture->resource.draw_binding, &src_rect,
3703 dst_texture, dst_sub_resource_idx, dst_location, &dst_rect, colour_key, filter);
3704 context_release(context);
3706 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
3707 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
3709 return WINED3D_OK;
3711 fallback:
3712 /* Special cases for render targets. */
3713 if (SUCCEEDED(wined3d_texture_blt_special(dst_texture, dst_sub_resource_idx, &dst_rect,
3714 src_texture, src_sub_resource_idx, &src_rect, flags, fx, filter)))
3715 return WINED3D_OK;
3717 cpu:
3718 return surface_cpu_blt(dst_texture, dst_sub_resource_idx, dst_box,
3719 src_texture, src_sub_resource_idx, src_box, flags, fx, filter);