wined3d: Explicitly calculate the sub-resource level in read_from_framebuffer().
[wine.git] / dlls / wined3d / surface.c
blob751424d4b63298a7036b4d9b86dbc460567551ea
1 /*
2 * Copyright 1997-2000 Marcus Meissner
3 * Copyright 1998-2000 Lionel Ulmer
4 * Copyright 2000-2001 TransGaming Technologies Inc.
5 * Copyright 2002-2005 Jason Edmeades
6 * Copyright 2002-2003 Raphael Junqueira
7 * Copyright 2004 Christian Costa
8 * Copyright 2005 Oliver Stieber
9 * Copyright 2006-2011, 2013-2014 Stefan Dösinger for CodeWeavers
10 * Copyright 2007-2008 Henri Verbeet
11 * Copyright 2006-2008 Roderick Colenbrander
12 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
36 static const DWORD surface_simple_locations = WINED3D_LOCATION_SYSMEM
37 | WINED3D_LOCATION_USER_MEMORY | WINED3D_LOCATION_BUFFER;
39 struct blt_info
41 GLenum binding;
42 GLenum bind_target;
43 enum wined3d_gl_resource_type tex_type;
44 struct wined3d_vec3 texcoords[4];
47 struct float_rect
49 float l;
50 float t;
51 float r;
52 float b;
55 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
57 f->l = ((r->left * 2.0f) / w) - 1.0f;
58 f->t = ((r->top * 2.0f) / h) - 1.0f;
59 f->r = ((r->right * 2.0f) / w) - 1.0f;
60 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
63 static void texture2d_get_blt_info(const struct wined3d_texture *texture,
64 unsigned int sub_resource_idx, const RECT *rect, struct blt_info *info)
66 struct wined3d_vec3 *coords = info->texcoords;
67 struct float_rect f;
68 unsigned int level;
69 GLenum target;
70 GLsizei w, h;
72 level = sub_resource_idx % texture->level_count;
73 w = wined3d_texture_get_level_pow2_width(texture, level);
74 h = wined3d_texture_get_level_pow2_height(texture, level);
75 target = wined3d_texture_get_sub_resource_target(texture, sub_resource_idx);
77 switch (target)
79 default:
80 FIXME("Unsupported texture target %#x.\n", target);
81 /* Fall back to GL_TEXTURE_2D */
82 case GL_TEXTURE_2D:
83 info->binding = GL_TEXTURE_BINDING_2D;
84 info->bind_target = GL_TEXTURE_2D;
85 info->tex_type = WINED3D_GL_RES_TYPE_TEX_2D;
86 coords[0].x = (float)rect->left / w;
87 coords[0].y = (float)rect->top / h;
88 coords[0].z = 0.0f;
90 coords[1].x = (float)rect->right / w;
91 coords[1].y = (float)rect->top / h;
92 coords[1].z = 0.0f;
94 coords[2].x = (float)rect->left / w;
95 coords[2].y = (float)rect->bottom / h;
96 coords[2].z = 0.0f;
98 coords[3].x = (float)rect->right / w;
99 coords[3].y = (float)rect->bottom / h;
100 coords[3].z = 0.0f;
101 break;
103 case GL_TEXTURE_RECTANGLE_ARB:
104 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
105 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
106 info->tex_type = WINED3D_GL_RES_TYPE_TEX_RECT;
107 coords[0].x = rect->left; coords[0].y = rect->top; coords[0].z = 0.0f;
108 coords[1].x = rect->right; coords[1].y = rect->top; coords[1].z = 0.0f;
109 coords[2].x = rect->left; coords[2].y = rect->bottom; coords[2].z = 0.0f;
110 coords[3].x = rect->right; coords[3].y = rect->bottom; coords[3].z = 0.0f;
111 break;
113 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
114 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
115 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
116 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
117 cube_coords_float(rect, w, h, &f);
119 coords[0].x = 1.0f; coords[0].y = -f.t; coords[0].z = -f.l;
120 coords[1].x = 1.0f; coords[1].y = -f.t; coords[1].z = -f.r;
121 coords[2].x = 1.0f; coords[2].y = -f.b; coords[2].z = -f.l;
122 coords[3].x = 1.0f; coords[3].y = -f.b; coords[3].z = -f.r;
123 break;
125 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
126 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
127 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
128 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
129 cube_coords_float(rect, w, h, &f);
131 coords[0].x = -1.0f; coords[0].y = -f.t; coords[0].z = f.l;
132 coords[1].x = -1.0f; coords[1].y = -f.t; coords[1].z = f.r;
133 coords[2].x = -1.0f; coords[2].y = -f.b; coords[2].z = f.l;
134 coords[3].x = -1.0f; coords[3].y = -f.b; coords[3].z = f.r;
135 break;
137 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
138 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
139 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
140 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
141 cube_coords_float(rect, w, h, &f);
143 coords[0].x = f.l; coords[0].y = 1.0f; coords[0].z = f.t;
144 coords[1].x = f.r; coords[1].y = 1.0f; coords[1].z = f.t;
145 coords[2].x = f.l; coords[2].y = 1.0f; coords[2].z = f.b;
146 coords[3].x = f.r; coords[3].y = 1.0f; coords[3].z = f.b;
147 break;
149 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
150 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
151 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
152 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
153 cube_coords_float(rect, w, h, &f);
155 coords[0].x = f.l; coords[0].y = -1.0f; coords[0].z = -f.t;
156 coords[1].x = f.r; coords[1].y = -1.0f; coords[1].z = -f.t;
157 coords[2].x = f.l; coords[2].y = -1.0f; coords[2].z = -f.b;
158 coords[3].x = f.r; coords[3].y = -1.0f; coords[3].z = -f.b;
159 break;
161 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
162 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
163 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
164 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
165 cube_coords_float(rect, w, h, &f);
167 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1.0f;
168 coords[1].x = f.r; coords[1].y = -f.t; coords[1].z = 1.0f;
169 coords[2].x = f.l; coords[2].y = -f.b; coords[2].z = 1.0f;
170 coords[3].x = f.r; coords[3].y = -f.b; coords[3].z = 1.0f;
171 break;
173 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
174 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
175 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
176 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
177 cube_coords_float(rect, w, h, &f);
179 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1.0f;
180 coords[1].x = -f.r; coords[1].y = -f.t; coords[1].z = -1.0f;
181 coords[2].x = -f.l; coords[2].y = -f.b; coords[2].z = -1.0f;
182 coords[3].x = -f.r; coords[3].y = -f.b; coords[3].z = -1.0f;
183 break;
187 /* Context activation is done by the caller. */
188 void draw_textured_quad(struct wined3d_texture *texture, unsigned int sub_resource_idx,
189 struct wined3d_context *context, const RECT *src_rect, const RECT *dst_rect,
190 enum wined3d_texture_filter_type filter)
192 const struct wined3d_gl_info *gl_info = context->gl_info;
193 struct blt_info info;
195 texture2d_get_blt_info(texture, sub_resource_idx, src_rect, &info);
197 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
198 checkGLcall("glEnable(bind_target)");
200 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
202 /* Filtering for StretchRect */
203 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
204 checkGLcall("glTexParameteri");
205 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
206 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
207 checkGLcall("glTexParameteri");
208 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
209 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
210 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
211 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
212 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
213 checkGLcall("glTexEnvi");
215 /* Draw a quad */
216 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
217 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[0].x);
218 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
220 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[1].x);
221 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
223 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[2].x);
224 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
226 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[3].x);
227 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
228 gl_info->gl_ops.gl.p_glEnd();
230 /* Unbind the texture */
231 context_bind_texture(context, info.bind_target, 0);
233 /* We changed the filtering settings on the texture. Inform the
234 * container about this to get the filters reset properly next draw. */
235 texture->texture_rgb.sampler_desc.mag_filter = WINED3D_TEXF_POINT;
236 texture->texture_rgb.sampler_desc.min_filter = WINED3D_TEXF_POINT;
237 texture->texture_rgb.sampler_desc.mip_filter = WINED3D_TEXF_NONE;
238 texture->texture_rgb.sampler_desc.srgb_decode = FALSE;
241 /* Works correctly only for <= 4 bpp formats. */
242 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
244 masks[0] = ((1u << format->red_size) - 1) << format->red_offset;
245 masks[1] = ((1u << format->green_size) - 1) << format->green_offset;
246 masks[2] = ((1u << format->blue_size) - 1) << format->blue_offset;
249 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
251 unsigned int t;
253 t = wined3d_texture_get_level_width(surface->container, surface->texture_level);
254 if ((r->left && r->right) || abs(r->right - r->left) != t)
255 return FALSE;
256 t = wined3d_texture_get_level_height(surface->container, surface->texture_level);
257 if ((r->top && r->bottom) || abs(r->bottom - r->top) != t)
258 return FALSE;
259 return TRUE;
262 static void surface_depth_blt_fbo(const struct wined3d_device *device,
263 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
264 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
266 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
267 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
268 struct wined3d_texture *dst_texture = dst_surface->container;
269 struct wined3d_texture *src_texture = src_surface->container;
270 const struct wined3d_gl_info *gl_info;
271 struct wined3d_context *context;
272 DWORD src_mask, dst_mask;
273 GLbitfield gl_mask;
275 TRACE("device %p\n", device);
276 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
277 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
278 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
279 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
281 src_mask = src_texture->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
282 dst_mask = dst_texture->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
284 if (src_mask != dst_mask)
286 ERR("Incompatible formats %s and %s.\n",
287 debug_d3dformat(src_texture->resource.format->id),
288 debug_d3dformat(dst_texture->resource.format->id));
289 return;
292 if (!src_mask)
294 ERR("Not a depth / stencil format: %s.\n",
295 debug_d3dformat(src_texture->resource.format->id));
296 return;
299 gl_mask = 0;
300 if (src_mask & WINED3DFMT_FLAG_DEPTH)
301 gl_mask |= GL_DEPTH_BUFFER_BIT;
302 if (src_mask & WINED3DFMT_FLAG_STENCIL)
303 gl_mask |= GL_STENCIL_BUFFER_BIT;
305 context = context_acquire(device, NULL, 0);
306 if (!context->valid)
308 context_release(context);
309 WARN("Invalid context, skipping blit.\n");
310 return;
313 /* Make sure the locations are up-to-date. Loading the destination
314 * surface isn't required if the entire surface is overwritten. */
315 wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, src_location);
316 if (!surface_is_full_rect(dst_surface, dst_rect))
317 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
318 else
319 wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, dst_location);
321 gl_info = context->gl_info;
323 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
324 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
326 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
327 context_set_draw_buffer(context, GL_NONE);
328 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
329 context_invalidate_state(context, STATE_FRAMEBUFFER);
331 if (gl_mask & GL_DEPTH_BUFFER_BIT)
333 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
334 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
336 if (gl_mask & GL_STENCIL_BUFFER_BIT)
338 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
340 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
341 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
343 gl_info->gl_ops.gl.p_glStencilMask(~0U);
344 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
347 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
348 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
350 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
351 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
352 checkGLcall("glBlitFramebuffer()");
354 if (wined3d_settings.strict_draw_ordering)
355 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
357 context_release(context);
360 static BOOL is_multisample_location(const struct wined3d_texture *texture, DWORD location)
362 if (location == WINED3D_LOCATION_RB_MULTISAMPLE)
363 return TRUE;
364 if (location != WINED3D_LOCATION_TEXTURE_RGB && location != WINED3D_LOCATION_TEXTURE_SRGB)
365 return FALSE;
366 return texture->target == GL_TEXTURE_2D_MULTISAMPLE || texture->target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
369 /* Blit between surface locations. Onscreen on different swapchains is not supported.
370 * Depth / stencil is not supported. Context activation is done by the caller. */
371 static void surface_blt_fbo(const struct wined3d_device *device,
372 struct wined3d_context *old_ctx, enum wined3d_texture_filter_type filter,
373 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
374 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
376 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
377 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
378 struct wined3d_texture *dst_texture = dst_surface->container;
379 struct wined3d_texture *src_texture = src_surface->container;
380 const struct wined3d_gl_info *gl_info;
381 struct wined3d_context *context = old_ctx;
382 struct wined3d_surface *required_rt, *restore_rt = NULL;
383 RECT src_rect, dst_rect;
384 GLenum gl_filter;
385 GLenum buffer;
387 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
388 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
389 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
390 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
391 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
393 src_rect = *src_rect_in;
394 dst_rect = *dst_rect_in;
396 switch (filter)
398 case WINED3D_TEXF_LINEAR:
399 gl_filter = GL_LINEAR;
400 break;
402 default:
403 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
404 case WINED3D_TEXF_NONE:
405 case WINED3D_TEXF_POINT:
406 gl_filter = GL_NEAREST;
407 break;
410 /* Resolve the source surface first if needed. */
411 if (is_multisample_location(src_texture, src_location)
412 && (src_texture->resource.format->id != dst_texture->resource.format->id
413 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
414 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
415 src_location = WINED3D_LOCATION_RB_RESOLVED;
417 /* Make sure the locations are up-to-date. Loading the destination
418 * surface isn't required if the entire surface is overwritten. (And is
419 * in fact harmful if we're being called by surface_load_location() with
420 * the purpose of loading the destination surface.) */
421 wined3d_texture_load_location(src_texture, src_sub_resource_idx, old_ctx, src_location);
422 if (!surface_is_full_rect(dst_surface, &dst_rect))
423 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, old_ctx, dst_location);
424 else
425 wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, old_ctx, dst_location);
428 if (src_location == WINED3D_LOCATION_DRAWABLE) required_rt = src_surface;
429 else if (dst_location == WINED3D_LOCATION_DRAWABLE) required_rt = dst_surface;
430 else required_rt = NULL;
432 restore_rt = context_get_rt_surface(old_ctx);
433 if (restore_rt != required_rt)
434 context = context_acquire(device, required_rt ? required_rt->container : NULL,
435 required_rt ? surface_get_sub_resource_idx(required_rt) : 0);
436 else
437 restore_rt = NULL;
439 if (!context->valid)
441 context_release(context);
442 WARN("Invalid context, skipping blit.\n");
443 return;
446 gl_info = context->gl_info;
448 if (src_location == WINED3D_LOCATION_DRAWABLE)
450 TRACE("Source surface %p is onscreen.\n", src_surface);
451 buffer = wined3d_texture_get_gl_buffer(src_texture);
452 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
454 else
456 TRACE("Source surface %p is offscreen.\n", src_surface);
457 buffer = GL_COLOR_ATTACHMENT0;
460 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
461 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
462 checkGLcall("glReadBuffer()");
463 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
465 if (dst_location == WINED3D_LOCATION_DRAWABLE)
467 TRACE("Destination surface %p is onscreen.\n", dst_surface);
468 buffer = wined3d_texture_get_gl_buffer(dst_texture);
469 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
471 else
473 TRACE("Destination surface %p is offscreen.\n", dst_surface);
474 buffer = GL_COLOR_ATTACHMENT0;
477 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
478 context_set_draw_buffer(context, buffer);
479 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
480 context_invalidate_state(context, STATE_FRAMEBUFFER);
482 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
483 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
484 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
485 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
486 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
488 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
489 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
491 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
492 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
493 checkGLcall("glBlitFramebuffer()");
495 if (wined3d_settings.strict_draw_ordering || (dst_location == WINED3D_LOCATION_DRAWABLE
496 && dst_texture->swapchain->front_buffer == dst_texture))
497 gl_info->gl_ops.gl.p_glFlush();
499 if (restore_rt)
500 context_restore(context, restore_rt);
503 static BOOL fbo_blitter_supported(enum wined3d_blit_op blit_op, const struct wined3d_gl_info *gl_info,
504 const struct wined3d_resource *src_resource, DWORD src_location,
505 const struct wined3d_resource *dst_resource, DWORD dst_location)
507 const struct wined3d_format *src_format = src_resource->format;
508 const struct wined3d_format *dst_format = dst_resource->format;
510 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
511 return FALSE;
513 /* Source and/or destination need to be on the GL side */
514 if (!(src_resource->access & dst_resource->access & WINED3D_RESOURCE_ACCESS_GPU))
515 return FALSE;
517 switch (blit_op)
519 case WINED3D_BLIT_OP_COLOR_BLIT:
520 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
521 || (src_resource->usage & WINED3DUSAGE_RENDERTARGET)))
522 return FALSE;
523 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
524 || (dst_resource->usage & WINED3DUSAGE_RENDERTARGET)))
525 return FALSE;
526 if ((src_format->id != dst_format->id || dst_location == WINED3D_LOCATION_DRAWABLE)
527 && (!is_identity_fixup(src_format->color_fixup) || !is_identity_fixup(dst_format->color_fixup)))
528 return FALSE;
529 break;
531 case WINED3D_BLIT_OP_DEPTH_BLIT:
532 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
533 return FALSE;
534 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
535 return FALSE;
536 /* Accept pure swizzle fixups for depth formats. In general we
537 * ignore the stencil component (if present) at the moment and the
538 * swizzle is not relevant with just the depth component. */
539 if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup)
540 || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup))
541 return FALSE;
542 break;
544 default:
545 return FALSE;
548 return TRUE;
551 /* This call just downloads data, the caller is responsible for binding the
552 * correct texture. */
553 /* Context activation is done by the caller. */
554 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
555 DWORD dst_location)
557 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
558 struct wined3d_texture *texture = surface->container;
559 const struct wined3d_format *format = texture->resource.format;
560 struct wined3d_texture_sub_resource *sub_resource;
561 unsigned int dst_row_pitch, dst_slice_pitch;
562 unsigned int src_row_pitch, src_slice_pitch;
563 struct wined3d_bo_address data;
564 BYTE *temporary_mem = NULL;
565 unsigned int level;
566 GLenum target;
567 void *mem;
569 /* Only support read back of converted P8 surfaces. */
570 if (texture->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT && !format->download)
572 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
573 return;
576 sub_resource = &texture->sub_resources[sub_resource_idx];
577 target = wined3d_texture_get_sub_resource_target(texture, sub_resource_idx);
578 level = sub_resource_idx % texture->level_count;
580 if (target == GL_TEXTURE_2D_ARRAY)
582 if (format->download)
584 FIXME("Reading back converted array texture %p is not supported.\n", texture);
585 return;
588 /* NP2 emulation is not allowed on array textures. */
589 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
590 ERR("Array texture %p uses NP2 emulation.\n", texture);
592 WARN_(d3d_perf)("Downloading all miplevel layers to get the surface data for a single sub-resource.\n");
594 if (!(temporary_mem = heap_calloc(texture->layer_count, sub_resource->size)))
596 ERR("Out of memory.\n");
597 return;
601 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
603 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
605 if (format->download)
607 FIXME("Reading back converted texture %p with NP2 emulation is not supported.\n", texture);
608 return;
611 wined3d_texture_get_pitch(texture, level, &dst_row_pitch, &dst_slice_pitch);
612 wined3d_format_calculate_pitch(format, texture->resource.device->surface_alignment,
613 wined3d_texture_get_level_pow2_width(texture, level),
614 wined3d_texture_get_level_pow2_height(texture, level),
615 &src_row_pitch, &src_slice_pitch);
616 if (!(temporary_mem = heap_alloc(src_slice_pitch)))
618 ERR("Out of memory.\n");
619 return;
622 if (data.buffer_object)
623 ERR("NP2 emulated texture uses PBO unexpectedly.\n");
624 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
625 ERR("Unexpected compressed format for NP2 emulated texture.\n");
628 if (format->download)
630 struct wined3d_format f;
632 if (data.buffer_object)
633 ERR("Converted texture %p uses PBO unexpectedly.\n", texture);
635 WARN_(d3d_perf)("Downloading converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
637 f = *format;
638 f.byte_count = format->conv_byte_count;
639 wined3d_texture_get_pitch(texture, level, &dst_row_pitch, &dst_slice_pitch);
640 wined3d_format_calculate_pitch(&f, texture->resource.device->surface_alignment,
641 wined3d_texture_get_level_width(texture, level),
642 wined3d_texture_get_level_height(texture, level),
643 &src_row_pitch, &src_slice_pitch);
645 if (!(temporary_mem = heap_alloc(src_slice_pitch)))
647 ERR("Failed to allocate memory.\n");
648 return;
652 if (temporary_mem)
654 mem = temporary_mem;
656 else if (data.buffer_object)
658 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
659 checkGLcall("glBindBuffer");
660 mem = data.addr;
662 else
664 mem = data.addr;
667 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
669 TRACE("Downloading compressed surface %p, level %u, format %#x, type %#x, data %p.\n",
670 surface, level, format->glFormat, format->glType, mem);
672 GL_EXTCALL(glGetCompressedTexImage(target, level, mem));
673 checkGLcall("glGetCompressedTexImage");
675 else
677 TRACE("Downloading surface %p, level %u, format %#x, type %#x, data %p.\n",
678 surface, level, format->glFormat, format->glType, mem);
680 gl_info->gl_ops.gl.p_glGetTexImage(target, level, format->glFormat, format->glType, mem);
681 checkGLcall("glGetTexImage");
684 if (format->download)
686 format->download(mem, data.addr, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch,
687 wined3d_texture_get_level_width(texture, level),
688 wined3d_texture_get_level_height(texture, level), 1);
690 else if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
692 const BYTE *src_data;
693 unsigned int h, y;
694 BYTE *dst_data;
696 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
697 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
698 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
700 * We're doing this...
702 * instead of boxing the texture :
703 * |<-texture width ->| -->pow2width| /\
704 * |111111111111111111| | |
705 * |222 Texture 222222| boxed empty | texture height
706 * |3333 Data 33333333| | |
707 * |444444444444444444| | \/
708 * ----------------------------------- |
709 * | boxed empty | boxed empty | pow2height
710 * | | | \/
711 * -----------------------------------
714 * we're repacking the data to the expected texture width
716 * |<-texture width ->| -->pow2width| /\
717 * |111111111111111111222222222222222| |
718 * |222333333333333333333444444444444| texture height
719 * |444444 | |
720 * | | \/
721 * | | |
722 * | empty | pow2height
723 * | | \/
724 * -----------------------------------
726 * == is the same as
728 * |<-texture width ->| /\
729 * |111111111111111111|
730 * |222222222222222222|texture height
731 * |333333333333333333|
732 * |444444444444444444| \/
733 * --------------------
735 * This also means that any references to surface memory should work with the data as if it were a
736 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
738 * internally the texture is still stored in a boxed format so any references to textureName will
739 * get a boxed texture with width pow2width and not a texture of width resource.width. */
740 src_data = mem;
741 dst_data = data.addr;
742 TRACE("Repacking the surface data from pitch %u to pitch %u.\n", src_row_pitch, dst_row_pitch);
743 h = wined3d_texture_get_level_height(texture, level);
744 for (y = 0; y < h; ++y)
746 memcpy(dst_data, src_data, dst_row_pitch);
747 src_data += src_row_pitch;
748 dst_data += dst_row_pitch;
751 else if (temporary_mem)
753 unsigned int layer = sub_resource_idx / texture->level_count;
754 void *src_data = temporary_mem + layer * sub_resource->size;
755 if (data.buffer_object)
757 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
758 checkGLcall("glBindBuffer");
759 GL_EXTCALL(glBufferSubData(GL_PIXEL_PACK_BUFFER, 0, sub_resource->size, src_data));
760 checkGLcall("glBufferSubData");
762 else
764 memcpy(data.addr, src_data, sub_resource->size);
768 if (data.buffer_object)
770 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
771 checkGLcall("glBindBuffer");
774 heap_free(temporary_mem);
777 /* This call just uploads data, the caller is responsible for binding the
778 * correct texture. */
779 /* Context activation is done by the caller. */
780 void wined3d_surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
781 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
782 BOOL srgb, const struct wined3d_const_bo_address *data)
784 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
785 struct wined3d_texture *texture = surface->container;
786 UINT update_w = src_rect->right - src_rect->left;
787 UINT update_h = src_rect->bottom - src_rect->top;
788 unsigned int level, layer;
789 GLenum target;
791 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
792 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
793 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
795 if (texture->sub_resources[sub_resource_idx].map_count)
797 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
798 texture->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
801 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE)
803 update_h *= format->height_scale.numerator;
804 update_h /= format->height_scale.denominator;
807 if (data->buffer_object)
809 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
810 checkGLcall("glBindBuffer");
813 target = wined3d_texture_get_sub_resource_target(texture, sub_resource_idx);
814 level = sub_resource_idx % texture->level_count;
815 layer = sub_resource_idx / texture->level_count;
817 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
819 unsigned int dst_row_pitch, dst_slice_pitch;
820 const BYTE *addr = data->addr;
821 GLenum internal;
823 addr += (src_rect->top / format->block_height) * src_pitch;
824 addr += (src_rect->left / format->block_width) * format->block_byte_count;
826 if (srgb)
827 internal = format->glGammaInternal;
828 else if (texture->resource.usage & WINED3DUSAGE_RENDERTARGET
829 && wined3d_resource_is_offscreen(&texture->resource))
830 internal = format->rtInternal;
831 else
832 internal = format->glInternal;
834 wined3d_format_calculate_pitch(format, 1, update_w, update_h, &dst_row_pitch, &dst_slice_pitch);
836 TRACE("Uploading compressed data, target %#x, level %u, layer %u, x %d, y %d, w %u, h %u, "
837 "format %#x, image_size %#x, addr %p.\n",
838 target, level, layer, dst_point->x, dst_point->y,
839 update_w, update_h, internal, dst_slice_pitch, addr);
841 if (dst_row_pitch == src_pitch)
843 if (target == GL_TEXTURE_2D_ARRAY)
845 GL_EXTCALL(glCompressedTexSubImage3D(target, level, dst_point->x, dst_point->y,
846 layer, update_w, update_h, 1, internal, dst_slice_pitch, addr));
848 else
850 GL_EXTCALL(glCompressedTexSubImage2D(target, level, dst_point->x, dst_point->y,
851 update_w, update_h, internal, dst_slice_pitch, addr));
854 else
856 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
857 UINT row, y;
859 /* glCompressedTexSubImage2D() ignores pixel store state, so we
860 * can't use the unpack row length like for glTexSubImage2D. */
861 for (row = 0, y = dst_point->y; row < row_count; ++row)
863 if (target == GL_TEXTURE_2D_ARRAY)
865 GL_EXTCALL(glCompressedTexSubImage3D(target, level, dst_point->x, y,
866 layer, update_w, format->block_height, 1, internal, dst_row_pitch, addr));
868 else
870 GL_EXTCALL(glCompressedTexSubImage2D(target, level, dst_point->x, y,
871 update_w, format->block_height, internal, dst_row_pitch, addr));
874 y += format->block_height;
875 addr += src_pitch;
878 checkGLcall("Upload compressed surface data");
880 else
882 const BYTE *addr = data->addr;
884 addr += src_rect->top * src_pitch;
885 addr += src_rect->left * format->byte_count;
887 TRACE("Uploading data, target %#x, level %u, layer %u, x %d, y %d, w %u, h %u, "
888 "format %#x, type %#x, addr %p.\n",
889 target, level, layer, dst_point->x, dst_point->y,
890 update_w, update_h, format->glFormat, format->glType, addr);
892 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
893 if (target == GL_TEXTURE_2D_ARRAY)
895 GL_EXTCALL(glTexSubImage3D(target, level, dst_point->x, dst_point->y,
896 layer, update_w, update_h, 1, format->glFormat, format->glType, addr));
898 else
900 gl_info->gl_ops.gl.p_glTexSubImage2D(target, level, dst_point->x, dst_point->y,
901 update_w, update_h, format->glFormat, format->glType, addr);
903 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
904 checkGLcall("Upload surface data");
907 if (data->buffer_object)
909 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
910 checkGLcall("glBindBuffer");
913 if (wined3d_settings.strict_draw_ordering)
914 gl_info->gl_ops.gl.p_glFlush();
916 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
918 struct wined3d_device *device = texture->resource.device;
919 unsigned int i;
921 for (i = 0; i < device->context_count; ++i)
923 context_surface_update(device->contexts[i], surface);
928 static HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
929 struct wined3d_surface *src_surface, const RECT *src_rect)
931 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
932 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
933 struct wined3d_texture *src_texture = src_surface->container;
934 struct wined3d_texture *dst_texture = dst_surface->container;
935 unsigned int src_row_pitch, src_slice_pitch;
936 const struct wined3d_gl_info *gl_info;
937 unsigned int src_level, dst_level;
938 struct wined3d_context *context;
939 struct wined3d_bo_address data;
940 UINT update_w, update_h;
942 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
943 dst_surface, wine_dbgstr_point(dst_point),
944 src_surface, wine_dbgstr_rect(src_rect));
946 context = context_acquire(dst_texture->resource.device, NULL, 0);
947 gl_info = context->gl_info;
949 /* Only load the surface for partial updates. For newly allocated texture
950 * the texture wouldn't be the current location, and we'd upload zeroes
951 * just to overwrite them again. */
952 update_w = src_rect->right - src_rect->left;
953 update_h = src_rect->bottom - src_rect->top;
954 dst_level = dst_sub_resource_idx % dst_texture->level_count;
955 if (update_w == wined3d_texture_get_level_width(dst_texture, dst_level)
956 && update_h == wined3d_texture_get_level_height(dst_texture, dst_level))
957 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
958 else
959 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
960 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
962 src_level = src_sub_resource_idx % src_texture->level_count;
963 wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &data,
964 src_texture->sub_resources[src_sub_resource_idx].locations);
965 wined3d_texture_get_pitch(src_texture, src_level, &src_row_pitch, &src_slice_pitch);
967 wined3d_surface_upload_data(dst_surface, gl_info, src_texture->resource.format, src_rect,
968 src_row_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
970 context_release(context);
972 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
973 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
975 return WINED3D_OK;
978 /* In D3D the depth stencil dimensions have to be greater than or equal to the
979 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
980 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
981 /* Context activation is done by the caller. */
982 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_rendertarget_info *rt)
984 unsigned int sub_resource_idx, width, height, level;
985 struct wined3d_renderbuffer_entry *entry;
986 const struct wined3d_texture *texture;
987 const struct wined3d_gl_info *gl_info;
988 unsigned int src_width, src_height;
989 GLuint renderbuffer = 0;
991 texture = surface->container;
992 gl_info = &texture->resource.device->adapter->gl_info;
993 sub_resource_idx = surface_get_sub_resource_idx(surface);
994 level = sub_resource_idx % texture->level_count;
996 if (rt && rt->resource->format->id != WINED3DFMT_NULL)
998 struct wined3d_texture *rt_texture;
999 unsigned int rt_level;
1001 if (rt->resource->type == WINED3D_RTYPE_BUFFER)
1003 FIXME("Unsupported resource type %s.\n", debug_d3dresourcetype(rt->resource->type));
1004 return;
1006 rt_texture = wined3d_texture_from_resource(rt->resource);
1007 rt_level = rt->sub_resource_idx % rt_texture->level_count;
1009 width = wined3d_texture_get_level_pow2_width(rt_texture, rt_level);
1010 height = wined3d_texture_get_level_pow2_height(rt_texture, rt_level);
1012 else
1014 width = wined3d_texture_get_level_pow2_width(texture, level);
1015 height = wined3d_texture_get_level_pow2_height(texture, level);
1018 src_width = wined3d_texture_get_level_pow2_width(texture, level);
1019 src_height = wined3d_texture_get_level_pow2_height(texture, level);
1021 /* A depth stencil smaller than the render target is not valid */
1022 if (width > src_width || height > src_height) return;
1024 /* Remove any renderbuffer set if the sizes match */
1025 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1026 || (width == src_width && height == src_height))
1028 surface->current_renderbuffer = NULL;
1029 return;
1032 /* Look if we've already got a renderbuffer of the correct dimensions */
1033 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1035 if (entry->width == width && entry->height == height)
1037 renderbuffer = entry->id;
1038 surface->current_renderbuffer = entry;
1039 break;
1043 if (!renderbuffer)
1045 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1046 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1047 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1048 texture->resource.format->glInternal, width, height);
1050 entry = heap_alloc(sizeof(*entry));
1051 entry->width = width;
1052 entry->height = height;
1053 entry->id = renderbuffer;
1054 list_add_head(&surface->renderbuffers, &entry->entry);
1056 surface->current_renderbuffer = entry;
1059 checkGLcall("set_compatible_renderbuffer");
1062 /* See also float_16_to_32() in wined3d_private.h */
1063 static inline unsigned short float_32_to_16(const float *in)
1065 int exp = 0;
1066 float tmp = fabsf(*in);
1067 unsigned int mantissa;
1068 unsigned short ret;
1070 /* Deal with special numbers */
1071 if (*in == 0.0f)
1072 return 0x0000;
1073 if (isnan(*in))
1074 return 0x7c01;
1075 if (isinf(*in))
1076 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1078 if (tmp < (float)(1u << 10))
1082 tmp = tmp * 2.0f;
1083 exp--;
1084 } while (tmp < (float)(1u << 10));
1086 else if (tmp >= (float)(1u << 11))
1090 tmp /= 2.0f;
1091 exp++;
1092 } while (tmp >= (float)(1u << 11));
1095 mantissa = (unsigned int)tmp;
1096 if (tmp - mantissa >= 0.5f)
1097 ++mantissa; /* Round to nearest, away from zero. */
1099 exp += 10; /* Normalize the mantissa. */
1100 exp += 15; /* Exponent is encoded with excess 15. */
1102 if (exp > 30) /* too big */
1104 ret = 0x7c00; /* INF */
1106 else if (exp <= 0)
1108 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1109 while (exp <= 0)
1111 mantissa = mantissa >> 1;
1112 ++exp;
1114 ret = mantissa & 0x3ff;
1116 else
1118 ret = (exp << 10) | (mantissa & 0x3ff);
1121 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1122 return ret;
1125 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
1126 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1128 unsigned short *dst_s;
1129 const float *src_f;
1130 unsigned int x, y;
1132 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1134 for (y = 0; y < h; ++y)
1136 src_f = (const float *)(src + y * pitch_in);
1137 dst_s = (unsigned short *) (dst + y * pitch_out);
1138 for (x = 0; x < w; ++x)
1140 dst_s[x] = float_32_to_16(src_f + x);
1145 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
1146 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1148 static const unsigned char convert_5to8[] =
1150 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
1151 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
1152 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
1153 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
1155 static const unsigned char convert_6to8[] =
1157 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
1158 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
1159 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
1160 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
1161 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
1162 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
1163 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
1164 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
1166 unsigned int x, y;
1168 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1170 for (y = 0; y < h; ++y)
1172 const WORD *src_line = (const WORD *)(src + y * pitch_in);
1173 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1174 for (x = 0; x < w; ++x)
1176 WORD pixel = src_line[x];
1177 dst_line[x] = 0xff000000u
1178 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
1179 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
1180 | convert_5to8[(pixel & 0x001fu)];
1185 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
1186 * in both cases we're just setting the X / Alpha channel to 0xff. */
1187 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
1188 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1190 unsigned int x, y;
1192 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1194 for (y = 0; y < h; ++y)
1196 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
1197 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1199 for (x = 0; x < w; ++x)
1201 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
1206 static inline BYTE cliptobyte(int x)
1208 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
1211 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
1212 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1214 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1215 unsigned int x, y;
1217 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1219 for (y = 0; y < h; ++y)
1221 const BYTE *src_line = src + y * pitch_in;
1222 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1223 for (x = 0; x < w; ++x)
1225 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1226 * C = Y - 16; D = U - 128; E = V - 128;
1227 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1228 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1229 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1230 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1231 * U and V are shared between the pixels. */
1232 if (!(x & 1)) /* For every even pixel, read new U and V. */
1234 d = (int) src_line[1] - 128;
1235 e = (int) src_line[3] - 128;
1236 r2 = 409 * e + 128;
1237 g2 = - 100 * d - 208 * e + 128;
1238 b2 = 516 * d + 128;
1240 c2 = 298 * ((int) src_line[0] - 16);
1241 dst_line[x] = 0xff000000
1242 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
1243 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
1244 | cliptobyte((c2 + b2) >> 8); /* blue */
1245 /* Scale RGB values to 0..255 range,
1246 * then clip them if still not in range (may be negative),
1247 * then shift them within DWORD if necessary. */
1248 src_line += 2;
1253 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
1254 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1256 unsigned int x, y;
1257 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1259 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
1261 for (y = 0; y < h; ++y)
1263 const BYTE *src_line = src + y * pitch_in;
1264 WORD *dst_line = (WORD *)(dst + y * pitch_out);
1265 for (x = 0; x < w; ++x)
1267 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1268 * C = Y - 16; D = U - 128; E = V - 128;
1269 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1270 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1271 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1272 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1273 * U and V are shared between the pixels. */
1274 if (!(x & 1)) /* For every even pixel, read new U and V. */
1276 d = (int) src_line[1] - 128;
1277 e = (int) src_line[3] - 128;
1278 r2 = 409 * e + 128;
1279 g2 = - 100 * d - 208 * e + 128;
1280 b2 = 516 * d + 128;
1282 c2 = 298 * ((int) src_line[0] - 16);
1283 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
1284 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
1285 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
1286 /* Scale RGB values to 0..255 range,
1287 * then clip them if still not in range (may be negative),
1288 * then shift them within DWORD if necessary. */
1289 src_line += 2;
1294 struct d3dfmt_converter_desc
1296 enum wined3d_format_id from, to;
1297 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
1300 static const struct d3dfmt_converter_desc converters[] =
1302 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
1303 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
1304 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1305 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1306 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
1307 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
1310 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
1311 enum wined3d_format_id to)
1313 unsigned int i;
1315 for (i = 0; i < ARRAY_SIZE(converters); ++i)
1317 if (converters[i].from == from && converters[i].to == to)
1318 return &converters[i];
1321 return NULL;
1324 static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture,
1325 unsigned int sub_resource_idx, const struct wined3d_format *dst_format)
1327 unsigned int texture_level = sub_resource_idx % src_texture->level_count;
1328 const struct wined3d_format *src_format = src_texture->resource.format;
1329 struct wined3d_device *device = src_texture->resource.device;
1330 const struct d3dfmt_converter_desc *conv = NULL;
1331 const struct wined3d_gl_info *gl_info = NULL;
1332 unsigned int src_row_pitch, src_slice_pitch;
1333 struct wined3d_context *context = NULL;
1334 struct wined3d_texture *dst_texture;
1335 struct wined3d_bo_address src_data;
1336 struct wined3d_resource_desc desc;
1337 DWORD map_binding;
1339 if (!(conv = find_converter(src_format->id, dst_format->id)) && (!device->d3d_initialized
1340 || !is_identity_fixup(src_format->color_fixup) || src_format->conv_byte_count
1341 || !is_identity_fixup(dst_format->color_fixup) || dst_format->conv_byte_count
1342 || (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)))
1344 FIXME("Cannot find a conversion function from format %s to %s.\n",
1345 debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
1346 return NULL;
1349 /* FIXME: Multisampled conversion? */
1350 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
1351 desc.format = dst_format->id;
1352 desc.multisample_type = WINED3D_MULTISAMPLE_NONE;
1353 desc.multisample_quality = 0;
1354 desc.usage = WINED3DUSAGE_SCRATCH | WINED3DUSAGE_PRIVATE;
1355 desc.access = WINED3D_RESOURCE_ACCESS_CPU | WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W;
1356 desc.width = wined3d_texture_get_level_width(src_texture, texture_level);
1357 desc.height = wined3d_texture_get_level_height(src_texture, texture_level);
1358 desc.depth = 1;
1359 desc.size = 0;
1360 if (FAILED(wined3d_texture_create(device, &desc, 1, 1,
1361 WINED3D_TEXTURE_CREATE_MAPPABLE | WINED3D_TEXTURE_CREATE_DISCARD,
1362 NULL, NULL, &wined3d_null_parent_ops, &dst_texture)))
1364 ERR("Failed to create a destination texture for conversion.\n");
1365 return NULL;
1368 if (device->d3d_initialized)
1370 context = context_acquire(device, NULL, 0);
1371 gl_info = context->gl_info;
1374 map_binding = src_texture->resource.map_binding;
1375 if (!wined3d_texture_load_location(src_texture, sub_resource_idx, context, map_binding))
1376 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
1377 wined3d_texture_get_pitch(src_texture, texture_level, &src_row_pitch, &src_slice_pitch);
1378 wined3d_texture_get_memory(src_texture, sub_resource_idx, &src_data, map_binding);
1380 if (conv)
1382 unsigned int dst_row_pitch, dst_slice_pitch;
1383 struct wined3d_bo_address dst_data;
1384 const BYTE *src;
1385 BYTE *dst;
1387 map_binding = dst_texture->resource.map_binding;
1388 if (!wined3d_texture_load_location(dst_texture, 0, context, map_binding))
1389 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
1390 wined3d_texture_get_pitch(dst_texture, 0, &dst_row_pitch, &dst_slice_pitch);
1391 wined3d_texture_get_memory(dst_texture, 0, &dst_data, map_binding);
1393 src = context_map_bo_address(context, &src_data,
1394 src_texture->sub_resources[sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ);
1395 dst = context_map_bo_address(context,
1396 &dst_data, dst_texture->sub_resources[0].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_WRITE);
1398 conv->convert(src, dst, src_row_pitch, dst_row_pitch, desc.width, desc.height);
1400 wined3d_texture_invalidate_location(dst_texture, 0, ~map_binding);
1401 context_unmap_bo_address(context, &dst_data, GL_PIXEL_UNPACK_BUFFER);
1402 context_unmap_bo_address(context, &src_data, GL_PIXEL_UNPACK_BUFFER);
1404 else
1406 RECT src_rect = {0, 0, desc.width, desc.height};
1407 POINT dst_point = {0, 0};
1409 TRACE("Using upload conversion.\n");
1411 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
1412 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
1413 wined3d_surface_upload_data(dst_texture->sub_resources[0].u.surface, gl_info, src_format,
1414 &src_rect, src_row_pitch, &dst_point, FALSE, wined3d_const_bo_address(&src_data));
1416 wined3d_texture_validate_location(dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB);
1417 wined3d_texture_invalidate_location(dst_texture, 0, ~WINED3D_LOCATION_TEXTURE_RGB);
1420 if (context)
1421 context_release(context);
1423 return dst_texture;
1426 static void read_from_framebuffer(struct wined3d_surface *surface,
1427 struct wined3d_context *old_ctx, DWORD src_location, DWORD dst_location)
1429 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
1430 struct wined3d_texture *texture = surface->container;
1431 struct wined3d_device *device = texture->resource.device;
1432 struct wined3d_context *context = old_ctx;
1433 struct wined3d_surface *restore_rt = NULL;
1434 const struct wined3d_gl_info *gl_info;
1435 unsigned int row_pitch, slice_pitch;
1436 unsigned int width, height, level;
1437 struct wined3d_bo_address data;
1438 BYTE *row, *top, *bottom;
1439 BOOL src_is_upside_down;
1440 unsigned int i;
1441 BYTE *mem;
1443 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
1445 restore_rt = context_get_rt_surface(old_ctx);
1446 if (restore_rt != surface)
1447 context = context_acquire(device, texture, sub_resource_idx);
1448 else
1449 restore_rt = NULL;
1450 gl_info = context->gl_info;
1452 if (src_location != texture->resource.draw_binding)
1454 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, surface, NULL, src_location);
1455 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1456 context_invalidate_state(context, STATE_FRAMEBUFFER);
1458 else
1460 context_apply_blit_state(context, device);
1463 /* Select the correct read buffer, and give some debug output.
1464 * There is no need to keep track of the current read buffer or reset it,
1465 * every part of the code that reads sets the read buffer as desired.
1467 if (src_location != WINED3D_LOCATION_DRAWABLE || wined3d_resource_is_offscreen(&texture->resource))
1469 /* Mapping the primary render target which is not on a swapchain.
1470 * Read from the back buffer. */
1471 TRACE("Mapping offscreen render target.\n");
1472 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1473 src_is_upside_down = TRUE;
1475 else
1477 /* Onscreen surfaces are always part of a swapchain */
1478 GLenum buffer = wined3d_texture_get_gl_buffer(texture);
1479 TRACE("Mapping %#x buffer.\n", buffer);
1480 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
1481 src_is_upside_down = FALSE;
1483 checkGLcall("glReadBuffer");
1485 if (data.buffer_object)
1487 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1488 checkGLcall("glBindBuffer");
1491 level = sub_resource_idx % texture->level_count;
1492 wined3d_texture_get_pitch(texture, level, &row_pitch, &slice_pitch);
1494 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1495 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / texture->resource.format->byte_count);
1496 checkGLcall("glPixelStorei");
1498 width = wined3d_texture_get_level_width(texture, level);
1499 height = wined3d_texture_get_level_height(texture, level);
1500 gl_info->gl_ops.gl.p_glReadPixels(0, 0, width, height,
1501 texture->resource.format->glFormat,
1502 texture->resource.format->glType, data.addr);
1503 checkGLcall("glReadPixels");
1505 /* Reset previous pixel store pack state */
1506 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
1507 checkGLcall("glPixelStorei");
1509 if (!src_is_upside_down)
1511 /* glReadPixels returns the image upside down, and there is no way to
1512 * prevent this. Flip the lines in software. */
1514 if (!(row = heap_alloc(row_pitch)))
1515 goto error;
1517 if (data.buffer_object)
1519 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
1520 checkGLcall("glMapBuffer");
1522 else
1523 mem = data.addr;
1525 top = mem;
1526 bottom = mem + row_pitch * (height - 1);
1527 for (i = 0; i < height / 2; i++)
1529 memcpy(row, top, row_pitch);
1530 memcpy(top, bottom, row_pitch);
1531 memcpy(bottom, row, row_pitch);
1532 top += row_pitch;
1533 bottom -= row_pitch;
1535 heap_free(row);
1537 if (data.buffer_object)
1538 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
1541 error:
1542 if (data.buffer_object)
1544 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1545 checkGLcall("glBindBuffer");
1548 if (restore_rt)
1549 context_restore(context, restore_rt);
1552 /* Read the framebuffer contents into a texture. Note that this function
1553 * doesn't do any kind of flipping. Using this on an onscreen surface will
1554 * result in a flipped D3D texture.
1556 * Context activation is done by the caller. This function may temporarily
1557 * switch to a different context and restore the original one before return. */
1558 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb, struct wined3d_context *old_ctx)
1560 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
1561 struct wined3d_texture *texture = surface->container;
1562 struct wined3d_device *device = texture->resource.device;
1563 const struct wined3d_gl_info *gl_info;
1564 struct wined3d_context *context = old_ctx;
1565 struct wined3d_surface *restore_rt = NULL;
1566 GLenum target;
1568 restore_rt = context_get_rt_surface(old_ctx);
1569 if (restore_rt != surface)
1570 context = context_acquire(device, texture, sub_resource_idx);
1571 else
1572 restore_rt = NULL;
1574 gl_info = context->gl_info;
1575 device_invalidate_state(device, STATE_FRAMEBUFFER);
1577 wined3d_texture_prepare_texture(texture, context, srgb);
1578 wined3d_texture_bind_and_dirtify(texture, context, srgb);
1580 TRACE("Reading back offscreen render target %p.\n", surface);
1582 if (wined3d_resource_is_offscreen(&texture->resource))
1583 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1584 else
1585 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(texture));
1586 checkGLcall("glReadBuffer");
1588 target = wined3d_texture_get_sub_resource_target(texture, sub_resource_idx);
1589 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(target, surface->texture_level,
1590 0, 0, 0, 0, wined3d_texture_get_level_width(texture, surface->texture_level),
1591 wined3d_texture_get_level_height(texture, surface->texture_level));
1592 checkGLcall("glCopyTexSubImage2D");
1594 if (restore_rt)
1595 context_restore(context, restore_rt);
1598 /* Does a direct frame buffer -> texture copy. Stretching is done with single
1599 * pixel copy calls. */
1600 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
1601 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
1603 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
1604 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1605 struct wined3d_texture *src_texture = src_surface->container;
1606 struct wined3d_texture *dst_texture = dst_surface->container;
1607 struct wined3d_device *device = dst_texture->resource.device;
1608 const struct wined3d_gl_info *gl_info;
1609 float xrel, yrel;
1610 struct wined3d_context *context;
1611 BOOL upsidedown = FALSE;
1612 RECT dst_rect = *dst_rect_in;
1613 unsigned int src_height;
1614 GLenum dst_target;
1616 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1617 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1619 if(dst_rect.top > dst_rect.bottom) {
1620 UINT tmp = dst_rect.bottom;
1621 dst_rect.bottom = dst_rect.top;
1622 dst_rect.top = tmp;
1623 upsidedown = TRUE;
1626 context = context_acquire(device, src_texture, src_sub_resource_idx);
1627 gl_info = context->gl_info;
1628 context_apply_blit_state(context, device);
1629 wined3d_texture_load(dst_texture, context, FALSE);
1631 /* Bind the target texture */
1632 context_bind_texture(context, dst_texture->target, dst_texture->texture_rgb.name);
1633 if (wined3d_resource_is_offscreen(&src_texture->resource))
1635 TRACE("Reading from an offscreen target\n");
1636 upsidedown = !upsidedown;
1637 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1639 else
1641 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
1643 checkGLcall("glReadBuffer");
1645 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
1646 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
1648 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1650 FIXME_(d3d_perf)("Doing a pixel by pixel copy from the framebuffer to a texture.\n");
1652 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1653 ERR("Texture filtering not supported in direct blit.\n");
1655 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1656 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1658 ERR("Texture filtering not supported in direct blit\n");
1661 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
1662 dst_target = wined3d_texture_get_sub_resource_target(dst_texture, dst_sub_resource_idx);
1663 if (upsidedown
1664 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1665 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1667 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
1668 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_target, dst_surface->texture_level,
1669 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
1670 src_rect->left, src_height - src_rect->bottom,
1671 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1673 else
1675 LONG row;
1676 UINT yoffset = src_height - src_rect->top + dst_rect.top - 1;
1677 /* I have to process this row by row to swap the image,
1678 * otherwise it would be upside down, so stretching in y direction
1679 * doesn't cost extra time
1681 * However, stretching in x direction can be avoided if not necessary
1683 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
1684 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1686 /* Well, that stuff works, but it's very slow.
1687 * find a better way instead
1689 LONG col;
1691 for (col = dst_rect.left; col < dst_rect.right; ++col)
1693 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_target, dst_surface->texture_level,
1694 dst_rect.left + col /* x offset */, row /* y offset */,
1695 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
1698 else
1700 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_target, dst_surface->texture_level,
1701 dst_rect.left /* x offset */, row /* y offset */,
1702 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
1706 checkGLcall("glCopyTexSubImage2D");
1708 context_release(context);
1710 /* The texture is now most up to date - If the surface is a render target
1711 * and has a drawable, this path is never entered. */
1712 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1713 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1716 /* Uses the hardware to stretch and flip the image */
1717 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
1718 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
1720 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
1721 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1722 unsigned int src_width, src_height, src_pow2_width, src_pow2_height;
1723 struct wined3d_texture *src_texture = src_surface->container;
1724 struct wined3d_texture *dst_texture = dst_surface->container;
1725 struct wined3d_device *device = dst_texture->resource.device;
1726 GLenum src_target, dst_target, texture_target;
1727 GLuint src, backup = 0;
1728 float left, right, top, bottom; /* Texture coordinates */
1729 const struct wined3d_gl_info *gl_info;
1730 struct wined3d_context *context;
1731 GLenum drawBuffer = GL_BACK;
1732 GLenum offscreen_buffer;
1733 BOOL noBackBufferBackup;
1734 BOOL src_offscreen;
1735 BOOL upsidedown = FALSE;
1736 RECT dst_rect = *dst_rect_in;
1738 TRACE("Using hwstretch blit\n");
1740 src_target = wined3d_texture_get_sub_resource_target(src_texture, src_sub_resource_idx);
1741 dst_target = wined3d_texture_get_sub_resource_target(dst_texture, dst_sub_resource_idx);
1743 /* Activate the Proper context for reading from the source surface, set it up for blitting */
1744 context = context_acquire(device, src_texture, src_sub_resource_idx);
1745 gl_info = context->gl_info;
1746 context_apply_blit_state(context, device);
1747 wined3d_texture_load(dst_texture, context, FALSE);
1749 offscreen_buffer = context_get_offscreen_gl_buffer(context);
1750 src_width = wined3d_texture_get_level_width(src_texture, src_surface->texture_level);
1751 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
1752 src_pow2_width = wined3d_texture_get_level_pow2_width(src_texture, src_surface->texture_level);
1753 src_pow2_height = wined3d_texture_get_level_pow2_height(src_texture, src_surface->texture_level);
1755 src_offscreen = wined3d_resource_is_offscreen(&src_texture->resource);
1756 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
1757 if (!noBackBufferBackup && !src_texture->texture_rgb.name)
1759 /* Get it a description */
1760 wined3d_texture_load(src_texture, context, FALSE);
1763 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
1764 * This way we don't have to wait for the 2nd readback to finish to leave this function.
1766 if (context->aux_buffers >= 2)
1768 /* Got more than one aux buffer? Use the 2nd aux buffer */
1769 drawBuffer = GL_AUX1;
1771 else if ((!src_offscreen || offscreen_buffer == GL_BACK) && context->aux_buffers >= 1)
1773 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
1774 drawBuffer = GL_AUX0;
1777 if (noBackBufferBackup)
1779 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
1780 checkGLcall("glGenTextures");
1781 context_bind_texture(context, GL_TEXTURE_2D, backup);
1782 texture_target = GL_TEXTURE_2D;
1784 else
1786 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
1787 * we are reading from the back buffer, the backup can be used as source texture
1789 texture_target = src_target;
1790 context_bind_texture(context, texture_target, src_texture->texture_rgb.name);
1791 gl_info->gl_ops.gl.p_glEnable(texture_target);
1792 checkGLcall("glEnable(texture_target)");
1794 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
1795 surface_get_sub_resource(src_surface)->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
1798 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1799 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1801 if(dst_rect.top > dst_rect.bottom) {
1802 UINT tmp = dst_rect.bottom;
1803 dst_rect.bottom = dst_rect.top;
1804 dst_rect.top = tmp;
1805 upsidedown = TRUE;
1808 if (src_offscreen)
1810 TRACE("Reading from an offscreen target\n");
1811 upsidedown = !upsidedown;
1812 gl_info->gl_ops.gl.p_glReadBuffer(offscreen_buffer);
1814 else
1816 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
1819 /* TODO: Only back up the part that will be overwritten */
1820 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, src_width, src_height);
1822 checkGLcall("glCopyTexSubImage2D");
1824 /* No issue with overriding these - the sampler is dirty due to blit usage */
1825 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
1826 checkGLcall("glTexParameteri");
1827 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
1828 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
1829 checkGLcall("glTexParameteri");
1831 if (!src_texture->swapchain || src_texture == src_texture->swapchain->back_buffers[0])
1833 src = backup ? backup : src_texture->texture_rgb.name;
1835 else
1837 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
1838 checkGLcall("glReadBuffer(GL_FRONT)");
1840 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
1841 checkGLcall("glGenTextures(1, &src)");
1842 context_bind_texture(context, GL_TEXTURE_2D, src);
1844 /* TODO: Only copy the part that will be read. Use src_rect->left,
1845 * src_rect->bottom as origin, but with the width watch out for power
1846 * of 2 sizes. */
1847 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_pow2_width,
1848 src_pow2_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
1849 checkGLcall("glTexImage2D");
1850 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, src_width, src_height);
1852 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1853 checkGLcall("glTexParameteri");
1854 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1855 checkGLcall("glTexParameteri");
1857 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
1858 checkGLcall("glReadBuffer(GL_BACK)");
1860 if (texture_target != GL_TEXTURE_2D)
1862 gl_info->gl_ops.gl.p_glDisable(texture_target);
1863 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
1864 texture_target = GL_TEXTURE_2D;
1867 checkGLcall("glEnd and previous");
1869 left = src_rect->left;
1870 right = src_rect->right;
1872 if (!upsidedown)
1874 top = src_height - src_rect->top;
1875 bottom = src_height - src_rect->bottom;
1877 else
1879 top = src_height - src_rect->bottom;
1880 bottom = src_height - src_rect->top;
1883 if (src_texture->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
1885 left /= src_pow2_width;
1886 right /= src_pow2_width;
1887 top /= src_pow2_height;
1888 bottom /= src_pow2_height;
1891 /* draw the source texture stretched and upside down. The correct surface is bound already */
1892 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1893 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1895 context_set_draw_buffer(context, drawBuffer);
1896 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
1898 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
1899 /* bottom left */
1900 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
1901 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
1903 /* top left */
1904 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
1905 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
1907 /* top right */
1908 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
1909 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1911 /* bottom right */
1912 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
1913 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
1914 gl_info->gl_ops.gl.p_glEnd();
1915 checkGLcall("glEnd and previous");
1917 if (texture_target != dst_target)
1919 gl_info->gl_ops.gl.p_glDisable(texture_target);
1920 gl_info->gl_ops.gl.p_glEnable(dst_target);
1921 texture_target = dst_target;
1924 /* Now read the stretched and upside down image into the destination texture */
1925 context_bind_texture(context, texture_target, dst_texture->texture_rgb.name);
1926 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
1928 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
1929 0, 0, /* We blitted the image to the origin */
1930 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1931 checkGLcall("glCopyTexSubImage2D");
1933 if (drawBuffer == GL_BACK)
1935 /* Write the back buffer backup back. */
1936 if (backup)
1938 if (texture_target != GL_TEXTURE_2D)
1940 gl_info->gl_ops.gl.p_glDisable(texture_target);
1941 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
1942 texture_target = GL_TEXTURE_2D;
1944 context_bind_texture(context, GL_TEXTURE_2D, backup);
1946 else
1948 if (texture_target != src_target)
1950 gl_info->gl_ops.gl.p_glDisable(texture_target);
1951 gl_info->gl_ops.gl.p_glEnable(src_target);
1952 texture_target = src_target;
1954 context_bind_texture(context, src_target, src_texture->texture_rgb.name);
1957 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
1958 /* top left */
1959 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
1960 gl_info->gl_ops.gl.p_glVertex2i(0, src_height);
1962 /* bottom left */
1963 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)src_height / (float)src_pow2_height);
1964 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
1966 /* bottom right */
1967 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width,
1968 (float)src_height / (float)src_pow2_height);
1969 gl_info->gl_ops.gl.p_glVertex2i(src_width, 0);
1971 /* top right */
1972 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width, 0.0f);
1973 gl_info->gl_ops.gl.p_glVertex2i(src_width, src_height);
1974 gl_info->gl_ops.gl.p_glEnd();
1976 gl_info->gl_ops.gl.p_glDisable(texture_target);
1977 checkGLcall("glDisable(texture_target)");
1979 /* Cleanup */
1980 if (src != src_texture->texture_rgb.name && src != backup)
1982 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
1983 checkGLcall("glDeleteTextures(1, &src)");
1985 if (backup)
1987 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
1988 checkGLcall("glDeleteTextures(1, &backup)");
1991 if (wined3d_settings.strict_draw_ordering)
1992 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
1994 context_release(context);
1996 /* The texture is now most up to date - If the surface is a render target
1997 * and has a drawable, this path is never entered. */
1998 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1999 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
2002 /* Front buffer coordinates are always full screen coordinates, but our GL
2003 * drawable is limited to the window's client area. The sysmem and texture
2004 * copies do have the full screen size. Note that GL has a bottom-left
2005 * origin, while D3D has a top-left origin. */
2006 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
2008 struct wined3d_texture *texture = surface->container;
2009 POINT offset = {0, 0};
2010 UINT drawable_height;
2011 RECT windowsize;
2013 if (!texture->swapchain)
2014 return;
2016 if (texture == texture->swapchain->front_buffer)
2018 ScreenToClient(window, &offset);
2019 OffsetRect(rect, offset.x, offset.y);
2022 GetClientRect(window, &windowsize);
2023 drawable_height = windowsize.bottom - windowsize.top;
2025 rect->top = drawable_height - rect->top;
2026 rect->bottom = drawable_height - rect->bottom;
2029 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
2030 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
2031 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
2033 struct wined3d_texture *dst_texture = dst_surface->container;
2034 struct wined3d_device *device = dst_texture->resource.device;
2035 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
2036 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
2037 struct wined3d_texture *src_texture;
2039 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
2040 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
2041 flags, fx, debug_d3dtexturefiltertype(filter));
2043 /* Get the swapchain. One of the surfaces has to be a primary surface. */
2044 if (!(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
2046 WARN("Destination resource is not GPU accessible, rejecting GL blit.\n");
2047 return WINED3DERR_INVALIDCALL;
2050 dst_swapchain = dst_texture->swapchain;
2052 if (src_surface)
2054 src_texture = src_surface->container;
2055 if (!(src_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
2057 WARN("Source resource is not GPU accessible, rejecting GL blit.\n");
2058 return WINED3DERR_INVALIDCALL;
2061 src_swapchain = src_texture->swapchain;
2063 else
2065 src_texture = NULL;
2066 src_swapchain = NULL;
2069 /* Early sort out of cases where no render target is used */
2070 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
2072 TRACE("No surface is render target, not using hardware blit.\n");
2073 return WINED3DERR_INVALIDCALL;
2076 /* No destination color keying supported */
2077 if (flags & (WINED3D_BLT_DST_CKEY | WINED3D_BLT_DST_CKEY_OVERRIDE))
2079 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2080 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2081 return WINED3DERR_INVALIDCALL;
2084 if (dst_swapchain && dst_swapchain == src_swapchain)
2086 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
2087 return WINED3DERR_INVALIDCALL;
2090 if (dst_swapchain && src_swapchain)
2092 FIXME("Implement hardware blit between two different swapchains\n");
2093 return WINED3DERR_INVALIDCALL;
2096 if (dst_swapchain)
2098 /* Handled with regular texture -> swapchain blit */
2099 if (src_surface == rt)
2100 TRACE("Blit from active render target to a swapchain\n");
2102 else if (src_swapchain && dst_surface == rt)
2104 FIXME("Implement blit from a swapchain to the active render target\n");
2105 return WINED3DERR_INVALIDCALL;
2108 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
2110 unsigned int src_width, src_height;
2111 /* Blit from render target to texture */
2112 BOOL stretchx;
2114 /* P8 read back is not implemented */
2115 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT
2116 || dst_texture->resource.format->id == WINED3DFMT_P8_UINT)
2118 TRACE("P8 read back not supported by frame buffer to texture blit\n");
2119 return WINED3DERR_INVALIDCALL;
2122 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_SRC_CKEY_OVERRIDE))
2124 TRACE("Color keying not supported by frame buffer to texture blit\n");
2125 return WINED3DERR_INVALIDCALL;
2126 /* Destination color key is checked above */
2129 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
2130 stretchx = TRUE;
2131 else
2132 stretchx = FALSE;
2134 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2135 * flip the image nor scale it.
2137 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2138 * -> If the app wants an image width an unscaled width, copy it line per line
2139 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
2140 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2141 * back buffer. This is slower than reading line per line, thus not used for flipping
2142 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2143 * pixel by pixel. */
2144 src_width = wined3d_texture_get_level_width(src_texture, src_surface->texture_level);
2145 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
2146 if (!stretchx || dst_rect->right - dst_rect->left > src_width
2147 || dst_rect->bottom - dst_rect->top > src_height)
2149 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
2150 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
2152 else
2154 TRACE("Using hardware stretching to flip / stretch the texture.\n");
2155 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
2158 return WINED3D_OK;
2161 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
2162 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
2163 return WINED3DERR_INVALIDCALL;
2166 /* Context activation is done by the caller. */
2167 static BOOL surface_load_sysmem(struct wined3d_surface *surface,
2168 struct wined3d_context *context, DWORD dst_location)
2170 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2171 const struct wined3d_gl_info *gl_info = context->gl_info;
2172 struct wined3d_texture *texture = surface->container;
2173 struct wined3d_texture_sub_resource *sub_resource;
2175 sub_resource = &texture->sub_resources[sub_resource_idx];
2176 wined3d_texture_prepare_location(texture, sub_resource_idx, context, dst_location);
2178 /* We cannot download data from multisample textures directly. */
2179 if (is_multisample_location(texture, WINED3D_LOCATION_TEXTURE_RGB))
2181 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_RB_RESOLVED);
2182 read_from_framebuffer(surface, context, WINED3D_LOCATION_RB_RESOLVED, dst_location);
2183 return TRUE;
2185 else
2187 if (sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
2188 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
2190 /* Download the surface to system memory. */
2191 if (sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
2193 wined3d_texture_bind_and_dirtify(texture, context,
2194 !(sub_resource->locations & WINED3D_LOCATION_TEXTURE_RGB));
2195 surface_download_data(surface, gl_info, dst_location);
2196 ++texture->download_count;
2198 return TRUE;
2202 if (!(texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2203 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
2205 read_from_framebuffer(surface, context, texture->resource.draw_binding, dst_location);
2206 return TRUE;
2209 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
2210 surface, wined3d_debug_location(sub_resource->locations));
2211 return FALSE;
2214 /* Context activation is done by the caller. */
2215 static BOOL surface_load_drawable(struct wined3d_surface *surface,
2216 struct wined3d_context *context)
2218 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2219 struct wined3d_texture *texture = surface->container;
2220 struct wined3d_surface *restore_rt = NULL;
2221 struct wined3d_device *device;
2222 RECT r;
2224 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2226 DWORD current = texture->sub_resources[sub_resource_idx].locations;
2227 FIXME("Unimplemented copy from %s for depth/stencil buffers.\n",
2228 wined3d_debug_location(current));
2229 return FALSE;
2232 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
2233 && wined3d_resource_is_offscreen(&texture->resource))
2235 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
2236 return FALSE;
2239 device = texture->resource.device;
2240 restore_rt = context_get_rt_surface(context);
2241 if (restore_rt != surface)
2242 context = context_acquire(device, texture, sub_resource_idx);
2243 else
2244 restore_rt = NULL;
2246 SetRect(&r, 0, 0, wined3d_texture_get_level_width(texture, surface->texture_level),
2247 wined3d_texture_get_level_height(texture, surface->texture_level));
2248 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
2249 device->blitter->ops->blitter_blit(device->blitter, WINED3D_BLIT_OP_COLOR_BLIT, context,
2250 surface, WINED3D_LOCATION_TEXTURE_RGB, &r,
2251 surface, WINED3D_LOCATION_DRAWABLE, &r,
2252 NULL, WINED3D_TEXF_POINT);
2254 if (restore_rt)
2255 context_restore(context, restore_rt);
2257 return TRUE;
2260 static BOOL surface_load_texture(struct wined3d_surface *surface,
2261 struct wined3d_context *context, BOOL srgb)
2263 unsigned int width, height, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch;
2264 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2265 const struct wined3d_gl_info *gl_info = context->gl_info;
2266 struct wined3d_texture *texture = surface->container;
2267 struct wined3d_device *device = texture->resource.device;
2268 const struct wined3d_color_key_conversion *conversion;
2269 struct wined3d_texture_sub_resource *sub_resource;
2270 struct wined3d_bo_address data;
2271 BYTE *src_mem, *dst_mem = NULL;
2272 struct wined3d_format format;
2273 POINT dst_point = {0, 0};
2274 RECT src_rect;
2275 BOOL depth;
2277 depth = texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL;
2278 sub_resource = surface_get_sub_resource(surface);
2280 if (!depth && wined3d_settings.offscreen_rendering_mode != ORM_FBO
2281 && wined3d_resource_is_offscreen(&texture->resource)
2282 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
2284 surface_load_fb_texture(surface, srgb, context);
2286 return TRUE;
2289 width = wined3d_texture_get_level_width(texture, surface->texture_level);
2290 height = wined3d_texture_get_level_height(texture, surface->texture_level);
2291 SetRect(&src_rect, 0, 0, width, height);
2293 if (!depth && sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
2294 && (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
2295 && fbo_blitter_supported(WINED3D_BLIT_OP_COLOR_BLIT, gl_info,
2296 &texture->resource, WINED3D_LOCATION_TEXTURE_RGB,
2297 &texture->resource, WINED3D_LOCATION_TEXTURE_SRGB))
2299 if (srgb)
2300 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
2301 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
2302 else
2303 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
2304 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
2306 return TRUE;
2309 if (!depth && sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
2310 && (!srgb || (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)))
2312 DWORD src_location = sub_resource->locations & WINED3D_LOCATION_RB_RESOLVED ?
2313 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
2314 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2316 if (fbo_blitter_supported(WINED3D_BLIT_OP_COLOR_BLIT, gl_info,
2317 &texture->resource, src_location, &texture->resource, dst_location))
2318 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, src_location,
2319 &src_rect, surface, dst_location, &src_rect);
2321 return TRUE;
2324 /* Upload from system memory */
2326 if (srgb)
2328 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | texture->resource.map_binding))
2329 == WINED3D_LOCATION_TEXTURE_RGB)
2331 FIXME_(d3d_perf)("Downloading RGB surface %p to reload it as sRGB.\n", surface);
2332 wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding);
2335 else
2337 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | texture->resource.map_binding))
2338 == WINED3D_LOCATION_TEXTURE_SRGB)
2340 FIXME_(d3d_perf)("Downloading sRGB surface %p to reload it as RGB.\n", surface);
2341 wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding);
2345 if (!(sub_resource->locations & surface_simple_locations))
2347 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
2348 /* Lets hope we get it from somewhere... */
2349 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM);
2352 wined3d_texture_prepare_texture(texture, context, srgb);
2353 wined3d_texture_bind_and_dirtify(texture, context, srgb);
2354 wined3d_texture_get_pitch(texture, surface->texture_level, &src_row_pitch, &src_slice_pitch);
2356 format = *texture->resource.format;
2357 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
2358 format = *wined3d_get_format(gl_info, conversion->dst_format, texture->resource.usage);
2360 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
2361 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
2362 * getting called. */
2363 if ((format.conv_byte_count || conversion) && texture->sub_resources[sub_resource_idx].buffer_object)
2365 TRACE("Removing the pbo attached to surface %p.\n", surface);
2367 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM);
2368 wined3d_texture_set_map_binding(texture, WINED3D_LOCATION_SYSMEM);
2371 wined3d_texture_get_memory(texture, sub_resource_idx, &data, sub_resource->locations);
2372 if (format.conv_byte_count)
2374 /* This code is entered for texture formats which need a fixup. */
2375 format.byte_count = format.conv_byte_count;
2376 wined3d_format_calculate_pitch(&format, 1, width, height, &dst_row_pitch, &dst_slice_pitch);
2378 src_mem = context_map_bo_address(context, &data, src_slice_pitch,
2379 GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ);
2380 if (!(dst_mem = heap_alloc(dst_slice_pitch)))
2382 ERR("Out of memory (%u).\n", dst_slice_pitch);
2383 context_release(context);
2384 return FALSE;
2386 format.upload(src_mem, dst_mem, src_row_pitch, src_slice_pitch,
2387 dst_row_pitch, dst_slice_pitch, width, height, 1);
2388 src_row_pitch = dst_row_pitch;
2389 context_unmap_bo_address(context, &data, GL_PIXEL_UNPACK_BUFFER);
2391 data.buffer_object = 0;
2392 data.addr = dst_mem;
2394 else if (conversion)
2396 /* This code is only entered for color keying fixups */
2397 struct wined3d_palette *palette = NULL;
2399 wined3d_format_calculate_pitch(&format, device->surface_alignment,
2400 width, height, &dst_row_pitch, &dst_slice_pitch);
2402 src_mem = context_map_bo_address(context, &data, src_slice_pitch,
2403 GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ);
2404 if (!(dst_mem = heap_alloc(dst_slice_pitch)))
2406 ERR("Out of memory (%u).\n", dst_slice_pitch);
2407 context_release(context);
2408 return FALSE;
2410 if (texture->swapchain && texture->swapchain->palette)
2411 palette = texture->swapchain->palette;
2412 conversion->convert(src_mem, src_row_pitch, dst_mem, dst_row_pitch,
2413 width, height, palette, &texture->async.gl_color_key);
2414 src_row_pitch = dst_row_pitch;
2415 context_unmap_bo_address(context, &data, GL_PIXEL_UNPACK_BUFFER);
2417 data.buffer_object = 0;
2418 data.addr = dst_mem;
2421 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
2422 src_row_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
2424 heap_free(dst_mem);
2426 return TRUE;
2429 /* Context activation is done by the caller. */
2430 static BOOL surface_load_renderbuffer(struct wined3d_surface *surface, struct wined3d_context *context,
2431 DWORD dst_location)
2433 struct wined3d_texture *texture = surface->container;
2434 const RECT rect = {0, 0,
2435 wined3d_texture_get_level_width(texture, surface->texture_level),
2436 wined3d_texture_get_level_height(texture, surface->texture_level)};
2437 DWORD locations = surface_get_sub_resource(surface)->locations;
2438 DWORD src_location;
2440 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2442 FIXME("Unimplemented copy from %s for depth/stencil buffers.\n",
2443 wined3d_debug_location(locations));
2444 return FALSE;
2447 if (locations & WINED3D_LOCATION_RB_MULTISAMPLE)
2448 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
2449 else if (locations & WINED3D_LOCATION_RB_RESOLVED)
2450 src_location = WINED3D_LOCATION_RB_RESOLVED;
2451 else if (locations & WINED3D_LOCATION_TEXTURE_SRGB)
2452 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
2453 else /* surface_blt_fbo will load the source location if necessary. */
2454 src_location = WINED3D_LOCATION_TEXTURE_RGB;
2456 surface_blt_fbo(texture->resource.device, context, WINED3D_TEXF_POINT,
2457 surface, src_location, &rect, surface, dst_location, &rect);
2459 return TRUE;
2462 /* Context activation is done by the caller. Context may be NULL in ddraw-only mode. */
2463 BOOL surface_load_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
2465 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
2467 switch (location)
2469 case WINED3D_LOCATION_USER_MEMORY:
2470 case WINED3D_LOCATION_SYSMEM:
2471 case WINED3D_LOCATION_BUFFER:
2472 return surface_load_sysmem(surface, context, location);
2474 case WINED3D_LOCATION_DRAWABLE:
2475 return surface_load_drawable(surface, context);
2477 case WINED3D_LOCATION_RB_RESOLVED:
2478 case WINED3D_LOCATION_RB_MULTISAMPLE:
2479 return surface_load_renderbuffer(surface, context, location);
2481 case WINED3D_LOCATION_TEXTURE_RGB:
2482 case WINED3D_LOCATION_TEXTURE_SRGB:
2483 return surface_load_texture(surface, context,
2484 location == WINED3D_LOCATION_TEXTURE_SRGB);
2486 default:
2487 ERR("Don't know how to handle location %#x.\n", location);
2488 return FALSE;
2492 /* Context activation is done by the caller. */
2493 static void fbo_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2495 struct wined3d_blitter *next;
2497 if ((next = blitter->next))
2498 next->ops->blitter_destroy(next, context);
2500 heap_free(blitter);
2503 static void fbo_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
2504 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
2505 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
2507 struct wined3d_blitter *next;
2509 if ((next = blitter->next))
2510 next->ops->blitter_clear(next, device, rt_count, fb, rect_count,
2511 clear_rects, draw_rect, flags, colour, depth, stencil);
2514 static DWORD fbo_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
2515 struct wined3d_context *context, struct wined3d_surface *src_surface, DWORD src_location,
2516 const RECT *src_rect, struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect,
2517 const struct wined3d_color_key *colour_key, enum wined3d_texture_filter_type filter)
2519 struct wined3d_resource *src_resource = &src_surface->container->resource;
2520 struct wined3d_resource *dst_resource = &dst_surface->container->resource;
2521 struct wined3d_device *device = dst_resource->device;
2522 enum wined3d_blit_op blit_op = op;
2523 struct wined3d_blitter *next;
2525 if (blit_op == WINED3D_BLIT_OP_RAW_BLIT && dst_resource->format->id == src_resource->format->id)
2527 if (dst_resource->format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
2528 blit_op = WINED3D_BLIT_OP_DEPTH_BLIT;
2529 else
2530 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
2533 if (!fbo_blitter_supported(blit_op, context->gl_info,
2534 src_resource, src_location, dst_resource, dst_location))
2536 if ((next = blitter->next))
2537 return next->ops->blitter_blit(next, op, context, src_surface, src_location,
2538 src_rect, dst_surface, dst_location, dst_rect, colour_key, filter);
2541 if (blit_op == WINED3D_BLIT_OP_COLOR_BLIT)
2543 TRACE("Colour blit.\n");
2544 surface_blt_fbo(device, context, filter, src_surface, src_location,
2545 src_rect, dst_surface, dst_location, dst_rect);
2546 return dst_location;
2549 if (blit_op == WINED3D_BLIT_OP_DEPTH_BLIT)
2551 TRACE("Depth/stencil blit.\n");
2552 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
2553 return dst_location;
2556 ERR("This blitter does not implement blit op %#x.\n", blit_op);
2557 return dst_location;
2560 static const struct wined3d_blitter_ops fbo_blitter_ops =
2562 fbo_blitter_destroy,
2563 fbo_blitter_clear,
2564 fbo_blitter_blit,
2567 void wined3d_fbo_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
2569 struct wined3d_blitter *blitter;
2571 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
2572 return;
2574 if (!(blitter = heap_alloc(sizeof(*blitter))))
2575 return;
2577 TRACE("Created blitter %p.\n", blitter);
2579 blitter->ops = &fbo_blitter_ops;
2580 blitter->next = *next;
2581 *next = blitter;
2584 /* Context activation is done by the caller. */
2585 static void raw_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2587 struct wined3d_blitter *next;
2589 if ((next = blitter->next))
2590 next->ops->blitter_destroy(next, context);
2592 heap_free(blitter);
2595 /* Context activation is done by the caller. */
2596 static void raw_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
2597 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
2598 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
2600 struct wined3d_blitter *next;
2602 if (!(next = blitter->next))
2604 ERR("No blitter to handle clear.\n");
2605 return;
2608 TRACE("Forwarding to blitter %p.\n", next);
2609 next->ops->blitter_clear(next, device, rt_count, fb, rect_count,
2610 clear_rects, draw_rect, flags, colour, depth, stencil);
2613 /* Context activation is done by the caller. */
2614 static DWORD raw_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
2615 struct wined3d_context *context, struct wined3d_surface *src_surface, DWORD src_location,
2616 const RECT *src_rect, struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect,
2617 const struct wined3d_color_key *colour_key, enum wined3d_texture_filter_type filter)
2619 const struct wined3d_gl_info *gl_info = context->gl_info;
2620 unsigned int src_sub_resource_idx, dst_sub_resource_idx;
2621 struct wined3d_texture *src_texture, *dst_texture;
2622 unsigned int src_layer, dst_layer;
2623 struct wined3d_blitter *next;
2624 GLuint src_name, dst_name;
2625 DWORD location;
2627 src_texture = src_surface->container;
2628 dst_texture = dst_surface->container;
2630 /* If we would need to copy from a renderbuffer or drawable, we'd probably
2631 * be better of using the FBO blitter directly, since we'd need to use it
2632 * to copy the resource contents to the texture anyway. */
2633 if (op != WINED3D_BLIT_OP_RAW_BLIT
2634 || (src_texture->resource.format->id == dst_texture->resource.format->id
2635 && (!(src_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
2636 || !(dst_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB)))))
2638 if (!(next = blitter->next))
2640 ERR("No blitter to handle blit op %#x.\n", op);
2641 return dst_location;
2644 TRACE("Forwarding to blitter %p.\n", next);
2645 return next->ops->blitter_blit(next, op, context, src_surface, src_location,
2646 src_rect, dst_surface, dst_location, dst_rect, colour_key, filter);
2649 TRACE("Blit using ARB_copy_image.\n");
2651 src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
2652 dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
2654 location = src_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB);
2655 if (!location)
2656 location = src_texture->flags & WINED3D_TEXTURE_IS_SRGB
2657 ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2658 if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, location))
2659 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(location));
2660 src_name = wined3d_texture_get_texture_name(src_texture, context, location == WINED3D_LOCATION_TEXTURE_SRGB);
2661 src_layer = src_sub_resource_idx / src_texture->level_count;
2663 location = dst_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB);
2664 if (!location)
2665 location = dst_texture->flags & WINED3D_TEXTURE_IS_SRGB
2666 ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2667 if (surface_is_full_rect(dst_surface, dst_rect))
2669 if (!wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, location))
2670 ERR("Failed to prepare the destination sub-resource into %s.\n", wined3d_debug_location(location));
2672 else
2674 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, location))
2675 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(location));
2677 dst_name = wined3d_texture_get_texture_name(dst_texture, context, location == WINED3D_LOCATION_TEXTURE_SRGB);
2678 dst_layer = dst_sub_resource_idx / dst_texture->level_count;
2680 GL_EXTCALL(glCopyImageSubData(src_name, src_texture->target, src_surface->texture_level,
2681 src_rect->left, src_rect->top, src_layer, dst_name, dst_texture->target, dst_surface->texture_level,
2682 dst_rect->left, dst_rect->top, dst_layer, src_rect->right - src_rect->left,
2683 src_rect->bottom - src_rect->top, 1));
2684 checkGLcall("copy image data");
2686 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, location);
2687 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~location);
2688 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location))
2689 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(dst_location));
2691 return dst_location | location;
2694 static const struct wined3d_blitter_ops raw_blitter_ops =
2696 raw_blitter_destroy,
2697 raw_blitter_clear,
2698 raw_blitter_blit,
2701 void wined3d_raw_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
2703 struct wined3d_blitter *blitter;
2705 if (!gl_info->supported[ARB_COPY_IMAGE])
2706 return;
2708 if (!(blitter = heap_alloc(sizeof(*blitter))))
2709 return;
2711 TRACE("Created blitter %p.\n", blitter);
2713 blitter->ops = &raw_blitter_ops;
2714 blitter->next = *next;
2715 *next = blitter;
2718 /* Context activation is done by the caller. */
2719 static void ffp_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2721 struct wined3d_blitter *next;
2723 if ((next = blitter->next))
2724 next->ops->blitter_destroy(next, context);
2726 heap_free(blitter);
2729 static BOOL ffp_blit_supported(enum wined3d_blit_op blit_op, const struct wined3d_context *context,
2730 const struct wined3d_resource *src_resource, DWORD src_location,
2731 const struct wined3d_resource *dst_resource, DWORD dst_location)
2733 const struct wined3d_format *src_format = src_resource->format;
2734 const struct wined3d_format *dst_format = dst_resource->format;
2735 BOOL decompress;
2737 decompress = src_format && (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
2738 && !(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED);
2739 if (!decompress && !(src_resource->access & dst_resource->access & WINED3D_RESOURCE_ACCESS_GPU))
2741 TRACE("Source or destination resource is not GPU accessible.\n");
2742 return FALSE;
2745 if (blit_op == WINED3D_BLIT_OP_RAW_BLIT && dst_format->id == src_format->id)
2747 if (dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
2748 blit_op = WINED3D_BLIT_OP_DEPTH_BLIT;
2749 else
2750 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
2753 switch (blit_op)
2755 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
2756 if (context->d3d_info->shader_color_key)
2758 TRACE("Color keying requires converted textures.\n");
2759 return FALSE;
2761 case WINED3D_BLIT_OP_COLOR_BLIT:
2762 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
2763 if (!context->gl_info->supported[WINED3D_GL_LEGACY_CONTEXT])
2764 return FALSE;
2766 if (TRACE_ON(d3d))
2768 TRACE("Checking support for fixup:\n");
2769 dump_color_fixup_desc(src_format->color_fixup);
2772 /* We only support identity conversions. */
2773 if (!is_identity_fixup(src_format->color_fixup)
2774 || !is_identity_fixup(dst_format->color_fixup))
2776 if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER
2777 && dst_format->id == src_format->id && dst_location == WINED3D_LOCATION_DRAWABLE)
2779 WARN("Claiming fixup support because of ORM_BACKBUFFER.\n");
2781 else
2783 TRACE("Fixups are not supported.\n");
2784 return FALSE;
2788 if (!(dst_resource->usage & WINED3DUSAGE_RENDERTARGET))
2790 TRACE("Can only blit to render targets.\n");
2791 return FALSE;
2793 return TRUE;
2795 default:
2796 TRACE("Unsupported blit operation %#x.\n", blit_op);
2797 return FALSE;
2801 static BOOL ffp_blitter_use_cpu_clear(struct wined3d_rendertarget_view *view)
2803 struct wined3d_resource *resource;
2804 struct wined3d_texture *texture;
2805 DWORD locations;
2807 resource = view->resource;
2808 if (resource->type == WINED3D_RTYPE_BUFFER)
2809 return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU);
2811 texture = texture_from_resource(resource);
2812 locations = texture->sub_resources[view->sub_resource_idx].locations;
2813 if (locations & (resource->map_binding | WINED3D_LOCATION_DISCARDED))
2814 return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU)
2815 || (texture->flags & WINED3D_TEXTURE_PIN_SYSMEM);
2817 return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU)
2818 && !(texture->flags & WINED3D_TEXTURE_CONVERTED);
2821 static void ffp_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
2822 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
2823 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
2825 struct wined3d_rendertarget_view *view;
2826 struct wined3d_blitter *next;
2827 DWORD next_flags = 0;
2828 unsigned int i;
2830 if (flags & WINED3DCLEAR_TARGET)
2832 for (i = 0; i < rt_count; ++i)
2834 if (!(view = fb->render_targets[i]))
2835 continue;
2837 if (ffp_blitter_use_cpu_clear(view)
2838 || (!(view->resource->usage & WINED3DUSAGE_RENDERTARGET)
2839 && (wined3d_settings.offscreen_rendering_mode != ORM_FBO
2840 || !(view->format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE))))
2842 next_flags |= WINED3DCLEAR_TARGET;
2843 flags &= ~WINED3DCLEAR_TARGET;
2844 break;
2847 /* FIXME: We should reject colour fills on formats with fixups,
2848 * but this would break P8 colour fills for example. */
2852 if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil)
2853 && (!view->format->depth_size || (flags & WINED3DCLEAR_ZBUFFER))
2854 && (!view->format->stencil_size || (flags & WINED3DCLEAR_STENCIL))
2855 && ffp_blitter_use_cpu_clear(view))
2857 next_flags |= flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL);
2858 flags &= ~(WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL);
2861 if (flags)
2862 device_clear_render_targets(device, rt_count, fb, rect_count,
2863 clear_rects, draw_rect, flags, colour, depth, stencil);
2865 if (next_flags && (next = blitter->next))
2866 next->ops->blitter_clear(next, device, rt_count, fb, rect_count,
2867 clear_rects, draw_rect, next_flags, colour, depth, stencil);
2870 static DWORD ffp_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
2871 struct wined3d_context *context, struct wined3d_surface *src_surface, DWORD src_location,
2872 const RECT *src_rect, struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect,
2873 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter)
2875 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
2876 struct wined3d_texture *src_texture = src_surface->container;
2877 struct wined3d_texture *dst_texture = dst_surface->container;
2878 const struct wined3d_gl_info *gl_info = context->gl_info;
2879 struct wined3d_resource *src_resource, *dst_resource;
2880 struct wined3d_color_key old_blt_key;
2881 struct wined3d_device *device;
2882 struct wined3d_blitter *next;
2883 DWORD old_color_key_flags;
2884 RECT r;
2886 src_resource = &src_texture->resource;
2887 dst_resource = &dst_texture->resource;
2888 device = dst_resource->device;
2890 if (!ffp_blit_supported(op, context, src_resource, src_location, dst_resource, dst_location))
2892 if ((next = blitter->next))
2893 return next->ops->blitter_blit(next, op, context, src_surface, src_location,
2894 src_rect, dst_surface, dst_location, dst_rect, color_key, filter);
2897 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
2899 old_blt_key = src_texture->async.src_blt_color_key;
2900 old_color_key_flags = src_texture->async.color_key_flags;
2901 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT, color_key);
2903 /* Make sure the surface is up-to-date. This should probably use
2904 * surface_load_location() and worry about the destination surface too,
2905 * unless we're overwriting it completely. */
2906 wined3d_texture_load(src_texture, context, FALSE);
2908 /* Activate the destination context, set it up for blitting. */
2909 context_apply_blit_state(context, device);
2911 if (dst_location == WINED3D_LOCATION_DRAWABLE)
2913 r = *dst_rect;
2914 surface_translate_drawable_coords(dst_surface, context->win_handle, &r);
2915 dst_rect = &r;
2918 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
2920 GLenum buffer;
2922 if (dst_location == WINED3D_LOCATION_DRAWABLE)
2924 TRACE("Destination surface %p is onscreen.\n", dst_surface);
2925 buffer = wined3d_texture_get_gl_buffer(dst_texture);
2927 else
2929 TRACE("Destination surface %p is offscreen.\n", dst_surface);
2930 buffer = GL_COLOR_ATTACHMENT0;
2932 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
2933 context_set_draw_buffer(context, buffer);
2934 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
2935 context_invalidate_state(context, STATE_FRAMEBUFFER);
2938 gl_info->gl_ops.gl.p_glEnable(src_texture->target);
2939 checkGLcall("glEnable(target)");
2941 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST || color_key)
2943 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
2944 checkGLcall("glEnable(GL_ALPHA_TEST)");
2947 if (color_key)
2949 /* For P8 surfaces, the alpha component contains the palette index.
2950 * Which means that the colorkey is one of the palette entries. In
2951 * other cases pixels that should be masked away have alpha set to 0. */
2952 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT)
2953 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
2954 (float)src_texture->async.src_blt_color_key.color_space_low_value / 255.0f);
2955 else
2956 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
2957 checkGLcall("glAlphaFunc");
2960 draw_textured_quad(src_texture, src_sub_resource_idx, context, src_rect, dst_rect, filter);
2962 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST || color_key)
2964 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2965 checkGLcall("glDisable(GL_ALPHA_TEST)");
2968 /* Leave the OpenGL state valid for blitting. */
2969 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
2970 checkGLcall("glDisable(GL_TEXTURE_2D)");
2971 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
2973 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
2974 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
2976 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
2978 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
2979 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
2982 if (wined3d_settings.strict_draw_ordering
2983 || (dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture))
2984 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2986 /* Restore the color key parameters */
2987 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT,
2988 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
2990 return dst_location;
2993 static const struct wined3d_blitter_ops ffp_blitter_ops =
2995 ffp_blitter_destroy,
2996 ffp_blitter_clear,
2997 ffp_blitter_blit,
3000 void wined3d_ffp_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
3002 struct wined3d_blitter *blitter;
3004 if (!(blitter = heap_alloc(sizeof(*blitter))))
3005 return;
3007 TRACE("Created blitter %p.\n", blitter);
3009 blitter->ops = &ffp_blitter_ops;
3010 blitter->next = *next;
3011 *next = blitter;
3014 /* Context activation is done by the caller. */
3015 static void cpu_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
3017 struct wined3d_blitter *next;
3019 if ((next = blitter->next))
3020 next->ops->blitter_destroy(next, context);
3022 heap_free(blitter);
3025 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
3026 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
3027 const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx)
3029 UINT row_block_count;
3030 const BYTE *src_row;
3031 BYTE *dst_row;
3032 UINT x, y;
3034 src_row = src_data;
3035 dst_row = dst_data;
3037 row_block_count = (update_w + format->block_width - 1) / format->block_width;
3039 if (!flags)
3041 for (y = 0; y < update_h; y += format->block_height)
3043 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
3044 src_row += src_pitch;
3045 dst_row += dst_pitch;
3048 return WINED3D_OK;
3051 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
3053 src_row += (((update_h / format->block_height) - 1) * src_pitch);
3055 switch (format->id)
3057 case WINED3DFMT_DXT1:
3058 for (y = 0; y < update_h; y += format->block_height)
3060 struct block
3062 WORD color[2];
3063 BYTE control_row[4];
3066 const struct block *s = (const struct block *)src_row;
3067 struct block *d = (struct block *)dst_row;
3069 for (x = 0; x < row_block_count; ++x)
3071 d[x].color[0] = s[x].color[0];
3072 d[x].color[1] = s[x].color[1];
3073 d[x].control_row[0] = s[x].control_row[3];
3074 d[x].control_row[1] = s[x].control_row[2];
3075 d[x].control_row[2] = s[x].control_row[1];
3076 d[x].control_row[3] = s[x].control_row[0];
3078 src_row -= src_pitch;
3079 dst_row += dst_pitch;
3081 return WINED3D_OK;
3083 case WINED3DFMT_DXT2:
3084 case WINED3DFMT_DXT3:
3085 for (y = 0; y < update_h; y += format->block_height)
3087 struct block
3089 WORD alpha_row[4];
3090 WORD color[2];
3091 BYTE control_row[4];
3094 const struct block *s = (const struct block *)src_row;
3095 struct block *d = (struct block *)dst_row;
3097 for (x = 0; x < row_block_count; ++x)
3099 d[x].alpha_row[0] = s[x].alpha_row[3];
3100 d[x].alpha_row[1] = s[x].alpha_row[2];
3101 d[x].alpha_row[2] = s[x].alpha_row[1];
3102 d[x].alpha_row[3] = s[x].alpha_row[0];
3103 d[x].color[0] = s[x].color[0];
3104 d[x].color[1] = s[x].color[1];
3105 d[x].control_row[0] = s[x].control_row[3];
3106 d[x].control_row[1] = s[x].control_row[2];
3107 d[x].control_row[2] = s[x].control_row[1];
3108 d[x].control_row[3] = s[x].control_row[0];
3110 src_row -= src_pitch;
3111 dst_row += dst_pitch;
3113 return WINED3D_OK;
3115 default:
3116 FIXME("Compressed flip not implemented for format %s.\n",
3117 debug_d3dformat(format->id));
3118 return E_NOTIMPL;
3122 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
3123 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
3125 return E_NOTIMPL;
3128 static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
3129 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
3130 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
3131 enum wined3d_texture_filter_type filter)
3133 unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count;
3134 struct wined3d_device *device = dst_texture->resource.device;
3135 const struct wined3d_format *src_format, *dst_format;
3136 struct wined3d_texture *converted_texture = NULL;
3137 struct wined3d_bo_address src_data, dst_data;
3138 unsigned int src_fmt_flags, dst_fmt_flags;
3139 struct wined3d_map_desc dst_map, src_map;
3140 struct wined3d_context *context = NULL;
3141 unsigned int x, sx, xinc, y, sy, yinc;
3142 unsigned int texture_level;
3143 HRESULT hr = WINED3D_OK;
3144 BOOL same_sub_resource;
3145 DWORD map_binding;
3146 const BYTE *sbase;
3147 const BYTE *sbuf;
3148 BYTE *dbuf;
3150 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
3151 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
3152 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture,
3153 src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
3155 if (device->d3d_initialized)
3156 context = context_acquire(device, NULL, 0);
3158 if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx)
3160 same_sub_resource = TRUE;
3162 map_binding = dst_texture->resource.map_binding;
3163 texture_level = dst_sub_resource_idx % dst_texture->level_count;
3164 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
3165 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
3166 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
3167 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
3168 wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
3169 dst_map.data = context_map_bo_address(context, &dst_data,
3170 dst_texture->sub_resources[dst_sub_resource_idx].size,
3171 GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ | WINED3D_MAP_WRITE);
3173 src_map = dst_map;
3174 src_format = dst_texture->resource.format;
3175 dst_format = src_format;
3176 dst_fmt_flags = dst_texture->resource.format_flags;
3177 src_fmt_flags = dst_fmt_flags;
3179 else
3181 same_sub_resource = FALSE;
3182 dst_format = dst_texture->resource.format;
3183 dst_fmt_flags = dst_texture->resource.format_flags;
3184 if (!(flags & WINED3D_BLT_RAW) && dst_texture->resource.format->id != src_texture->resource.format->id)
3186 if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format)))
3188 FIXME("Cannot convert %s to %s.\n", debug_d3dformat(src_texture->resource.format->id),
3189 debug_d3dformat(dst_texture->resource.format->id));
3190 if (context)
3191 context_release(context);
3192 return WINED3DERR_NOTAVAILABLE;
3194 src_texture = converted_texture;
3195 src_sub_resource_idx = 0;
3197 src_format = src_texture->resource.format;
3198 src_fmt_flags = src_texture->resource.format_flags;
3200 map_binding = src_texture->resource.map_binding;
3201 texture_level = src_sub_resource_idx % src_texture->level_count;
3202 if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, map_binding))
3203 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
3204 wined3d_texture_get_pitch(src_texture, texture_level, &src_map.row_pitch, &src_map.slice_pitch);
3205 wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &src_data, map_binding);
3206 src_map.data = context_map_bo_address(context, &src_data,
3207 src_texture->sub_resources[src_sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ);
3209 map_binding = dst_texture->resource.map_binding;
3210 texture_level = dst_sub_resource_idx % dst_texture->level_count;
3211 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
3212 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
3213 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
3214 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
3215 wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
3216 dst_map.data = context_map_bo_address(context, &dst_data,
3217 dst_texture->sub_resources[dst_sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_WRITE);
3219 flags &= ~WINED3D_BLT_RAW;
3221 bpp = dst_format->byte_count;
3222 src_height = src_box->bottom - src_box->top;
3223 src_width = src_box->right - src_box->left;
3224 dst_height = dst_box->bottom - dst_box->top;
3225 dst_width = dst_box->right - dst_box->left;
3226 row_byte_count = dst_width * bpp;
3228 sbase = (BYTE *)src_map.data
3229 + ((src_box->top / src_format->block_height) * src_map.row_pitch)
3230 + ((src_box->left / src_format->block_width) * src_format->block_byte_count);
3231 dbuf = (BYTE *)dst_map.data
3232 + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch)
3233 + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count);
3235 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
3237 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
3239 if (same_sub_resource)
3241 FIXME("Only plain blits supported on compressed surfaces.\n");
3242 hr = E_NOTIMPL;
3243 goto release;
3246 if (src_height != dst_height || src_width != dst_width)
3248 WARN("Stretching not supported on compressed surfaces.\n");
3249 hr = WINED3DERR_INVALIDCALL;
3250 goto release;
3253 hr = surface_cpu_blt_compressed(sbase, dbuf,
3254 src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height,
3255 src_format, flags, fx);
3256 goto release;
3259 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
3260 && (src_width != dst_width || src_height != dst_height))
3262 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
3263 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
3266 xinc = (src_width << 16) / dst_width;
3267 yinc = (src_height << 16) / dst_height;
3269 if (!flags)
3271 /* No effects, we can cheat here. */
3272 if (dst_width == src_width)
3274 if (dst_height == src_height)
3276 /* No stretching in either direction. This needs to be as fast
3277 * as possible. */
3278 sbuf = sbase;
3280 /* Check for overlapping surfaces. */
3281 if (!same_sub_resource || dst_box->top < src_box->top
3282 || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
3284 /* No overlap, or dst above src, so copy from top downwards. */
3285 for (y = 0; y < dst_height; ++y)
3287 memcpy(dbuf, sbuf, row_byte_count);
3288 sbuf += src_map.row_pitch;
3289 dbuf += dst_map.row_pitch;
3292 else if (dst_box->top > src_box->top)
3294 /* Copy from bottom upwards. */
3295 sbuf += src_map.row_pitch * dst_height;
3296 dbuf += dst_map.row_pitch * dst_height;
3297 for (y = 0; y < dst_height; ++y)
3299 sbuf -= src_map.row_pitch;
3300 dbuf -= dst_map.row_pitch;
3301 memcpy(dbuf, sbuf, row_byte_count);
3304 else
3306 /* Src and dst overlapping on the same line, use memmove. */
3307 for (y = 0; y < dst_height; ++y)
3309 memmove(dbuf, sbuf, row_byte_count);
3310 sbuf += src_map.row_pitch;
3311 dbuf += dst_map.row_pitch;
3315 else
3317 /* Stretching in y direction only. */
3318 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3320 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3321 memcpy(dbuf, sbuf, row_byte_count);
3322 dbuf += dst_map.row_pitch;
3326 else
3328 /* Stretching in X direction. */
3329 unsigned int last_sy = ~0u;
3330 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3332 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3334 if ((sy >> 16) == (last_sy >> 16))
3336 /* This source row is the same as last source row -
3337 * Copy the already stretched row. */
3338 memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count);
3340 else
3342 #define STRETCH_ROW(type) \
3343 do { \
3344 const type *s = (const type *)sbuf; \
3345 type *d = (type *)dbuf; \
3346 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3347 d[x] = s[sx >> 16]; \
3348 } while(0)
3350 switch(bpp)
3352 case 1:
3353 STRETCH_ROW(BYTE);
3354 break;
3355 case 2:
3356 STRETCH_ROW(WORD);
3357 break;
3358 case 4:
3359 STRETCH_ROW(DWORD);
3360 break;
3361 case 3:
3363 const BYTE *s;
3364 BYTE *d = dbuf;
3365 for (x = sx = 0; x < dst_width; x++, sx+= xinc)
3367 DWORD pixel;
3369 s = sbuf + 3 * (sx >> 16);
3370 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3371 d[0] = (pixel ) & 0xff;
3372 d[1] = (pixel >> 8) & 0xff;
3373 d[2] = (pixel >> 16) & 0xff;
3374 d += 3;
3376 break;
3378 default:
3379 FIXME("Stretched blit not implemented for bpp %u.\n", bpp * 8);
3380 hr = WINED3DERR_NOTAVAILABLE;
3381 goto error;
3383 #undef STRETCH_ROW
3385 dbuf += dst_map.row_pitch;
3386 last_sy = sy;
3390 else
3392 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
3393 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
3394 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
3395 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3396 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
3398 /* The color keying flags are checked for correctness in ddraw. */
3399 if (flags & WINED3D_BLT_SRC_CKEY)
3401 keylow = src_texture->async.src_blt_color_key.color_space_low_value;
3402 keyhigh = src_texture->async.src_blt_color_key.color_space_high_value;
3404 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3406 keylow = fx->src_color_key.color_space_low_value;
3407 keyhigh = fx->src_color_key.color_space_high_value;
3410 if (flags & WINED3D_BLT_DST_CKEY)
3412 /* Destination color keys are taken from the source surface! */
3413 destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value;
3414 destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value;
3416 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
3418 destkeylow = fx->dst_color_key.color_space_low_value;
3419 destkeyhigh = fx->dst_color_key.color_space_high_value;
3422 if (bpp == 1)
3424 keymask = 0xff;
3426 else
3428 DWORD masks[3];
3429 get_color_masks(src_format, masks);
3430 keymask = masks[0] | masks[1] | masks[2];
3432 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3433 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
3436 if (flags & WINED3D_BLT_FX)
3438 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
3439 LONG tmpxy;
3440 dTopLeft = dbuf;
3441 dTopRight = dbuf + ((dst_width - 1) * bpp);
3442 dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
3443 dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
3445 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
3447 /* I don't think we need to do anything about this flag. */
3448 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
3450 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
3452 tmp = dTopRight;
3453 dTopRight = dTopLeft;
3454 dTopLeft = tmp;
3455 tmp = dBottomRight;
3456 dBottomRight = dBottomLeft;
3457 dBottomLeft = tmp;
3458 dstxinc = dstxinc * -1;
3460 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
3462 tmp = dTopLeft;
3463 dTopLeft = dBottomLeft;
3464 dBottomLeft = tmp;
3465 tmp = dTopRight;
3466 dTopRight = dBottomRight;
3467 dBottomRight = tmp;
3468 dstyinc = dstyinc * -1;
3470 if (fx->fx & WINEDDBLTFX_NOTEARING)
3472 /* I don't think we need to do anything about this flag. */
3473 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
3475 if (fx->fx & WINEDDBLTFX_ROTATE180)
3477 tmp = dBottomRight;
3478 dBottomRight = dTopLeft;
3479 dTopLeft = tmp;
3480 tmp = dBottomLeft;
3481 dBottomLeft = dTopRight;
3482 dTopRight = tmp;
3483 dstxinc = dstxinc * -1;
3484 dstyinc = dstyinc * -1;
3486 if (fx->fx & WINEDDBLTFX_ROTATE270)
3488 tmp = dTopLeft;
3489 dTopLeft = dBottomLeft;
3490 dBottomLeft = dBottomRight;
3491 dBottomRight = dTopRight;
3492 dTopRight = tmp;
3493 tmpxy = dstxinc;
3494 dstxinc = dstyinc;
3495 dstyinc = tmpxy;
3496 dstxinc = dstxinc * -1;
3498 if (fx->fx & WINEDDBLTFX_ROTATE90)
3500 tmp = dTopLeft;
3501 dTopLeft = dTopRight;
3502 dTopRight = dBottomRight;
3503 dBottomRight = dBottomLeft;
3504 dBottomLeft = tmp;
3505 tmpxy = dstxinc;
3506 dstxinc = dstyinc;
3507 dstyinc = tmpxy;
3508 dstyinc = dstyinc * -1;
3510 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
3512 /* I don't think we need to do anything about this flag. */
3513 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
3515 dbuf = dTopLeft;
3516 flags &= ~(WINED3D_BLT_FX);
3519 #define COPY_COLORKEY_FX(type) \
3520 do { \
3521 const type *s; \
3522 type *d = (type *)dbuf, *dx, tmp; \
3523 for (y = sy = 0; y < dst_height; ++y, sy += yinc) \
3525 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
3526 dx = d; \
3527 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3529 tmp = s[sx >> 16]; \
3530 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
3531 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
3533 dx[0] = tmp; \
3535 dx = (type *)(((BYTE *)dx) + dstxinc); \
3537 d = (type *)(((BYTE *)d) + dstyinc); \
3539 } while(0)
3541 switch (bpp)
3543 case 1:
3544 COPY_COLORKEY_FX(BYTE);
3545 break;
3546 case 2:
3547 COPY_COLORKEY_FX(WORD);
3548 break;
3549 case 4:
3550 COPY_COLORKEY_FX(DWORD);
3551 break;
3552 case 3:
3554 const BYTE *s;
3555 BYTE *d = dbuf, *dx;
3556 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3558 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3559 dx = d;
3560 for (x = sx = 0; x < dst_width; ++x, sx+= xinc)
3562 DWORD pixel, dpixel = 0;
3563 s = sbuf + 3 * (sx>>16);
3564 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3565 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
3566 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
3567 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
3569 dx[0] = (pixel ) & 0xff;
3570 dx[1] = (pixel >> 8) & 0xff;
3571 dx[2] = (pixel >> 16) & 0xff;
3573 dx += dstxinc;
3575 d += dstyinc;
3577 break;
3579 default:
3580 FIXME("%s color-keyed blit not implemented for bpp %u.\n",
3581 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
3582 hr = WINED3DERR_NOTAVAILABLE;
3583 goto error;
3584 #undef COPY_COLORKEY_FX
3588 error:
3589 if (flags)
3590 FIXME(" Unsupported flags %#x.\n", flags);
3592 release:
3593 context_unmap_bo_address(context, &dst_data, GL_PIXEL_UNPACK_BUFFER);
3594 if (!same_sub_resource)
3595 context_unmap_bo_address(context, &src_data, GL_PIXEL_UNPACK_BUFFER);
3596 if (SUCCEEDED(hr) && dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture)
3598 SetRect(&dst_texture->swapchain->front_buffer_update,
3599 dst_box->left, dst_box->top, dst_box->right, dst_box->bottom);
3600 dst_texture->swapchain->swapchain_ops->swapchain_frontbuffer_updated(dst_texture->swapchain);
3602 if (converted_texture)
3603 wined3d_texture_decref(converted_texture);
3604 if (context)
3605 context_release(context);
3607 return hr;
3610 static void surface_cpu_blt_colour_fill(struct wined3d_rendertarget_view *view,
3611 const struct wined3d_box *box, const struct wined3d_color *colour)
3613 struct wined3d_device *device = view->resource->device;
3614 struct wined3d_context *context = NULL;
3615 struct wined3d_texture *texture;
3616 struct wined3d_bo_address data;
3617 unsigned int x, y, w, h, bpp;
3618 struct wined3d_map_desc map;
3619 DWORD map_binding;
3620 BYTE *row;
3621 DWORD c;
3623 TRACE("view %p, box %s, colour %s.\n", view, debug_box(box), debug_color(colour));
3625 if (view->format_flags & WINED3DFMT_FLAG_BLOCKS)
3627 FIXME("Not implemented for format %s.\n", debug_d3dformat(view->format->id));
3628 return;
3631 if (view->format->id != view->resource->format->id)
3632 FIXME("View format %s doesn't match resource format %s.\n",
3633 debug_d3dformat(view->format->id), debug_d3dformat(view->resource->format->id));
3635 if (view->resource->type == WINED3D_RTYPE_BUFFER)
3637 FIXME("Not implemented for buffers.\n");
3638 return;
3641 if (device->d3d_initialized)
3642 context = context_acquire(device, NULL, 0);
3644 c = wined3d_format_convert_from_float(view->format, colour);
3645 bpp = view->format->byte_count;
3646 w = box->right - box->left;
3647 h = box->bottom - box->top;
3649 texture = texture_from_resource(view->resource);
3650 map_binding = texture->resource.map_binding;
3651 if (!wined3d_texture_load_location(texture, view->sub_resource_idx, context, map_binding))
3652 ERR("Failed to load the sub-resource into %s.\n", wined3d_debug_location(map_binding));
3653 wined3d_texture_invalidate_location(texture, view->sub_resource_idx, ~map_binding);
3654 wined3d_texture_get_pitch(texture, view->sub_resource_idx % texture->level_count,
3655 &map.row_pitch, &map.slice_pitch);
3656 wined3d_texture_get_memory(texture, view->sub_resource_idx, &data, map_binding);
3657 map.data = context_map_bo_address(context, &data,
3658 texture->sub_resources[view->sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_WRITE);
3659 map.data = (BYTE *)map.data
3660 + (box->front * map.slice_pitch)
3661 + ((box->top / view->format->block_height) * map.row_pitch)
3662 + ((box->left / view->format->block_width) * view->format->block_byte_count);
3664 switch (bpp)
3666 case 1:
3667 for (x = 0; x < w; ++x)
3669 ((BYTE *)map.data)[x] = c;
3671 break;
3673 case 2:
3674 for (x = 0; x < w; ++x)
3676 ((WORD *)map.data)[x] = c;
3678 break;
3680 case 3:
3682 row = map.data;
3683 for (x = 0; x < w; ++x, row += 3)
3685 row[0] = (c ) & 0xff;
3686 row[1] = (c >> 8) & 0xff;
3687 row[2] = (c >> 16) & 0xff;
3689 break;
3691 case 4:
3692 for (x = 0; x < w; ++x)
3694 ((DWORD *)map.data)[x] = c;
3696 break;
3698 default:
3699 FIXME("Not implemented for bpp %u.\n", bpp);
3700 wined3d_resource_unmap(view->resource, view->sub_resource_idx);
3701 return;
3704 row = map.data;
3705 for (y = 1; y < h; ++y)
3707 row += map.row_pitch;
3708 memcpy(row, map.data, w * bpp);
3711 context_unmap_bo_address(context, &data, GL_PIXEL_UNPACK_BUFFER);
3712 if (context)
3713 context_release(context);
3716 static void cpu_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
3717 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
3718 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
3720 struct wined3d_color c = {depth, 0.0f, 0.0f, 0.0f};
3721 struct wined3d_rendertarget_view *view;
3722 struct wined3d_box box;
3723 unsigned int i, j;
3725 if (!rect_count)
3727 rect_count = 1;
3728 clear_rects = draw_rect;
3731 for (i = 0; i < rect_count; ++i)
3733 box.left = max(clear_rects[i].left, draw_rect->left);
3734 box.top = max(clear_rects[i].top, draw_rect->top);
3735 box.right = min(clear_rects[i].right, draw_rect->right);
3736 box.bottom = min(clear_rects[i].bottom, draw_rect->bottom);
3737 box.front = 0;
3738 box.back = 1;
3740 if (box.left >= box.right || box.top >= box.bottom)
3741 continue;
3743 if (flags & WINED3DCLEAR_TARGET)
3745 for (j = 0; j < rt_count; ++j)
3747 if ((view = fb->render_targets[j]))
3748 surface_cpu_blt_colour_fill(view, &box, colour);
3752 if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil))
3754 if ((view->format->depth_size && !(flags & WINED3DCLEAR_ZBUFFER))
3755 || (view->format->stencil_size && !(flags & WINED3DCLEAR_STENCIL)))
3756 FIXME("Clearing %#x on %s.\n", flags, debug_d3dformat(view->format->id));
3758 surface_cpu_blt_colour_fill(view, &box, &c);
3763 static DWORD cpu_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
3764 struct wined3d_context *context, struct wined3d_surface *src_surface, DWORD src_location,
3765 const RECT *src_rect, struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect,
3766 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter)
3768 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
3769 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
3770 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
3771 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
3772 struct wined3d_texture *dst_texture = dst_surface->container;
3773 struct wined3d_texture *src_texture = src_surface->container;
3774 struct wined3d_blt_fx fx;
3775 DWORD flags = 0;
3777 memset(&fx, 0, sizeof(fx));
3778 switch (op)
3780 case WINED3D_BLIT_OP_COLOR_BLIT:
3781 case WINED3D_BLIT_OP_DEPTH_BLIT:
3782 case WINED3D_BLIT_OP_RAW_BLIT:
3783 break;
3784 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
3785 flags |= WINED3D_BLT_ALPHA_TEST;
3786 break;
3787 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
3788 flags |= WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_FX;
3789 fx.src_color_key = *color_key;
3790 break;
3791 default:
3792 FIXME("Unhandled op %#x.\n", op);
3793 break;
3796 if (FAILED(surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
3797 src_texture, src_sub_resource_idx, &src_box, flags, &fx, filter)))
3798 ERR("Failed to blit.\n");
3799 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
3801 return dst_location | (dst_texture->sub_resources[dst_sub_resource_idx].locations
3802 & dst_texture->resource.map_binding);
3805 static const struct wined3d_blitter_ops cpu_blitter_ops =
3807 cpu_blitter_destroy,
3808 cpu_blitter_clear,
3809 cpu_blitter_blit,
3812 struct wined3d_blitter *wined3d_cpu_blitter_create(void)
3814 struct wined3d_blitter *blitter;
3816 if (!(blitter = heap_alloc(sizeof(*blitter))))
3817 return NULL;
3819 TRACE("Created blitter %p.\n", blitter);
3821 blitter->ops = &cpu_blitter_ops;
3822 blitter->next = NULL;
3824 return blitter;
3827 HRESULT wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3828 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
3829 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
3831 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
3832 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
3833 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
3834 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
3835 struct wined3d_texture_sub_resource *src_sub_resource, *dst_sub_resource;
3836 struct wined3d_texture *dst_texture = dst_surface->container;
3837 struct wined3d_texture *src_texture = src_surface->container;
3838 struct wined3d_device *device = dst_texture->resource.device;
3839 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3840 const struct wined3d_color_key *colour_key = NULL;
3841 DWORD dst_location, valid_locations;
3842 DWORD src_ds_flags, dst_ds_flags;
3843 struct wined3d_context *context;
3844 enum wined3d_blit_op blit_op;
3845 BOOL scale, convert, resolve;
3847 static const DWORD simple_blit = WINED3D_BLT_SRC_CKEY
3848 | WINED3D_BLT_SRC_CKEY_OVERRIDE
3849 | WINED3D_BLT_ALPHA_TEST
3850 | WINED3D_BLT_RAW;
3852 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
3853 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3854 flags, fx, debug_d3dtexturefiltertype(filter));
3855 TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage));
3857 if (fx)
3859 TRACE("fx %#x.\n", fx->fx);
3860 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
3861 fx->dst_color_key.color_space_low_value,
3862 fx->dst_color_key.color_space_high_value);
3863 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
3864 fx->src_color_key.color_space_low_value,
3865 fx->src_color_key.color_space_high_value);
3868 if (!fx || !(fx->fx))
3869 flags &= ~WINED3D_BLT_FX;
3871 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
3872 if (flags & WINED3D_BLT_DO_NOT_WAIT)
3874 static unsigned int once;
3876 if (!once++)
3877 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
3880 flags &= ~(WINED3D_BLT_SYNCHRONOUS | WINED3D_BLT_DO_NOT_WAIT | WINED3D_BLT_WAIT);
3882 if (!device->d3d_initialized)
3884 WARN("D3D not initialized, using fallback.\n");
3885 goto cpu;
3888 if (flags & ~simple_blit)
3890 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
3891 goto fallback;
3894 src_swapchain = src_texture->swapchain;
3895 dst_swapchain = dst_texture->swapchain;
3897 /* This isn't strictly needed. FBO blits for example could deal with
3898 * cross-swapchain blits by first downloading the source to a texture
3899 * before switching to the destination context. We just have this here to
3900 * not have to deal with the issue, since cross-swapchain blits should be
3901 * rare. */
3902 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
3904 FIXME("Using fallback for cross-swapchain blit.\n");
3905 goto fallback;
3908 scale = src_rect->right - src_rect->left != dst_rect->right - dst_rect->left
3909 || src_rect->bottom - src_rect->top != dst_rect->bottom - dst_rect->top;
3910 convert = src_texture->resource.format->id != dst_texture->resource.format->id;
3911 resolve = src_texture->resource.multisample_type != dst_texture->resource.multisample_type;
3913 dst_ds_flags = dst_texture->resource.format_flags
3914 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
3915 src_ds_flags = src_texture->resource.format_flags
3916 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
3918 if (src_ds_flags || dst_ds_flags)
3920 TRACE("Depth/stencil blit.\n");
3922 if (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)
3923 dst_location = dst_texture->resource.draw_binding;
3924 else
3925 dst_location = dst_texture->resource.map_binding;
3927 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3928 valid_locations = device->blitter->ops->blitter_blit(device->blitter,
3929 WINED3D_BLIT_OP_DEPTH_BLIT, context,
3930 src_surface, src_texture->resource.draw_binding, src_rect,
3931 dst_surface, dst_location, dst_rect, NULL, filter);
3932 context_release(context);
3934 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
3935 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
3937 return WINED3D_OK;
3940 TRACE("Colour blit.\n");
3942 dst_sub_resource = &dst_texture->sub_resources[dst_sub_resource_idx];
3943 src_sub_resource = &src_texture->sub_resources[src_sub_resource_idx];
3945 /* In principle this would apply to depth blits as well, but we don't
3946 * implement those in the CPU blitter at the moment. */
3947 if ((dst_sub_resource->locations & dst_texture->resource.map_binding)
3948 && (src_sub_resource->locations & src_texture->resource.map_binding))
3950 if (scale)
3951 TRACE("Not doing sysmem blit because of scaling.\n");
3952 else if (convert)
3953 TRACE("Not doing sysmem blit because of format conversion.\n");
3954 else
3955 goto cpu;
3958 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
3959 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3961 colour_key = &fx->src_color_key;
3962 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
3964 else if (flags & WINED3D_BLT_SRC_CKEY)
3966 colour_key = &src_texture->async.src_blt_color_key;
3967 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
3969 else if (flags & WINED3D_BLT_ALPHA_TEST)
3971 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
3973 else if ((src_sub_resource->locations & surface_simple_locations)
3974 && !(dst_sub_resource->locations & surface_simple_locations))
3976 /* Upload */
3977 if (scale)
3978 TRACE("Not doing upload because of scaling.\n");
3979 else if (convert)
3980 TRACE("Not doing upload because of format conversion.\n");
3981 else if (dst_texture->resource.format->conv_byte_count)
3982 TRACE("Not doing upload because the destination format needs conversion.\n");
3983 else
3985 POINT dst_point = {dst_rect->left, dst_rect->top};
3987 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, src_rect)))
3989 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
3991 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3992 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx,
3993 context, dst_texture->resource.draw_binding);
3994 context_release(context);
3996 return WINED3D_OK;
4000 else if (dst_swapchain && dst_swapchain->back_buffers
4001 && dst_texture == dst_swapchain->front_buffer
4002 && src_texture == dst_swapchain->back_buffers[0])
4004 /* Use present for back -> front blits. The idea behind this is that
4005 * present is potentially faster than a blit, in particular when FBO
4006 * blits aren't available. Some ddraw applications like Half-Life and
4007 * Prince of Persia 3D use Blt() from the backbuffer to the
4008 * frontbuffer instead of doing a Flip(). D3d8 and d3d9 applications
4009 * can't blit directly to the frontbuffer. */
4010 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
4012 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
4014 /* Set the swap effect to COPY, we don't want the backbuffer to become
4015 * undefined. */
4016 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
4017 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, 0);
4018 dst_swapchain->desc.swap_effect = swap_effect;
4020 return WINED3D_OK;
4022 else if ((flags & WINED3D_BLT_RAW) || (!scale && !convert && !resolve))
4024 blit_op = WINED3D_BLIT_OP_RAW_BLIT;
4027 if (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)
4028 dst_location = dst_texture->resource.draw_binding;
4029 else
4030 dst_location = dst_texture->resource.map_binding;
4032 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
4033 valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context,
4034 src_surface, src_texture->resource.draw_binding, src_rect,
4035 dst_surface, dst_location, dst_rect, colour_key, filter);
4036 context_release(context);
4038 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
4039 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
4041 return WINED3D_OK;
4043 fallback:
4044 /* Special cases for render targets. */
4045 if (SUCCEEDED(surface_blt_special(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter)))
4046 return WINED3D_OK;
4048 cpu:
4049 return surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
4050 src_texture, src_sub_resource_idx, &src_box, flags, fx, filter);