wined3d: Store the EXT_fbo "renderbuffers" list in the texture instead of the surface.
[wine.git] / dlls / wined3d / surface.c
blob5a2bf52af745284d8343af1c79943a62db09b76c
1 /*
2 * Copyright 1997-2000 Marcus Meissner
3 * Copyright 1998-2000 Lionel Ulmer
4 * Copyright 2000-2001 TransGaming Technologies Inc.
5 * Copyright 2002-2005 Jason Edmeades
6 * Copyright 2002-2003 Raphael Junqueira
7 * Copyright 2004 Christian Costa
8 * Copyright 2005 Oliver Stieber
9 * Copyright 2006-2011, 2013-2014 Stefan Dösinger for CodeWeavers
10 * Copyright 2007-2008 Henri Verbeet
11 * Copyright 2006-2008 Roderick Colenbrander
12 * Copyright 2009-2011 Henri Verbeet for CodeWeavers
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
31 #include "wined3d_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(d3d);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
36 static const DWORD surface_simple_locations = WINED3D_LOCATION_SYSMEM
37 | WINED3D_LOCATION_USER_MEMORY | WINED3D_LOCATION_BUFFER;
39 struct blt_info
41 GLenum binding;
42 GLenum bind_target;
43 enum wined3d_gl_resource_type tex_type;
44 struct wined3d_vec3 texcoords[4];
47 struct float_rect
49 float l;
50 float t;
51 float r;
52 float b;
55 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
57 f->l = ((r->left * 2.0f) / w) - 1.0f;
58 f->t = ((r->top * 2.0f) / h) - 1.0f;
59 f->r = ((r->right * 2.0f) / w) - 1.0f;
60 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
63 static void texture2d_get_blt_info(const struct wined3d_texture *texture,
64 unsigned int sub_resource_idx, const RECT *rect, struct blt_info *info)
66 struct wined3d_vec3 *coords = info->texcoords;
67 struct float_rect f;
68 unsigned int level;
69 GLenum target;
70 GLsizei w, h;
72 level = sub_resource_idx % texture->level_count;
73 w = wined3d_texture_get_level_pow2_width(texture, level);
74 h = wined3d_texture_get_level_pow2_height(texture, level);
75 target = wined3d_texture_get_sub_resource_target(texture, sub_resource_idx);
77 switch (target)
79 default:
80 FIXME("Unsupported texture target %#x.\n", target);
81 /* Fall back to GL_TEXTURE_2D */
82 case GL_TEXTURE_2D:
83 info->binding = GL_TEXTURE_BINDING_2D;
84 info->bind_target = GL_TEXTURE_2D;
85 info->tex_type = WINED3D_GL_RES_TYPE_TEX_2D;
86 coords[0].x = (float)rect->left / w;
87 coords[0].y = (float)rect->top / h;
88 coords[0].z = 0.0f;
90 coords[1].x = (float)rect->right / w;
91 coords[1].y = (float)rect->top / h;
92 coords[1].z = 0.0f;
94 coords[2].x = (float)rect->left / w;
95 coords[2].y = (float)rect->bottom / h;
96 coords[2].z = 0.0f;
98 coords[3].x = (float)rect->right / w;
99 coords[3].y = (float)rect->bottom / h;
100 coords[3].z = 0.0f;
101 break;
103 case GL_TEXTURE_RECTANGLE_ARB:
104 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
105 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
106 info->tex_type = WINED3D_GL_RES_TYPE_TEX_RECT;
107 coords[0].x = rect->left; coords[0].y = rect->top; coords[0].z = 0.0f;
108 coords[1].x = rect->right; coords[1].y = rect->top; coords[1].z = 0.0f;
109 coords[2].x = rect->left; coords[2].y = rect->bottom; coords[2].z = 0.0f;
110 coords[3].x = rect->right; coords[3].y = rect->bottom; coords[3].z = 0.0f;
111 break;
113 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
114 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
115 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
116 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
117 cube_coords_float(rect, w, h, &f);
119 coords[0].x = 1.0f; coords[0].y = -f.t; coords[0].z = -f.l;
120 coords[1].x = 1.0f; coords[1].y = -f.t; coords[1].z = -f.r;
121 coords[2].x = 1.0f; coords[2].y = -f.b; coords[2].z = -f.l;
122 coords[3].x = 1.0f; coords[3].y = -f.b; coords[3].z = -f.r;
123 break;
125 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
126 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
127 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
128 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
129 cube_coords_float(rect, w, h, &f);
131 coords[0].x = -1.0f; coords[0].y = -f.t; coords[0].z = f.l;
132 coords[1].x = -1.0f; coords[1].y = -f.t; coords[1].z = f.r;
133 coords[2].x = -1.0f; coords[2].y = -f.b; coords[2].z = f.l;
134 coords[3].x = -1.0f; coords[3].y = -f.b; coords[3].z = f.r;
135 break;
137 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
138 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
139 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
140 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
141 cube_coords_float(rect, w, h, &f);
143 coords[0].x = f.l; coords[0].y = 1.0f; coords[0].z = f.t;
144 coords[1].x = f.r; coords[1].y = 1.0f; coords[1].z = f.t;
145 coords[2].x = f.l; coords[2].y = 1.0f; coords[2].z = f.b;
146 coords[3].x = f.r; coords[3].y = 1.0f; coords[3].z = f.b;
147 break;
149 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
150 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
151 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
152 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
153 cube_coords_float(rect, w, h, &f);
155 coords[0].x = f.l; coords[0].y = -1.0f; coords[0].z = -f.t;
156 coords[1].x = f.r; coords[1].y = -1.0f; coords[1].z = -f.t;
157 coords[2].x = f.l; coords[2].y = -1.0f; coords[2].z = -f.b;
158 coords[3].x = f.r; coords[3].y = -1.0f; coords[3].z = -f.b;
159 break;
161 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
162 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
163 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
164 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
165 cube_coords_float(rect, w, h, &f);
167 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1.0f;
168 coords[1].x = f.r; coords[1].y = -f.t; coords[1].z = 1.0f;
169 coords[2].x = f.l; coords[2].y = -f.b; coords[2].z = 1.0f;
170 coords[3].x = f.r; coords[3].y = -f.b; coords[3].z = 1.0f;
171 break;
173 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
174 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
175 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
176 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
177 cube_coords_float(rect, w, h, &f);
179 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1.0f;
180 coords[1].x = -f.r; coords[1].y = -f.t; coords[1].z = -1.0f;
181 coords[2].x = -f.l; coords[2].y = -f.b; coords[2].z = -1.0f;
182 coords[3].x = -f.r; coords[3].y = -f.b; coords[3].z = -1.0f;
183 break;
187 /* Context activation is done by the caller. */
188 void draw_textured_quad(struct wined3d_texture *texture, unsigned int sub_resource_idx,
189 struct wined3d_context *context, const RECT *src_rect, const RECT *dst_rect,
190 enum wined3d_texture_filter_type filter)
192 const struct wined3d_gl_info *gl_info = context->gl_info;
193 struct blt_info info;
195 texture2d_get_blt_info(texture, sub_resource_idx, src_rect, &info);
197 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
198 checkGLcall("glEnable(bind_target)");
200 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
202 /* Filtering for StretchRect */
203 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
204 checkGLcall("glTexParameteri");
205 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
206 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
207 checkGLcall("glTexParameteri");
208 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
209 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
210 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
211 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
212 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
213 checkGLcall("glTexEnvi");
215 /* Draw a quad */
216 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
217 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[0].x);
218 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
220 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[1].x);
221 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
223 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[2].x);
224 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
226 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[3].x);
227 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
228 gl_info->gl_ops.gl.p_glEnd();
230 /* Unbind the texture */
231 context_bind_texture(context, info.bind_target, 0);
233 /* We changed the filtering settings on the texture. Inform the
234 * container about this to get the filters reset properly next draw. */
235 texture->texture_rgb.sampler_desc.mag_filter = WINED3D_TEXF_POINT;
236 texture->texture_rgb.sampler_desc.min_filter = WINED3D_TEXF_POINT;
237 texture->texture_rgb.sampler_desc.mip_filter = WINED3D_TEXF_NONE;
238 texture->texture_rgb.sampler_desc.srgb_decode = FALSE;
241 /* Works correctly only for <= 4 bpp formats. */
242 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
244 masks[0] = ((1u << format->red_size) - 1) << format->red_offset;
245 masks[1] = ((1u << format->green_size) - 1) << format->green_offset;
246 masks[2] = ((1u << format->blue_size) - 1) << format->blue_offset;
249 static BOOL texture2d_is_full_rect(const struct wined3d_texture *texture, unsigned int level, const RECT *r)
251 unsigned int t;
253 t = wined3d_texture_get_level_width(texture, level);
254 if ((r->left && r->right) || abs(r->right - r->left) != t)
255 return FALSE;
256 t = wined3d_texture_get_level_height(texture, level);
257 if ((r->top && r->bottom) || abs(r->bottom - r->top) != t)
258 return FALSE;
259 return TRUE;
262 static void 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 (!texture2d_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, 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 (!texture2d_is_full_rect(dst_texture, dst_sub_resource_idx % dst_texture->level_count, &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 wined3d_texture_translate_drawable_coords(src_texture, 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 wined3d_texture_translate_drawable_coords(dst_texture, 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_texture *texture, unsigned int sub_resource_idx,
781 const struct wined3d_gl_info *gl_info, const struct wined3d_format *format, const RECT *src_rect,
782 unsigned int src_pitch, const POINT *dst_point, BOOL srgb, const struct wined3d_const_bo_address *data)
784 UINT update_w = src_rect->right - src_rect->left;
785 UINT update_h = src_rect->bottom - src_rect->top;
786 unsigned int level, layer;
787 GLenum target;
789 TRACE("texure %p, sub_resource_idx %u, gl_info %p, format %s, src_rect %s, "
790 "src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
791 texture, sub_resource_idx, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect),
792 src_pitch, wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
794 if (texture->sub_resources[sub_resource_idx].map_count)
796 WARN("Uploading a texture that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
797 texture->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
800 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE)
802 update_h *= format->height_scale.numerator;
803 update_h /= format->height_scale.denominator;
806 if (data->buffer_object)
808 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
809 checkGLcall("glBindBuffer");
812 target = wined3d_texture_get_sub_resource_target(texture, sub_resource_idx);
813 level = sub_resource_idx % texture->level_count;
814 layer = sub_resource_idx / texture->level_count;
816 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
818 unsigned int dst_row_pitch, dst_slice_pitch;
819 const BYTE *addr = data->addr;
820 GLenum internal;
822 addr += (src_rect->top / format->block_height) * src_pitch;
823 addr += (src_rect->left / format->block_width) * format->block_byte_count;
825 if (srgb)
826 internal = format->glGammaInternal;
827 else if (texture->resource.usage & WINED3DUSAGE_RENDERTARGET
828 && wined3d_resource_is_offscreen(&texture->resource))
829 internal = format->rtInternal;
830 else
831 internal = format->glInternal;
833 wined3d_format_calculate_pitch(format, 1, update_w, update_h, &dst_row_pitch, &dst_slice_pitch);
835 TRACE("Uploading compressed data, target %#x, level %u, layer %u, x %d, y %d, w %u, h %u, "
836 "format %#x, image_size %#x, addr %p.\n",
837 target, level, layer, dst_point->x, dst_point->y,
838 update_w, update_h, internal, dst_slice_pitch, addr);
840 if (dst_row_pitch == src_pitch)
842 if (target == GL_TEXTURE_2D_ARRAY)
844 GL_EXTCALL(glCompressedTexSubImage3D(target, level, dst_point->x, dst_point->y,
845 layer, update_w, update_h, 1, internal, dst_slice_pitch, addr));
847 else
849 GL_EXTCALL(glCompressedTexSubImage2D(target, level, dst_point->x, dst_point->y,
850 update_w, update_h, internal, dst_slice_pitch, addr));
853 else
855 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
856 UINT row, y;
858 /* glCompressedTexSubImage2D() ignores pixel store state, so we
859 * can't use the unpack row length like for glTexSubImage2D. */
860 for (row = 0, y = dst_point->y; row < row_count; ++row)
862 if (target == GL_TEXTURE_2D_ARRAY)
864 GL_EXTCALL(glCompressedTexSubImage3D(target, level, dst_point->x, y,
865 layer, update_w, format->block_height, 1, internal, dst_row_pitch, addr));
867 else
869 GL_EXTCALL(glCompressedTexSubImage2D(target, level, dst_point->x, y,
870 update_w, format->block_height, internal, dst_row_pitch, addr));
873 y += format->block_height;
874 addr += src_pitch;
877 checkGLcall("Upload compressed texture data");
879 else
881 const BYTE *addr = data->addr;
883 addr += src_rect->top * src_pitch;
884 addr += src_rect->left * format->byte_count;
886 TRACE("Uploading data, target %#x, level %u, layer %u, x %d, y %d, w %u, h %u, "
887 "format %#x, type %#x, addr %p.\n",
888 target, level, layer, dst_point->x, dst_point->y,
889 update_w, update_h, format->glFormat, format->glType, addr);
891 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
892 if (target == GL_TEXTURE_2D_ARRAY)
894 GL_EXTCALL(glTexSubImage3D(target, level, dst_point->x, dst_point->y,
895 layer, update_w, update_h, 1, format->glFormat, format->glType, addr));
897 else
899 gl_info->gl_ops.gl.p_glTexSubImage2D(target, level, dst_point->x, dst_point->y,
900 update_w, update_h, format->glFormat, format->glType, addr);
902 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
903 checkGLcall("Upload texture data");
906 if (data->buffer_object)
908 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
909 checkGLcall("glBindBuffer");
912 if (wined3d_settings.strict_draw_ordering)
913 gl_info->gl_ops.gl.p_glFlush();
915 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
917 struct wined3d_surface *surface = texture->sub_resources[sub_resource_idx].u.surface;
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_texture, dst_sub_resource_idx, 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 /* See also float_16_to_32() in wined3d_private.h */
979 static inline unsigned short float_32_to_16(const float *in)
981 int exp = 0;
982 float tmp = fabsf(*in);
983 unsigned int mantissa;
984 unsigned short ret;
986 /* Deal with special numbers */
987 if (*in == 0.0f)
988 return 0x0000;
989 if (isnan(*in))
990 return 0x7c01;
991 if (isinf(*in))
992 return (*in < 0.0f ? 0xfc00 : 0x7c00);
994 if (tmp < (float)(1u << 10))
998 tmp = tmp * 2.0f;
999 exp--;
1000 } while (tmp < (float)(1u << 10));
1002 else if (tmp >= (float)(1u << 11))
1006 tmp /= 2.0f;
1007 exp++;
1008 } while (tmp >= (float)(1u << 11));
1011 mantissa = (unsigned int)tmp;
1012 if (tmp - mantissa >= 0.5f)
1013 ++mantissa; /* Round to nearest, away from zero. */
1015 exp += 10; /* Normalize the mantissa. */
1016 exp += 15; /* Exponent is encoded with excess 15. */
1018 if (exp > 30) /* too big */
1020 ret = 0x7c00; /* INF */
1022 else if (exp <= 0)
1024 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1025 while (exp <= 0)
1027 mantissa = mantissa >> 1;
1028 ++exp;
1030 ret = mantissa & 0x3ff;
1032 else
1034 ret = (exp << 10) | (mantissa & 0x3ff);
1037 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1038 return ret;
1041 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
1042 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1044 unsigned short *dst_s;
1045 const float *src_f;
1046 unsigned int x, y;
1048 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1050 for (y = 0; y < h; ++y)
1052 src_f = (const float *)(src + y * pitch_in);
1053 dst_s = (unsigned short *) (dst + y * pitch_out);
1054 for (x = 0; x < w; ++x)
1056 dst_s[x] = float_32_to_16(src_f + x);
1061 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
1062 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1064 static const unsigned char convert_5to8[] =
1066 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
1067 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
1068 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
1069 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
1071 static const unsigned char convert_6to8[] =
1073 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
1074 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
1075 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
1076 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
1077 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
1078 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
1079 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
1080 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
1082 unsigned int x, y;
1084 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1086 for (y = 0; y < h; ++y)
1088 const WORD *src_line = (const WORD *)(src + y * pitch_in);
1089 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1090 for (x = 0; x < w; ++x)
1092 WORD pixel = src_line[x];
1093 dst_line[x] = 0xff000000u
1094 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
1095 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
1096 | convert_5to8[(pixel & 0x001fu)];
1101 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
1102 * in both cases we're just setting the X / Alpha channel to 0xff. */
1103 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
1104 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1106 unsigned int x, y;
1108 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1110 for (y = 0; y < h; ++y)
1112 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
1113 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1115 for (x = 0; x < w; ++x)
1117 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
1122 static inline BYTE cliptobyte(int x)
1124 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
1127 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
1128 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1130 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1131 unsigned int x, y;
1133 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1135 for (y = 0; y < h; ++y)
1137 const BYTE *src_line = src + y * pitch_in;
1138 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1139 for (x = 0; x < w; ++x)
1141 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1142 * C = Y - 16; D = U - 128; E = V - 128;
1143 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1144 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1145 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1146 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1147 * U and V are shared between the pixels. */
1148 if (!(x & 1)) /* For every even pixel, read new U and V. */
1150 d = (int) src_line[1] - 128;
1151 e = (int) src_line[3] - 128;
1152 r2 = 409 * e + 128;
1153 g2 = - 100 * d - 208 * e + 128;
1154 b2 = 516 * d + 128;
1156 c2 = 298 * ((int) src_line[0] - 16);
1157 dst_line[x] = 0xff000000
1158 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
1159 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
1160 | cliptobyte((c2 + b2) >> 8); /* blue */
1161 /* Scale RGB values to 0..255 range,
1162 * then clip them if still not in range (may be negative),
1163 * then shift them within DWORD if necessary. */
1164 src_line += 2;
1169 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
1170 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1172 unsigned int x, y;
1173 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1175 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
1177 for (y = 0; y < h; ++y)
1179 const BYTE *src_line = src + y * pitch_in;
1180 WORD *dst_line = (WORD *)(dst + y * pitch_out);
1181 for (x = 0; x < w; ++x)
1183 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1184 * C = Y - 16; D = U - 128; E = V - 128;
1185 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1186 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1187 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1188 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1189 * U and V are shared between the pixels. */
1190 if (!(x & 1)) /* For every even pixel, read new U and V. */
1192 d = (int) src_line[1] - 128;
1193 e = (int) src_line[3] - 128;
1194 r2 = 409 * e + 128;
1195 g2 = - 100 * d - 208 * e + 128;
1196 b2 = 516 * d + 128;
1198 c2 = 298 * ((int) src_line[0] - 16);
1199 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
1200 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
1201 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
1202 /* Scale RGB values to 0..255 range,
1203 * then clip them if still not in range (may be negative),
1204 * then shift them within DWORD if necessary. */
1205 src_line += 2;
1210 struct d3dfmt_converter_desc
1212 enum wined3d_format_id from, to;
1213 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
1216 static const struct d3dfmt_converter_desc converters[] =
1218 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
1219 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
1220 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1221 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1222 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
1223 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
1226 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
1227 enum wined3d_format_id to)
1229 unsigned int i;
1231 for (i = 0; i < ARRAY_SIZE(converters); ++i)
1233 if (converters[i].from == from && converters[i].to == to)
1234 return &converters[i];
1237 return NULL;
1240 static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture,
1241 unsigned int sub_resource_idx, const struct wined3d_format *dst_format)
1243 unsigned int texture_level = sub_resource_idx % src_texture->level_count;
1244 const struct wined3d_format *src_format = src_texture->resource.format;
1245 struct wined3d_device *device = src_texture->resource.device;
1246 const struct d3dfmt_converter_desc *conv = NULL;
1247 const struct wined3d_gl_info *gl_info = NULL;
1248 unsigned int src_row_pitch, src_slice_pitch;
1249 struct wined3d_context *context = NULL;
1250 struct wined3d_texture *dst_texture;
1251 struct wined3d_bo_address src_data;
1252 struct wined3d_resource_desc desc;
1253 DWORD map_binding;
1255 if (!(conv = find_converter(src_format->id, dst_format->id)) && (!device->d3d_initialized
1256 || !is_identity_fixup(src_format->color_fixup) || src_format->conv_byte_count
1257 || !is_identity_fixup(dst_format->color_fixup) || dst_format->conv_byte_count
1258 || (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)))
1260 FIXME("Cannot find a conversion function from format %s to %s.\n",
1261 debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
1262 return NULL;
1265 /* FIXME: Multisampled conversion? */
1266 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
1267 desc.format = dst_format->id;
1268 desc.multisample_type = WINED3D_MULTISAMPLE_NONE;
1269 desc.multisample_quality = 0;
1270 desc.usage = WINED3DUSAGE_SCRATCH | WINED3DUSAGE_PRIVATE;
1271 desc.access = WINED3D_RESOURCE_ACCESS_CPU | WINED3D_RESOURCE_ACCESS_MAP_R | WINED3D_RESOURCE_ACCESS_MAP_W;
1272 desc.width = wined3d_texture_get_level_width(src_texture, texture_level);
1273 desc.height = wined3d_texture_get_level_height(src_texture, texture_level);
1274 desc.depth = 1;
1275 desc.size = 0;
1276 if (FAILED(wined3d_texture_create(device, &desc, 1, 1,
1277 WINED3D_TEXTURE_CREATE_MAPPABLE | WINED3D_TEXTURE_CREATE_DISCARD,
1278 NULL, NULL, &wined3d_null_parent_ops, &dst_texture)))
1280 ERR("Failed to create a destination texture for conversion.\n");
1281 return NULL;
1284 if (device->d3d_initialized)
1286 context = context_acquire(device, NULL, 0);
1287 gl_info = context->gl_info;
1290 map_binding = src_texture->resource.map_binding;
1291 if (!wined3d_texture_load_location(src_texture, sub_resource_idx, context, map_binding))
1292 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
1293 wined3d_texture_get_pitch(src_texture, texture_level, &src_row_pitch, &src_slice_pitch);
1294 wined3d_texture_get_memory(src_texture, sub_resource_idx, &src_data, map_binding);
1296 if (conv)
1298 unsigned int dst_row_pitch, dst_slice_pitch;
1299 struct wined3d_bo_address dst_data;
1300 const BYTE *src;
1301 BYTE *dst;
1303 map_binding = dst_texture->resource.map_binding;
1304 if (!wined3d_texture_load_location(dst_texture, 0, context, map_binding))
1305 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
1306 wined3d_texture_get_pitch(dst_texture, 0, &dst_row_pitch, &dst_slice_pitch);
1307 wined3d_texture_get_memory(dst_texture, 0, &dst_data, map_binding);
1309 src = context_map_bo_address(context, &src_data,
1310 src_texture->sub_resources[sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ);
1311 dst = context_map_bo_address(context,
1312 &dst_data, dst_texture->sub_resources[0].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_WRITE);
1314 conv->convert(src, dst, src_row_pitch, dst_row_pitch, desc.width, desc.height);
1316 wined3d_texture_invalidate_location(dst_texture, 0, ~map_binding);
1317 context_unmap_bo_address(context, &dst_data, GL_PIXEL_UNPACK_BUFFER);
1318 context_unmap_bo_address(context, &src_data, GL_PIXEL_UNPACK_BUFFER);
1320 else
1322 RECT src_rect = {0, 0, desc.width, desc.height};
1323 POINT dst_point = {0, 0};
1325 TRACE("Using upload conversion.\n");
1327 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
1328 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
1329 wined3d_surface_upload_data(dst_texture, 0, gl_info, src_format,
1330 &src_rect, src_row_pitch, &dst_point, FALSE, wined3d_const_bo_address(&src_data));
1332 wined3d_texture_validate_location(dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB);
1333 wined3d_texture_invalidate_location(dst_texture, 0, ~WINED3D_LOCATION_TEXTURE_RGB);
1336 if (context)
1337 context_release(context);
1339 return dst_texture;
1342 static void read_from_framebuffer(struct wined3d_surface *surface,
1343 struct wined3d_context *old_ctx, DWORD src_location, DWORD dst_location)
1345 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
1346 struct wined3d_texture *texture = surface->container;
1347 struct wined3d_device *device = texture->resource.device;
1348 struct wined3d_context *context = old_ctx;
1349 struct wined3d_surface *restore_rt = NULL;
1350 const struct wined3d_gl_info *gl_info;
1351 unsigned int row_pitch, slice_pitch;
1352 unsigned int width, height, level;
1353 struct wined3d_bo_address data;
1354 BYTE *row, *top, *bottom;
1355 BOOL src_is_upside_down;
1356 unsigned int i;
1357 BYTE *mem;
1359 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
1361 restore_rt = context_get_rt_surface(old_ctx);
1362 if (restore_rt != surface)
1363 context = context_acquire(device, texture, sub_resource_idx);
1364 else
1365 restore_rt = NULL;
1366 gl_info = context->gl_info;
1368 if (src_location != texture->resource.draw_binding)
1370 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, surface, NULL, src_location);
1371 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
1372 context_invalidate_state(context, STATE_FRAMEBUFFER);
1374 else
1376 context_apply_blit_state(context, device);
1379 /* Select the correct read buffer, and give some debug output.
1380 * There is no need to keep track of the current read buffer or reset it,
1381 * every part of the code that reads sets the read buffer as desired.
1383 if (src_location != WINED3D_LOCATION_DRAWABLE || wined3d_resource_is_offscreen(&texture->resource))
1385 /* Mapping the primary render target which is not on a swapchain.
1386 * Read from the back buffer. */
1387 TRACE("Mapping offscreen render target.\n");
1388 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1389 src_is_upside_down = TRUE;
1391 else
1393 /* Onscreen surfaces are always part of a swapchain */
1394 GLenum buffer = wined3d_texture_get_gl_buffer(texture);
1395 TRACE("Mapping %#x buffer.\n", buffer);
1396 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
1397 src_is_upside_down = FALSE;
1399 checkGLcall("glReadBuffer");
1401 if (data.buffer_object)
1403 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1404 checkGLcall("glBindBuffer");
1407 level = sub_resource_idx % texture->level_count;
1408 wined3d_texture_get_pitch(texture, level, &row_pitch, &slice_pitch);
1410 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1411 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / texture->resource.format->byte_count);
1412 checkGLcall("glPixelStorei");
1414 width = wined3d_texture_get_level_width(texture, level);
1415 height = wined3d_texture_get_level_height(texture, level);
1416 gl_info->gl_ops.gl.p_glReadPixels(0, 0, width, height,
1417 texture->resource.format->glFormat,
1418 texture->resource.format->glType, data.addr);
1419 checkGLcall("glReadPixels");
1421 /* Reset previous pixel store pack state */
1422 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
1423 checkGLcall("glPixelStorei");
1425 if (!src_is_upside_down)
1427 /* glReadPixels returns the image upside down, and there is no way to
1428 * prevent this. Flip the lines in software. */
1430 if (!(row = heap_alloc(row_pitch)))
1431 goto error;
1433 if (data.buffer_object)
1435 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
1436 checkGLcall("glMapBuffer");
1438 else
1439 mem = data.addr;
1441 top = mem;
1442 bottom = mem + row_pitch * (height - 1);
1443 for (i = 0; i < height / 2; i++)
1445 memcpy(row, top, row_pitch);
1446 memcpy(top, bottom, row_pitch);
1447 memcpy(bottom, row, row_pitch);
1448 top += row_pitch;
1449 bottom -= row_pitch;
1451 heap_free(row);
1453 if (data.buffer_object)
1454 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
1457 error:
1458 if (data.buffer_object)
1460 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1461 checkGLcall("glBindBuffer");
1464 if (restore_rt)
1465 context_restore(context, restore_rt);
1468 /* Read the framebuffer contents into a texture. Note that this function
1469 * doesn't do any kind of flipping. Using this on an onscreen surface will
1470 * result in a flipped D3D texture.
1472 * Context activation is done by the caller. This function may temporarily
1473 * switch to a different context and restore the original one before return. */
1474 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb, struct wined3d_context *old_ctx)
1476 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
1477 struct wined3d_texture *texture = surface->container;
1478 struct wined3d_device *device = texture->resource.device;
1479 const struct wined3d_gl_info *gl_info;
1480 struct wined3d_context *context = old_ctx;
1481 struct wined3d_surface *restore_rt = NULL;
1482 unsigned int level;
1483 GLenum target;
1485 restore_rt = context_get_rt_surface(old_ctx);
1486 if (restore_rt != surface)
1487 context = context_acquire(device, texture, sub_resource_idx);
1488 else
1489 restore_rt = NULL;
1491 gl_info = context->gl_info;
1492 device_invalidate_state(device, STATE_FRAMEBUFFER);
1494 wined3d_texture_prepare_texture(texture, context, srgb);
1495 wined3d_texture_bind_and_dirtify(texture, context, srgb);
1497 TRACE("Reading back offscreen render target %p.\n", surface);
1499 if (wined3d_resource_is_offscreen(&texture->resource))
1500 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1501 else
1502 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(texture));
1503 checkGLcall("glReadBuffer");
1505 level = sub_resource_idx % texture->level_count;
1506 target = wined3d_texture_get_sub_resource_target(texture, sub_resource_idx);
1507 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(target, level, 0, 0, 0, 0,
1508 wined3d_texture_get_level_width(texture, level),
1509 wined3d_texture_get_level_height(texture, level));
1510 checkGLcall("glCopyTexSubImage2D");
1512 if (restore_rt)
1513 context_restore(context, restore_rt);
1516 /* Does a direct frame buffer -> texture copy. Stretching is done with single
1517 * pixel copy calls. */
1518 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
1519 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
1521 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
1522 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1523 struct wined3d_texture *src_texture = src_surface->container;
1524 struct wined3d_texture *dst_texture = dst_surface->container;
1525 struct wined3d_device *device = dst_texture->resource.device;
1526 unsigned int src_height, src_level, dst_level;
1527 const struct wined3d_gl_info *gl_info;
1528 float xrel, yrel;
1529 struct wined3d_context *context;
1530 BOOL upsidedown = FALSE;
1531 RECT dst_rect = *dst_rect_in;
1532 GLenum dst_target;
1534 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1535 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1537 if(dst_rect.top > dst_rect.bottom) {
1538 UINT tmp = dst_rect.bottom;
1539 dst_rect.bottom = dst_rect.top;
1540 dst_rect.top = tmp;
1541 upsidedown = TRUE;
1544 context = context_acquire(device, src_texture, src_sub_resource_idx);
1545 gl_info = context->gl_info;
1546 context_apply_blit_state(context, device);
1547 wined3d_texture_load(dst_texture, context, FALSE);
1549 /* Bind the target texture */
1550 context_bind_texture(context, dst_texture->target, dst_texture->texture_rgb.name);
1551 if (wined3d_resource_is_offscreen(&src_texture->resource))
1553 TRACE("Reading from an offscreen target\n");
1554 upsidedown = !upsidedown;
1555 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1557 else
1559 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
1561 checkGLcall("glReadBuffer");
1563 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
1564 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
1566 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1568 FIXME_(d3d_perf)("Doing a pixel by pixel copy from the framebuffer to a texture.\n");
1570 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1571 ERR("Texture filtering not supported in direct blit.\n");
1573 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1574 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1576 ERR("Texture filtering not supported in direct blit\n");
1579 src_level = src_sub_resource_idx % src_texture->level_count;
1580 dst_level = dst_sub_resource_idx % dst_texture->level_count;
1582 src_height = wined3d_texture_get_level_height(src_texture, src_level);
1583 dst_target = wined3d_texture_get_sub_resource_target(dst_texture, dst_sub_resource_idx);
1584 if (upsidedown
1585 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1586 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1588 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
1589 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_target, dst_level,
1590 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
1591 src_rect->left, src_height - src_rect->bottom,
1592 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1594 else
1596 LONG row;
1597 UINT yoffset = src_height - src_rect->top + dst_rect.top - 1;
1598 /* I have to process this row by row to swap the image,
1599 * otherwise it would be upside down, so stretching in y direction
1600 * doesn't cost extra time
1602 * However, stretching in x direction can be avoided if not necessary
1604 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
1605 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1607 /* Well, that stuff works, but it's very slow.
1608 * find a better way instead
1610 LONG col;
1612 for (col = dst_rect.left; col < dst_rect.right; ++col)
1614 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_target, dst_level,
1615 dst_rect.left + col /* x offset */, row /* y offset */,
1616 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
1619 else
1621 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_target, dst_level,
1622 dst_rect.left /* x offset */, row /* y offset */,
1623 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
1627 checkGLcall("glCopyTexSubImage2D");
1629 context_release(context);
1631 /* The texture is now most up to date - If the surface is a render target
1632 * and has a drawable, this path is never entered. */
1633 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1634 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1637 /* Uses the hardware to stretch and flip the image */
1638 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
1639 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
1641 unsigned int src_width, src_height, src_pow2_width, src_pow2_height, src_level;
1642 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
1643 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1644 struct wined3d_texture *src_texture = src_surface->container;
1645 struct wined3d_texture *dst_texture = dst_surface->container;
1646 struct wined3d_device *device = dst_texture->resource.device;
1647 GLenum src_target, dst_target, texture_target;
1648 GLuint src, backup = 0;
1649 float left, right, top, bottom; /* Texture coordinates */
1650 const struct wined3d_gl_info *gl_info;
1651 struct wined3d_context *context;
1652 GLenum drawBuffer = GL_BACK;
1653 GLenum offscreen_buffer;
1654 BOOL noBackBufferBackup;
1655 BOOL src_offscreen;
1656 BOOL upsidedown = FALSE;
1657 RECT dst_rect = *dst_rect_in;
1659 TRACE("Using hwstretch blit\n");
1661 src_target = wined3d_texture_get_sub_resource_target(src_texture, src_sub_resource_idx);
1662 dst_target = wined3d_texture_get_sub_resource_target(dst_texture, dst_sub_resource_idx);
1664 /* Activate the Proper context for reading from the source surface, set it up for blitting */
1665 context = context_acquire(device, src_texture, src_sub_resource_idx);
1666 gl_info = context->gl_info;
1667 context_apply_blit_state(context, device);
1668 wined3d_texture_load(dst_texture, context, FALSE);
1670 offscreen_buffer = context_get_offscreen_gl_buffer(context);
1671 src_level = src_sub_resource_idx % src_texture->level_count;
1672 src_width = wined3d_texture_get_level_width(src_texture, src_level);
1673 src_height = wined3d_texture_get_level_height(src_texture, src_level);
1674 src_pow2_width = wined3d_texture_get_level_pow2_width(src_texture, src_level);
1675 src_pow2_height = wined3d_texture_get_level_pow2_height(src_texture, src_level);
1677 src_offscreen = wined3d_resource_is_offscreen(&src_texture->resource);
1678 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
1679 if (!noBackBufferBackup && !src_texture->texture_rgb.name)
1681 /* Get it a description */
1682 wined3d_texture_load(src_texture, context, FALSE);
1685 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
1686 * This way we don't have to wait for the 2nd readback to finish to leave this function.
1688 if (context->aux_buffers >= 2)
1690 /* Got more than one aux buffer? Use the 2nd aux buffer */
1691 drawBuffer = GL_AUX1;
1693 else if ((!src_offscreen || offscreen_buffer == GL_BACK) && context->aux_buffers >= 1)
1695 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
1696 drawBuffer = GL_AUX0;
1699 if (noBackBufferBackup)
1701 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
1702 checkGLcall("glGenTextures");
1703 context_bind_texture(context, GL_TEXTURE_2D, backup);
1704 texture_target = GL_TEXTURE_2D;
1706 else
1708 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
1709 * we are reading from the back buffer, the backup can be used as source texture
1711 texture_target = src_target;
1712 context_bind_texture(context, texture_target, src_texture->texture_rgb.name);
1713 gl_info->gl_ops.gl.p_glEnable(texture_target);
1714 checkGLcall("glEnable(texture_target)");
1716 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
1717 surface_get_sub_resource(src_surface)->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
1720 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1721 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1723 if(dst_rect.top > dst_rect.bottom) {
1724 UINT tmp = dst_rect.bottom;
1725 dst_rect.bottom = dst_rect.top;
1726 dst_rect.top = tmp;
1727 upsidedown = TRUE;
1730 if (src_offscreen)
1732 TRACE("Reading from an offscreen target\n");
1733 upsidedown = !upsidedown;
1734 gl_info->gl_ops.gl.p_glReadBuffer(offscreen_buffer);
1736 else
1738 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
1741 /* TODO: Only back up the part that will be overwritten */
1742 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, src_width, src_height);
1744 checkGLcall("glCopyTexSubImage2D");
1746 /* No issue with overriding these - the sampler is dirty due to blit usage */
1747 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
1748 checkGLcall("glTexParameteri");
1749 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
1750 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
1751 checkGLcall("glTexParameteri");
1753 if (!src_texture->swapchain || src_texture == src_texture->swapchain->back_buffers[0])
1755 src = backup ? backup : src_texture->texture_rgb.name;
1757 else
1759 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
1760 checkGLcall("glReadBuffer(GL_FRONT)");
1762 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
1763 checkGLcall("glGenTextures(1, &src)");
1764 context_bind_texture(context, GL_TEXTURE_2D, src);
1766 /* TODO: Only copy the part that will be read. Use src_rect->left,
1767 * src_rect->bottom as origin, but with the width watch out for power
1768 * of 2 sizes. */
1769 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_pow2_width,
1770 src_pow2_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
1771 checkGLcall("glTexImage2D");
1772 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, src_width, src_height);
1774 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1775 checkGLcall("glTexParameteri");
1776 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1777 checkGLcall("glTexParameteri");
1779 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
1780 checkGLcall("glReadBuffer(GL_BACK)");
1782 if (texture_target != GL_TEXTURE_2D)
1784 gl_info->gl_ops.gl.p_glDisable(texture_target);
1785 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
1786 texture_target = GL_TEXTURE_2D;
1789 checkGLcall("glEnd and previous");
1791 left = src_rect->left;
1792 right = src_rect->right;
1794 if (!upsidedown)
1796 top = src_height - src_rect->top;
1797 bottom = src_height - src_rect->bottom;
1799 else
1801 top = src_height - src_rect->bottom;
1802 bottom = src_height - src_rect->top;
1805 if (src_texture->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
1807 left /= src_pow2_width;
1808 right /= src_pow2_width;
1809 top /= src_pow2_height;
1810 bottom /= src_pow2_height;
1813 /* draw the source texture stretched and upside down. The correct surface is bound already */
1814 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1815 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1817 context_set_draw_buffer(context, drawBuffer);
1818 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
1820 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
1821 /* bottom left */
1822 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
1823 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
1825 /* top left */
1826 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
1827 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
1829 /* top right */
1830 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
1831 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1833 /* bottom right */
1834 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
1835 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
1836 gl_info->gl_ops.gl.p_glEnd();
1837 checkGLcall("glEnd and previous");
1839 if (texture_target != dst_target)
1841 gl_info->gl_ops.gl.p_glDisable(texture_target);
1842 gl_info->gl_ops.gl.p_glEnable(dst_target);
1843 texture_target = dst_target;
1846 /* Now read the stretched and upside down image into the destination texture */
1847 context_bind_texture(context, texture_target, dst_texture->texture_rgb.name);
1848 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
1850 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
1851 0, 0, /* We blitted the image to the origin */
1852 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1853 checkGLcall("glCopyTexSubImage2D");
1855 if (drawBuffer == GL_BACK)
1857 /* Write the back buffer backup back. */
1858 if (backup)
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;
1866 context_bind_texture(context, GL_TEXTURE_2D, backup);
1868 else
1870 if (texture_target != src_target)
1872 gl_info->gl_ops.gl.p_glDisable(texture_target);
1873 gl_info->gl_ops.gl.p_glEnable(src_target);
1874 texture_target = src_target;
1876 context_bind_texture(context, src_target, src_texture->texture_rgb.name);
1879 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
1880 /* top left */
1881 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
1882 gl_info->gl_ops.gl.p_glVertex2i(0, src_height);
1884 /* bottom left */
1885 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)src_height / (float)src_pow2_height);
1886 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
1888 /* bottom right */
1889 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width,
1890 (float)src_height / (float)src_pow2_height);
1891 gl_info->gl_ops.gl.p_glVertex2i(src_width, 0);
1893 /* top right */
1894 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width, 0.0f);
1895 gl_info->gl_ops.gl.p_glVertex2i(src_width, src_height);
1896 gl_info->gl_ops.gl.p_glEnd();
1898 gl_info->gl_ops.gl.p_glDisable(texture_target);
1899 checkGLcall("glDisable(texture_target)");
1901 /* Cleanup */
1902 if (src != src_texture->texture_rgb.name && src != backup)
1904 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
1905 checkGLcall("glDeleteTextures(1, &src)");
1907 if (backup)
1909 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
1910 checkGLcall("glDeleteTextures(1, &backup)");
1913 if (wined3d_settings.strict_draw_ordering)
1914 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
1916 context_release(context);
1918 /* The texture is now most up to date - If the surface is a render target
1919 * and has a drawable, this path is never entered. */
1920 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1921 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1924 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
1925 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
1926 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
1928 struct wined3d_texture *dst_texture = dst_surface->container;
1929 struct wined3d_device *device = dst_texture->resource.device;
1930 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
1931 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1932 struct wined3d_texture *src_texture;
1934 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1935 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
1936 flags, fx, debug_d3dtexturefiltertype(filter));
1938 /* Get the swapchain. One of the surfaces has to be a primary surface. */
1939 if (!(dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1941 WARN("Destination resource is not GPU accessible, rejecting GL blit.\n");
1942 return WINED3DERR_INVALIDCALL;
1945 dst_swapchain = dst_texture->swapchain;
1947 if (src_surface)
1949 src_texture = src_surface->container;
1950 if (!(src_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU))
1952 WARN("Source resource is not GPU accessible, rejecting GL blit.\n");
1953 return WINED3DERR_INVALIDCALL;
1956 src_swapchain = src_texture->swapchain;
1958 else
1960 src_texture = NULL;
1961 src_swapchain = NULL;
1964 /* Early sort out of cases where no render target is used */
1965 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
1967 TRACE("No surface is render target, not using hardware blit.\n");
1968 return WINED3DERR_INVALIDCALL;
1971 /* No destination color keying supported */
1972 if (flags & (WINED3D_BLT_DST_CKEY | WINED3D_BLT_DST_CKEY_OVERRIDE))
1974 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
1975 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
1976 return WINED3DERR_INVALIDCALL;
1979 if (dst_swapchain && dst_swapchain == src_swapchain)
1981 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
1982 return WINED3DERR_INVALIDCALL;
1985 if (dst_swapchain && src_swapchain)
1987 FIXME("Implement hardware blit between two different swapchains\n");
1988 return WINED3DERR_INVALIDCALL;
1991 if (dst_swapchain)
1993 /* Handled with regular texture -> swapchain blit */
1994 if (src_surface == rt)
1995 TRACE("Blit from active render target to a swapchain\n");
1997 else if (src_swapchain && dst_surface == rt)
1999 FIXME("Implement blit from a swapchain to the active render target\n");
2000 return WINED3DERR_INVALIDCALL;
2003 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
2005 unsigned int src_level, src_width, src_height;
2006 /* Blit from render target to texture */
2007 BOOL stretchx;
2009 /* P8 read back is not implemented */
2010 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT
2011 || dst_texture->resource.format->id == WINED3DFMT_P8_UINT)
2013 TRACE("P8 read back not supported by frame buffer to texture blit\n");
2014 return WINED3DERR_INVALIDCALL;
2017 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_SRC_CKEY_OVERRIDE))
2019 TRACE("Color keying not supported by frame buffer to texture blit\n");
2020 return WINED3DERR_INVALIDCALL;
2021 /* Destination color key is checked above */
2024 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
2025 stretchx = TRUE;
2026 else
2027 stretchx = FALSE;
2029 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2030 * flip the image nor scale it.
2032 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2033 * -> If the app wants an image width an unscaled width, copy it line per line
2034 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
2035 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2036 * back buffer. This is slower than reading line per line, thus not used for flipping
2037 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2038 * pixel by pixel. */
2039 src_level = surface_get_sub_resource_idx(src_surface) % src_texture->level_count;
2040 src_width = wined3d_texture_get_level_width(src_texture, src_level);
2041 src_height = wined3d_texture_get_level_height(src_texture, src_level);
2042 if (!stretchx || dst_rect->right - dst_rect->left > src_width
2043 || dst_rect->bottom - dst_rect->top > src_height)
2045 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
2046 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
2048 else
2050 TRACE("Using hardware stretching to flip / stretch the texture.\n");
2051 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
2054 return WINED3D_OK;
2057 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
2058 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
2059 return WINED3DERR_INVALIDCALL;
2062 /* Context activation is done by the caller. */
2063 static BOOL surface_load_sysmem(struct wined3d_surface *surface,
2064 struct wined3d_context *context, DWORD dst_location)
2066 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2067 const struct wined3d_gl_info *gl_info = context->gl_info;
2068 struct wined3d_texture *texture = surface->container;
2069 struct wined3d_texture_sub_resource *sub_resource;
2071 sub_resource = &texture->sub_resources[sub_resource_idx];
2072 wined3d_texture_prepare_location(texture, sub_resource_idx, context, dst_location);
2074 /* We cannot download data from multisample textures directly. */
2075 if (is_multisample_location(texture, WINED3D_LOCATION_TEXTURE_RGB))
2077 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_RB_RESOLVED);
2078 read_from_framebuffer(surface, context, WINED3D_LOCATION_RB_RESOLVED, dst_location);
2079 return TRUE;
2081 else
2083 if (sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
2084 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
2086 /* Download the surface to system memory. */
2087 if (sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
2089 wined3d_texture_bind_and_dirtify(texture, context,
2090 !(sub_resource->locations & WINED3D_LOCATION_TEXTURE_RGB));
2091 surface_download_data(surface, gl_info, dst_location);
2092 ++texture->download_count;
2094 return TRUE;
2098 if (!(texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2099 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
2101 read_from_framebuffer(surface, context, texture->resource.draw_binding, dst_location);
2102 return TRUE;
2105 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
2106 surface, wined3d_debug_location(sub_resource->locations));
2107 return FALSE;
2110 /* Context activation is done by the caller. */
2111 static BOOL surface_load_drawable(struct wined3d_surface *surface,
2112 struct wined3d_context *context)
2114 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2115 struct wined3d_texture *texture = surface->container;
2116 struct wined3d_surface *restore_rt = NULL;
2117 struct wined3d_device *device;
2118 unsigned int level;
2119 RECT r;
2121 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2123 DWORD current = texture->sub_resources[sub_resource_idx].locations;
2124 FIXME("Unimplemented copy from %s for depth/stencil buffers.\n",
2125 wined3d_debug_location(current));
2126 return FALSE;
2129 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
2130 && wined3d_resource_is_offscreen(&texture->resource))
2132 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
2133 return FALSE;
2136 device = texture->resource.device;
2137 restore_rt = context_get_rt_surface(context);
2138 if (restore_rt != surface)
2139 context = context_acquire(device, texture, sub_resource_idx);
2140 else
2141 restore_rt = NULL;
2143 level = sub_resource_idx % texture->level_count;
2144 SetRect(&r, 0, 0, wined3d_texture_get_level_width(texture, level),
2145 wined3d_texture_get_level_height(texture, level));
2146 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
2147 device->blitter->ops->blitter_blit(device->blitter, WINED3D_BLIT_OP_COLOR_BLIT, context,
2148 surface, WINED3D_LOCATION_TEXTURE_RGB, &r,
2149 surface, WINED3D_LOCATION_DRAWABLE, &r,
2150 NULL, WINED3D_TEXF_POINT);
2152 if (restore_rt)
2153 context_restore(context, restore_rt);
2155 return TRUE;
2158 static BOOL surface_load_texture(struct wined3d_surface *surface,
2159 struct wined3d_context *context, BOOL srgb)
2161 unsigned int width, height, level, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch;
2162 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2163 const struct wined3d_gl_info *gl_info = context->gl_info;
2164 struct wined3d_texture *texture = surface->container;
2165 struct wined3d_device *device = texture->resource.device;
2166 const struct wined3d_color_key_conversion *conversion;
2167 struct wined3d_texture_sub_resource *sub_resource;
2168 struct wined3d_bo_address data;
2169 BYTE *src_mem, *dst_mem = NULL;
2170 struct wined3d_format format;
2171 POINT dst_point = {0, 0};
2172 RECT src_rect;
2173 BOOL depth;
2175 depth = texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL;
2176 sub_resource = surface_get_sub_resource(surface);
2178 if (!depth && wined3d_settings.offscreen_rendering_mode != ORM_FBO
2179 && wined3d_resource_is_offscreen(&texture->resource)
2180 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
2182 surface_load_fb_texture(surface, srgb, context);
2184 return TRUE;
2187 level = sub_resource_idx % texture->level_count;
2188 width = wined3d_texture_get_level_width(texture, level);
2189 height = wined3d_texture_get_level_height(texture, level);
2190 SetRect(&src_rect, 0, 0, width, height);
2192 if (!depth && sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
2193 && (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
2194 && fbo_blitter_supported(WINED3D_BLIT_OP_COLOR_BLIT, gl_info,
2195 &texture->resource, WINED3D_LOCATION_TEXTURE_RGB,
2196 &texture->resource, WINED3D_LOCATION_TEXTURE_SRGB))
2198 if (srgb)
2199 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
2200 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
2201 else
2202 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
2203 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
2205 return TRUE;
2208 if (!depth && sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
2209 && (!srgb || (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)))
2211 DWORD src_location = sub_resource->locations & WINED3D_LOCATION_RB_RESOLVED ?
2212 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
2213 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2215 if (fbo_blitter_supported(WINED3D_BLIT_OP_COLOR_BLIT, gl_info,
2216 &texture->resource, src_location, &texture->resource, dst_location))
2217 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, src_location,
2218 &src_rect, surface, dst_location, &src_rect);
2220 return TRUE;
2223 /* Upload from system memory */
2225 if (srgb)
2227 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | texture->resource.map_binding))
2228 == WINED3D_LOCATION_TEXTURE_RGB)
2230 FIXME_(d3d_perf)("Downloading RGB surface %p to reload it as sRGB.\n", surface);
2231 wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding);
2234 else
2236 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | texture->resource.map_binding))
2237 == WINED3D_LOCATION_TEXTURE_SRGB)
2239 FIXME_(d3d_perf)("Downloading sRGB surface %p to reload it as RGB.\n", surface);
2240 wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding);
2244 if (!(sub_resource->locations & surface_simple_locations))
2246 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
2247 /* Lets hope we get it from somewhere... */
2248 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM);
2251 wined3d_texture_prepare_texture(texture, context, srgb);
2252 wined3d_texture_bind_and_dirtify(texture, context, srgb);
2253 wined3d_texture_get_pitch(texture, level, &src_row_pitch, &src_slice_pitch);
2255 format = *texture->resource.format;
2256 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
2257 format = *wined3d_get_format(gl_info, conversion->dst_format, texture->resource.usage);
2259 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
2260 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
2261 * getting called. */
2262 if ((format.conv_byte_count || conversion) && texture->sub_resources[sub_resource_idx].buffer_object)
2264 TRACE("Removing the pbo attached to surface %p.\n", surface);
2266 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM);
2267 wined3d_texture_set_map_binding(texture, WINED3D_LOCATION_SYSMEM);
2270 wined3d_texture_get_memory(texture, sub_resource_idx, &data, sub_resource->locations);
2271 if (format.conv_byte_count)
2273 /* This code is entered for texture formats which need a fixup. */
2274 format.byte_count = format.conv_byte_count;
2275 wined3d_format_calculate_pitch(&format, 1, width, height, &dst_row_pitch, &dst_slice_pitch);
2277 src_mem = context_map_bo_address(context, &data, src_slice_pitch,
2278 GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ);
2279 if (!(dst_mem = heap_alloc(dst_slice_pitch)))
2281 ERR("Out of memory (%u).\n", dst_slice_pitch);
2282 context_release(context);
2283 return FALSE;
2285 format.upload(src_mem, dst_mem, src_row_pitch, src_slice_pitch,
2286 dst_row_pitch, dst_slice_pitch, width, height, 1);
2287 src_row_pitch = dst_row_pitch;
2288 context_unmap_bo_address(context, &data, GL_PIXEL_UNPACK_BUFFER);
2290 data.buffer_object = 0;
2291 data.addr = dst_mem;
2293 else if (conversion)
2295 /* This code is only entered for color keying fixups */
2296 struct wined3d_palette *palette = NULL;
2298 wined3d_format_calculate_pitch(&format, device->surface_alignment,
2299 width, height, &dst_row_pitch, &dst_slice_pitch);
2301 src_mem = context_map_bo_address(context, &data, src_slice_pitch,
2302 GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ);
2303 if (!(dst_mem = heap_alloc(dst_slice_pitch)))
2305 ERR("Out of memory (%u).\n", dst_slice_pitch);
2306 context_release(context);
2307 return FALSE;
2309 if (texture->swapchain && texture->swapchain->palette)
2310 palette = texture->swapchain->palette;
2311 conversion->convert(src_mem, src_row_pitch, dst_mem, dst_row_pitch,
2312 width, height, palette, &texture->async.gl_color_key);
2313 src_row_pitch = dst_row_pitch;
2314 context_unmap_bo_address(context, &data, GL_PIXEL_UNPACK_BUFFER);
2316 data.buffer_object = 0;
2317 data.addr = dst_mem;
2320 wined3d_surface_upload_data(texture, sub_resource_idx, gl_info, &format, &src_rect,
2321 src_row_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
2323 heap_free(dst_mem);
2325 return TRUE;
2328 /* Context activation is done by the caller. */
2329 static BOOL surface_load_renderbuffer(struct wined3d_surface *surface, struct wined3d_context *context,
2330 DWORD dst_location)
2332 struct wined3d_texture *texture = surface->container;
2333 unsigned int level = surface_get_sub_resource_idx(surface) % texture->level_count;
2334 const RECT rect = {0, 0,
2335 wined3d_texture_get_level_width(texture, level),
2336 wined3d_texture_get_level_height(texture, level)};
2337 DWORD locations = surface_get_sub_resource(surface)->locations;
2338 DWORD src_location;
2340 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2342 FIXME("Unimplemented copy from %s for depth/stencil buffers.\n",
2343 wined3d_debug_location(locations));
2344 return FALSE;
2347 if (locations & WINED3D_LOCATION_RB_MULTISAMPLE)
2348 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
2349 else if (locations & WINED3D_LOCATION_RB_RESOLVED)
2350 src_location = WINED3D_LOCATION_RB_RESOLVED;
2351 else if (locations & WINED3D_LOCATION_TEXTURE_SRGB)
2352 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
2353 else /* surface_blt_fbo will load the source location if necessary. */
2354 src_location = WINED3D_LOCATION_TEXTURE_RGB;
2356 surface_blt_fbo(texture->resource.device, context, WINED3D_TEXF_POINT,
2357 surface, src_location, &rect, surface, dst_location, &rect);
2359 return TRUE;
2362 /* Context activation is done by the caller. Context may be NULL in ddraw-only mode. */
2363 BOOL surface_load_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
2365 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
2367 switch (location)
2369 case WINED3D_LOCATION_USER_MEMORY:
2370 case WINED3D_LOCATION_SYSMEM:
2371 case WINED3D_LOCATION_BUFFER:
2372 return surface_load_sysmem(surface, context, location);
2374 case WINED3D_LOCATION_DRAWABLE:
2375 return surface_load_drawable(surface, context);
2377 case WINED3D_LOCATION_RB_RESOLVED:
2378 case WINED3D_LOCATION_RB_MULTISAMPLE:
2379 return surface_load_renderbuffer(surface, context, location);
2381 case WINED3D_LOCATION_TEXTURE_RGB:
2382 case WINED3D_LOCATION_TEXTURE_SRGB:
2383 return surface_load_texture(surface, context,
2384 location == WINED3D_LOCATION_TEXTURE_SRGB);
2386 default:
2387 ERR("Don't know how to handle location %#x.\n", location);
2388 return FALSE;
2392 /* Context activation is done by the caller. */
2393 static void fbo_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2395 struct wined3d_blitter *next;
2397 if ((next = blitter->next))
2398 next->ops->blitter_destroy(next, context);
2400 heap_free(blitter);
2403 static void fbo_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
2404 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
2405 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
2407 struct wined3d_blitter *next;
2409 if ((next = blitter->next))
2410 next->ops->blitter_clear(next, device, rt_count, fb, rect_count,
2411 clear_rects, draw_rect, flags, colour, depth, stencil);
2414 static DWORD fbo_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
2415 struct wined3d_context *context, struct wined3d_surface *src_surface, DWORD src_location,
2416 const RECT *src_rect, struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect,
2417 const struct wined3d_color_key *colour_key, enum wined3d_texture_filter_type filter)
2419 struct wined3d_resource *src_resource = &src_surface->container->resource;
2420 struct wined3d_resource *dst_resource = &dst_surface->container->resource;
2421 struct wined3d_device *device = dst_resource->device;
2422 enum wined3d_blit_op blit_op = op;
2423 struct wined3d_blitter *next;
2425 if (blit_op == WINED3D_BLIT_OP_RAW_BLIT && dst_resource->format->id == src_resource->format->id)
2427 if (dst_resource->format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
2428 blit_op = WINED3D_BLIT_OP_DEPTH_BLIT;
2429 else
2430 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
2433 if (!fbo_blitter_supported(blit_op, context->gl_info,
2434 src_resource, src_location, dst_resource, dst_location))
2436 if ((next = blitter->next))
2437 return next->ops->blitter_blit(next, op, context, src_surface, src_location,
2438 src_rect, dst_surface, dst_location, dst_rect, colour_key, filter);
2441 if (blit_op == WINED3D_BLIT_OP_COLOR_BLIT)
2443 TRACE("Colour blit.\n");
2444 surface_blt_fbo(device, context, filter, src_surface, src_location,
2445 src_rect, dst_surface, dst_location, dst_rect);
2446 return dst_location;
2449 if (blit_op == WINED3D_BLIT_OP_DEPTH_BLIT)
2451 TRACE("Depth/stencil blit.\n");
2452 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
2453 return dst_location;
2456 ERR("This blitter does not implement blit op %#x.\n", blit_op);
2457 return dst_location;
2460 static const struct wined3d_blitter_ops fbo_blitter_ops =
2462 fbo_blitter_destroy,
2463 fbo_blitter_clear,
2464 fbo_blitter_blit,
2467 void wined3d_fbo_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
2469 struct wined3d_blitter *blitter;
2471 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
2472 return;
2474 if (!(blitter = heap_alloc(sizeof(*blitter))))
2475 return;
2477 TRACE("Created blitter %p.\n", blitter);
2479 blitter->ops = &fbo_blitter_ops;
2480 blitter->next = *next;
2481 *next = blitter;
2484 /* Context activation is done by the caller. */
2485 static void raw_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2487 struct wined3d_blitter *next;
2489 if ((next = blitter->next))
2490 next->ops->blitter_destroy(next, context);
2492 heap_free(blitter);
2495 /* Context activation is done by the caller. */
2496 static void raw_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
2497 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
2498 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
2500 struct wined3d_blitter *next;
2502 if (!(next = blitter->next))
2504 ERR("No blitter to handle clear.\n");
2505 return;
2508 TRACE("Forwarding to blitter %p.\n", next);
2509 next->ops->blitter_clear(next, device, rt_count, fb, rect_count,
2510 clear_rects, draw_rect, flags, colour, depth, stencil);
2513 /* Context activation is done by the caller. */
2514 static DWORD raw_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 const struct wined3d_gl_info *gl_info = context->gl_info;
2520 unsigned int src_sub_resource_idx, dst_sub_resource_idx;
2521 unsigned int src_level, src_layer, dst_level, dst_layer;
2522 struct wined3d_texture *src_texture, *dst_texture;
2523 struct wined3d_blitter *next;
2524 GLuint src_name, dst_name;
2525 DWORD location;
2527 src_texture = src_surface->container;
2528 dst_texture = dst_surface->container;
2530 /* If we would need to copy from a renderbuffer or drawable, we'd probably
2531 * be better of using the FBO blitter directly, since we'd need to use it
2532 * to copy the resource contents to the texture anyway. */
2533 if (op != WINED3D_BLIT_OP_RAW_BLIT
2534 || (src_texture->resource.format->id == dst_texture->resource.format->id
2535 && (!(src_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
2536 || !(dst_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB)))))
2538 if (!(next = blitter->next))
2540 ERR("No blitter to handle blit op %#x.\n", op);
2541 return dst_location;
2544 TRACE("Forwarding to blitter %p.\n", next);
2545 return next->ops->blitter_blit(next, op, context, src_surface, src_location,
2546 src_rect, dst_surface, dst_location, dst_rect, colour_key, filter);
2549 TRACE("Blit using ARB_copy_image.\n");
2551 src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
2552 src_level = src_sub_resource_idx % src_texture->level_count;
2553 src_layer = src_sub_resource_idx / src_texture->level_count;
2555 dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
2556 dst_level = dst_sub_resource_idx % dst_texture->level_count;
2557 dst_layer = dst_sub_resource_idx / dst_texture->level_count;
2559 location = src_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB);
2560 if (!location)
2561 location = src_texture->flags & WINED3D_TEXTURE_IS_SRGB
2562 ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2563 if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, location))
2564 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(location));
2565 src_name = wined3d_texture_get_texture_name(src_texture, context, location == WINED3D_LOCATION_TEXTURE_SRGB);
2567 location = dst_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB);
2568 if (!location)
2569 location = dst_texture->flags & WINED3D_TEXTURE_IS_SRGB
2570 ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2571 if (texture2d_is_full_rect(dst_texture, dst_level, dst_rect))
2573 if (!wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, location))
2574 ERR("Failed to prepare the destination sub-resource into %s.\n", wined3d_debug_location(location));
2576 else
2578 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, location))
2579 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(location));
2581 dst_name = wined3d_texture_get_texture_name(dst_texture, context, location == WINED3D_LOCATION_TEXTURE_SRGB);
2583 GL_EXTCALL(glCopyImageSubData(src_name, src_texture->target, src_level,
2584 src_rect->left, src_rect->top, src_layer, dst_name, dst_texture->target, dst_level,
2585 dst_rect->left, dst_rect->top, dst_layer, src_rect->right - src_rect->left,
2586 src_rect->bottom - src_rect->top, 1));
2587 checkGLcall("copy image data");
2589 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, location);
2590 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~location);
2591 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location))
2592 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(dst_location));
2594 return dst_location | location;
2597 static const struct wined3d_blitter_ops raw_blitter_ops =
2599 raw_blitter_destroy,
2600 raw_blitter_clear,
2601 raw_blitter_blit,
2604 void wined3d_raw_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
2606 struct wined3d_blitter *blitter;
2608 if (!gl_info->supported[ARB_COPY_IMAGE])
2609 return;
2611 if (!(blitter = heap_alloc(sizeof(*blitter))))
2612 return;
2614 TRACE("Created blitter %p.\n", blitter);
2616 blitter->ops = &raw_blitter_ops;
2617 blitter->next = *next;
2618 *next = blitter;
2621 /* Context activation is done by the caller. */
2622 static void ffp_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2624 struct wined3d_blitter *next;
2626 if ((next = blitter->next))
2627 next->ops->blitter_destroy(next, context);
2629 heap_free(blitter);
2632 static BOOL ffp_blit_supported(enum wined3d_blit_op blit_op, const struct wined3d_context *context,
2633 const struct wined3d_resource *src_resource, DWORD src_location,
2634 const struct wined3d_resource *dst_resource, DWORD dst_location)
2636 const struct wined3d_format *src_format = src_resource->format;
2637 const struct wined3d_format *dst_format = dst_resource->format;
2638 BOOL decompress;
2640 decompress = src_format && (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
2641 && !(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED);
2642 if (!decompress && !(src_resource->access & dst_resource->access & WINED3D_RESOURCE_ACCESS_GPU))
2644 TRACE("Source or destination resource is not GPU accessible.\n");
2645 return FALSE;
2648 if (blit_op == WINED3D_BLIT_OP_RAW_BLIT && dst_format->id == src_format->id)
2650 if (dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
2651 blit_op = WINED3D_BLIT_OP_DEPTH_BLIT;
2652 else
2653 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
2656 switch (blit_op)
2658 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
2659 if (context->d3d_info->shader_color_key)
2661 TRACE("Color keying requires converted textures.\n");
2662 return FALSE;
2664 case WINED3D_BLIT_OP_COLOR_BLIT:
2665 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
2666 if (!context->gl_info->supported[WINED3D_GL_LEGACY_CONTEXT])
2667 return FALSE;
2669 if (TRACE_ON(d3d))
2671 TRACE("Checking support for fixup:\n");
2672 dump_color_fixup_desc(src_format->color_fixup);
2675 /* We only support identity conversions. */
2676 if (!is_identity_fixup(src_format->color_fixup)
2677 || !is_identity_fixup(dst_format->color_fixup))
2679 if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER
2680 && dst_format->id == src_format->id && dst_location == WINED3D_LOCATION_DRAWABLE)
2682 WARN("Claiming fixup support because of ORM_BACKBUFFER.\n");
2684 else
2686 TRACE("Fixups are not supported.\n");
2687 return FALSE;
2691 if (!(dst_resource->usage & WINED3DUSAGE_RENDERTARGET))
2693 TRACE("Can only blit to render targets.\n");
2694 return FALSE;
2696 return TRUE;
2698 default:
2699 TRACE("Unsupported blit operation %#x.\n", blit_op);
2700 return FALSE;
2704 static BOOL ffp_blitter_use_cpu_clear(struct wined3d_rendertarget_view *view)
2706 struct wined3d_resource *resource;
2707 struct wined3d_texture *texture;
2708 DWORD locations;
2710 resource = view->resource;
2711 if (resource->type == WINED3D_RTYPE_BUFFER)
2712 return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU);
2714 texture = texture_from_resource(resource);
2715 locations = texture->sub_resources[view->sub_resource_idx].locations;
2716 if (locations & (resource->map_binding | WINED3D_LOCATION_DISCARDED))
2717 return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU)
2718 || (texture->flags & WINED3D_TEXTURE_PIN_SYSMEM);
2720 return !(resource->access & WINED3D_RESOURCE_ACCESS_GPU)
2721 && !(texture->flags & WINED3D_TEXTURE_CONVERTED);
2724 static void ffp_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
2725 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
2726 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
2728 struct wined3d_rendertarget_view *view;
2729 struct wined3d_blitter *next;
2730 DWORD next_flags = 0;
2731 unsigned int i;
2733 if (flags & WINED3DCLEAR_TARGET)
2735 for (i = 0; i < rt_count; ++i)
2737 if (!(view = fb->render_targets[i]))
2738 continue;
2740 if (ffp_blitter_use_cpu_clear(view)
2741 || (!(view->resource->usage & WINED3DUSAGE_RENDERTARGET)
2742 && (wined3d_settings.offscreen_rendering_mode != ORM_FBO
2743 || !(view->format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE))))
2745 next_flags |= WINED3DCLEAR_TARGET;
2746 flags &= ~WINED3DCLEAR_TARGET;
2747 break;
2750 /* FIXME: We should reject colour fills on formats with fixups,
2751 * but this would break P8 colour fills for example. */
2755 if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil)
2756 && (!view->format->depth_size || (flags & WINED3DCLEAR_ZBUFFER))
2757 && (!view->format->stencil_size || (flags & WINED3DCLEAR_STENCIL))
2758 && ffp_blitter_use_cpu_clear(view))
2760 next_flags |= flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL);
2761 flags &= ~(WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL);
2764 if (flags)
2765 device_clear_render_targets(device, rt_count, fb, rect_count,
2766 clear_rects, draw_rect, flags, colour, depth, stencil);
2768 if (next_flags && (next = blitter->next))
2769 next->ops->blitter_clear(next, device, rt_count, fb, rect_count,
2770 clear_rects, draw_rect, next_flags, colour, depth, stencil);
2773 static DWORD ffp_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
2774 struct wined3d_context *context, struct wined3d_surface *src_surface, DWORD src_location,
2775 const RECT *src_rect, struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect,
2776 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter)
2778 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
2779 struct wined3d_texture *src_texture = src_surface->container;
2780 struct wined3d_texture *dst_texture = dst_surface->container;
2781 const struct wined3d_gl_info *gl_info = context->gl_info;
2782 struct wined3d_resource *src_resource, *dst_resource;
2783 struct wined3d_color_key old_blt_key;
2784 struct wined3d_device *device;
2785 struct wined3d_blitter *next;
2786 DWORD old_color_key_flags;
2787 RECT r;
2789 src_resource = &src_texture->resource;
2790 dst_resource = &dst_texture->resource;
2791 device = dst_resource->device;
2793 if (!ffp_blit_supported(op, context, src_resource, src_location, dst_resource, dst_location))
2795 if ((next = blitter->next))
2796 return next->ops->blitter_blit(next, op, context, src_surface, src_location,
2797 src_rect, dst_surface, dst_location, dst_rect, color_key, filter);
2800 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
2802 old_blt_key = src_texture->async.src_blt_color_key;
2803 old_color_key_flags = src_texture->async.color_key_flags;
2804 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT, color_key);
2806 /* Make sure the surface is up-to-date. This should probably use
2807 * surface_load_location() and worry about the destination surface too,
2808 * unless we're overwriting it completely. */
2809 wined3d_texture_load(src_texture, context, FALSE);
2811 /* Activate the destination context, set it up for blitting. */
2812 context_apply_blit_state(context, device);
2814 if (dst_location == WINED3D_LOCATION_DRAWABLE)
2816 r = *dst_rect;
2817 wined3d_texture_translate_drawable_coords(dst_texture, context->win_handle, &r);
2818 dst_rect = &r;
2821 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
2823 GLenum buffer;
2825 if (dst_location == WINED3D_LOCATION_DRAWABLE)
2827 TRACE("Destination surface %p is onscreen.\n", dst_surface);
2828 buffer = wined3d_texture_get_gl_buffer(dst_texture);
2830 else
2832 TRACE("Destination surface %p is offscreen.\n", dst_surface);
2833 buffer = GL_COLOR_ATTACHMENT0;
2835 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
2836 context_set_draw_buffer(context, buffer);
2837 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
2838 context_invalidate_state(context, STATE_FRAMEBUFFER);
2841 gl_info->gl_ops.gl.p_glEnable(src_texture->target);
2842 checkGLcall("glEnable(target)");
2844 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST || color_key)
2846 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
2847 checkGLcall("glEnable(GL_ALPHA_TEST)");
2850 if (color_key)
2852 /* For P8 surfaces, the alpha component contains the palette index.
2853 * Which means that the colorkey is one of the palette entries. In
2854 * other cases pixels that should be masked away have alpha set to 0. */
2855 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT)
2856 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
2857 (float)src_texture->async.src_blt_color_key.color_space_low_value / 255.0f);
2858 else
2859 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
2860 checkGLcall("glAlphaFunc");
2863 draw_textured_quad(src_texture, src_sub_resource_idx, context, src_rect, dst_rect, filter);
2865 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST || color_key)
2867 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2868 checkGLcall("glDisable(GL_ALPHA_TEST)");
2871 /* Leave the OpenGL state valid for blitting. */
2872 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
2873 checkGLcall("glDisable(GL_TEXTURE_2D)");
2874 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
2876 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
2877 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
2879 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
2881 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
2882 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
2885 if (wined3d_settings.strict_draw_ordering
2886 || (dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture))
2887 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2889 /* Restore the color key parameters */
2890 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT,
2891 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
2893 return dst_location;
2896 static const struct wined3d_blitter_ops ffp_blitter_ops =
2898 ffp_blitter_destroy,
2899 ffp_blitter_clear,
2900 ffp_blitter_blit,
2903 void wined3d_ffp_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
2905 struct wined3d_blitter *blitter;
2907 if (!(blitter = heap_alloc(sizeof(*blitter))))
2908 return;
2910 TRACE("Created blitter %p.\n", blitter);
2912 blitter->ops = &ffp_blitter_ops;
2913 blitter->next = *next;
2914 *next = blitter;
2917 /* Context activation is done by the caller. */
2918 static void cpu_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2920 struct wined3d_blitter *next;
2922 if ((next = blitter->next))
2923 next->ops->blitter_destroy(next, context);
2925 heap_free(blitter);
2928 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
2929 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
2930 const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx)
2932 UINT row_block_count;
2933 const BYTE *src_row;
2934 BYTE *dst_row;
2935 UINT x, y;
2937 src_row = src_data;
2938 dst_row = dst_data;
2940 row_block_count = (update_w + format->block_width - 1) / format->block_width;
2942 if (!flags)
2944 for (y = 0; y < update_h; y += format->block_height)
2946 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
2947 src_row += src_pitch;
2948 dst_row += dst_pitch;
2951 return WINED3D_OK;
2954 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
2956 src_row += (((update_h / format->block_height) - 1) * src_pitch);
2958 switch (format->id)
2960 case WINED3DFMT_DXT1:
2961 for (y = 0; y < update_h; y += format->block_height)
2963 struct block
2965 WORD color[2];
2966 BYTE control_row[4];
2969 const struct block *s = (const struct block *)src_row;
2970 struct block *d = (struct block *)dst_row;
2972 for (x = 0; x < row_block_count; ++x)
2974 d[x].color[0] = s[x].color[0];
2975 d[x].color[1] = s[x].color[1];
2976 d[x].control_row[0] = s[x].control_row[3];
2977 d[x].control_row[1] = s[x].control_row[2];
2978 d[x].control_row[2] = s[x].control_row[1];
2979 d[x].control_row[3] = s[x].control_row[0];
2981 src_row -= src_pitch;
2982 dst_row += dst_pitch;
2984 return WINED3D_OK;
2986 case WINED3DFMT_DXT2:
2987 case WINED3DFMT_DXT3:
2988 for (y = 0; y < update_h; y += format->block_height)
2990 struct block
2992 WORD alpha_row[4];
2993 WORD color[2];
2994 BYTE control_row[4];
2997 const struct block *s = (const struct block *)src_row;
2998 struct block *d = (struct block *)dst_row;
3000 for (x = 0; x < row_block_count; ++x)
3002 d[x].alpha_row[0] = s[x].alpha_row[3];
3003 d[x].alpha_row[1] = s[x].alpha_row[2];
3004 d[x].alpha_row[2] = s[x].alpha_row[1];
3005 d[x].alpha_row[3] = s[x].alpha_row[0];
3006 d[x].color[0] = s[x].color[0];
3007 d[x].color[1] = s[x].color[1];
3008 d[x].control_row[0] = s[x].control_row[3];
3009 d[x].control_row[1] = s[x].control_row[2];
3010 d[x].control_row[2] = s[x].control_row[1];
3011 d[x].control_row[3] = s[x].control_row[0];
3013 src_row -= src_pitch;
3014 dst_row += dst_pitch;
3016 return WINED3D_OK;
3018 default:
3019 FIXME("Compressed flip not implemented for format %s.\n",
3020 debug_d3dformat(format->id));
3021 return E_NOTIMPL;
3025 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
3026 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
3028 return E_NOTIMPL;
3031 static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
3032 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
3033 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
3034 enum wined3d_texture_filter_type filter)
3036 unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count;
3037 struct wined3d_device *device = dst_texture->resource.device;
3038 const struct wined3d_format *src_format, *dst_format;
3039 struct wined3d_texture *converted_texture = NULL;
3040 struct wined3d_bo_address src_data, dst_data;
3041 unsigned int src_fmt_flags, dst_fmt_flags;
3042 struct wined3d_map_desc dst_map, src_map;
3043 struct wined3d_context *context = NULL;
3044 unsigned int x, sx, xinc, y, sy, yinc;
3045 unsigned int texture_level;
3046 HRESULT hr = WINED3D_OK;
3047 BOOL same_sub_resource;
3048 DWORD map_binding;
3049 const BYTE *sbase;
3050 const BYTE *sbuf;
3051 BYTE *dbuf;
3053 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
3054 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
3055 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture,
3056 src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
3058 if (device->d3d_initialized)
3059 context = context_acquire(device, NULL, 0);
3061 if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx)
3063 same_sub_resource = TRUE;
3065 map_binding = dst_texture->resource.map_binding;
3066 texture_level = dst_sub_resource_idx % dst_texture->level_count;
3067 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
3068 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
3069 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
3070 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
3071 wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
3072 dst_map.data = context_map_bo_address(context, &dst_data,
3073 dst_texture->sub_resources[dst_sub_resource_idx].size,
3074 GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ | WINED3D_MAP_WRITE);
3076 src_map = dst_map;
3077 src_format = dst_texture->resource.format;
3078 dst_format = src_format;
3079 dst_fmt_flags = dst_texture->resource.format_flags;
3080 src_fmt_flags = dst_fmt_flags;
3082 else
3084 same_sub_resource = FALSE;
3085 dst_format = dst_texture->resource.format;
3086 dst_fmt_flags = dst_texture->resource.format_flags;
3087 if (!(flags & WINED3D_BLT_RAW) && dst_texture->resource.format->id != src_texture->resource.format->id)
3089 if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format)))
3091 FIXME("Cannot convert %s to %s.\n", debug_d3dformat(src_texture->resource.format->id),
3092 debug_d3dformat(dst_texture->resource.format->id));
3093 if (context)
3094 context_release(context);
3095 return WINED3DERR_NOTAVAILABLE;
3097 src_texture = converted_texture;
3098 src_sub_resource_idx = 0;
3100 src_format = src_texture->resource.format;
3101 src_fmt_flags = src_texture->resource.format_flags;
3103 map_binding = src_texture->resource.map_binding;
3104 texture_level = src_sub_resource_idx % src_texture->level_count;
3105 if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, map_binding))
3106 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
3107 wined3d_texture_get_pitch(src_texture, texture_level, &src_map.row_pitch, &src_map.slice_pitch);
3108 wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &src_data, map_binding);
3109 src_map.data = context_map_bo_address(context, &src_data,
3110 src_texture->sub_resources[src_sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READ);
3112 map_binding = dst_texture->resource.map_binding;
3113 texture_level = dst_sub_resource_idx % dst_texture->level_count;
3114 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
3115 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
3116 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
3117 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
3118 wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
3119 dst_map.data = context_map_bo_address(context, &dst_data,
3120 dst_texture->sub_resources[dst_sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_WRITE);
3122 flags &= ~WINED3D_BLT_RAW;
3124 bpp = dst_format->byte_count;
3125 src_height = src_box->bottom - src_box->top;
3126 src_width = src_box->right - src_box->left;
3127 dst_height = dst_box->bottom - dst_box->top;
3128 dst_width = dst_box->right - dst_box->left;
3129 row_byte_count = dst_width * bpp;
3131 sbase = (BYTE *)src_map.data
3132 + ((src_box->top / src_format->block_height) * src_map.row_pitch)
3133 + ((src_box->left / src_format->block_width) * src_format->block_byte_count);
3134 dbuf = (BYTE *)dst_map.data
3135 + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch)
3136 + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count);
3138 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
3140 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
3142 if (same_sub_resource)
3144 FIXME("Only plain blits supported on compressed surfaces.\n");
3145 hr = E_NOTIMPL;
3146 goto release;
3149 if (src_height != dst_height || src_width != dst_width)
3151 WARN("Stretching not supported on compressed surfaces.\n");
3152 hr = WINED3DERR_INVALIDCALL;
3153 goto release;
3156 hr = surface_cpu_blt_compressed(sbase, dbuf,
3157 src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height,
3158 src_format, flags, fx);
3159 goto release;
3162 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
3163 && (src_width != dst_width || src_height != dst_height))
3165 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
3166 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
3169 xinc = (src_width << 16) / dst_width;
3170 yinc = (src_height << 16) / dst_height;
3172 if (!flags)
3174 /* No effects, we can cheat here. */
3175 if (dst_width == src_width)
3177 if (dst_height == src_height)
3179 /* No stretching in either direction. This needs to be as fast
3180 * as possible. */
3181 sbuf = sbase;
3183 /* Check for overlapping surfaces. */
3184 if (!same_sub_resource || dst_box->top < src_box->top
3185 || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
3187 /* No overlap, or dst above src, so copy from top downwards. */
3188 for (y = 0; y < dst_height; ++y)
3190 memcpy(dbuf, sbuf, row_byte_count);
3191 sbuf += src_map.row_pitch;
3192 dbuf += dst_map.row_pitch;
3195 else if (dst_box->top > src_box->top)
3197 /* Copy from bottom upwards. */
3198 sbuf += src_map.row_pitch * dst_height;
3199 dbuf += dst_map.row_pitch * dst_height;
3200 for (y = 0; y < dst_height; ++y)
3202 sbuf -= src_map.row_pitch;
3203 dbuf -= dst_map.row_pitch;
3204 memcpy(dbuf, sbuf, row_byte_count);
3207 else
3209 /* Src and dst overlapping on the same line, use memmove. */
3210 for (y = 0; y < dst_height; ++y)
3212 memmove(dbuf, sbuf, row_byte_count);
3213 sbuf += src_map.row_pitch;
3214 dbuf += dst_map.row_pitch;
3218 else
3220 /* Stretching in y direction only. */
3221 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3223 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3224 memcpy(dbuf, sbuf, row_byte_count);
3225 dbuf += dst_map.row_pitch;
3229 else
3231 /* Stretching in X direction. */
3232 unsigned int last_sy = ~0u;
3233 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3235 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3237 if ((sy >> 16) == (last_sy >> 16))
3239 /* This source row is the same as last source row -
3240 * Copy the already stretched row. */
3241 memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count);
3243 else
3245 #define STRETCH_ROW(type) \
3246 do { \
3247 const type *s = (const type *)sbuf; \
3248 type *d = (type *)dbuf; \
3249 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3250 d[x] = s[sx >> 16]; \
3251 } while(0)
3253 switch(bpp)
3255 case 1:
3256 STRETCH_ROW(BYTE);
3257 break;
3258 case 2:
3259 STRETCH_ROW(WORD);
3260 break;
3261 case 4:
3262 STRETCH_ROW(DWORD);
3263 break;
3264 case 3:
3266 const BYTE *s;
3267 BYTE *d = dbuf;
3268 for (x = sx = 0; x < dst_width; x++, sx+= xinc)
3270 DWORD pixel;
3272 s = sbuf + 3 * (sx >> 16);
3273 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3274 d[0] = (pixel ) & 0xff;
3275 d[1] = (pixel >> 8) & 0xff;
3276 d[2] = (pixel >> 16) & 0xff;
3277 d += 3;
3279 break;
3281 default:
3282 FIXME("Stretched blit not implemented for bpp %u.\n", bpp * 8);
3283 hr = WINED3DERR_NOTAVAILABLE;
3284 goto error;
3286 #undef STRETCH_ROW
3288 dbuf += dst_map.row_pitch;
3289 last_sy = sy;
3293 else
3295 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
3296 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
3297 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
3298 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3299 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
3301 /* The color keying flags are checked for correctness in ddraw. */
3302 if (flags & WINED3D_BLT_SRC_CKEY)
3304 keylow = src_texture->async.src_blt_color_key.color_space_low_value;
3305 keyhigh = src_texture->async.src_blt_color_key.color_space_high_value;
3307 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3309 keylow = fx->src_color_key.color_space_low_value;
3310 keyhigh = fx->src_color_key.color_space_high_value;
3313 if (flags & WINED3D_BLT_DST_CKEY)
3315 /* Destination color keys are taken from the source surface! */
3316 destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value;
3317 destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value;
3319 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
3321 destkeylow = fx->dst_color_key.color_space_low_value;
3322 destkeyhigh = fx->dst_color_key.color_space_high_value;
3325 if (bpp == 1)
3327 keymask = 0xff;
3329 else
3331 DWORD masks[3];
3332 get_color_masks(src_format, masks);
3333 keymask = masks[0] | masks[1] | masks[2];
3335 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3336 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
3339 if (flags & WINED3D_BLT_FX)
3341 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
3342 LONG tmpxy;
3343 dTopLeft = dbuf;
3344 dTopRight = dbuf + ((dst_width - 1) * bpp);
3345 dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
3346 dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
3348 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
3350 /* I don't think we need to do anything about this flag. */
3351 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
3353 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
3355 tmp = dTopRight;
3356 dTopRight = dTopLeft;
3357 dTopLeft = tmp;
3358 tmp = dBottomRight;
3359 dBottomRight = dBottomLeft;
3360 dBottomLeft = tmp;
3361 dstxinc = dstxinc * -1;
3363 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
3365 tmp = dTopLeft;
3366 dTopLeft = dBottomLeft;
3367 dBottomLeft = tmp;
3368 tmp = dTopRight;
3369 dTopRight = dBottomRight;
3370 dBottomRight = tmp;
3371 dstyinc = dstyinc * -1;
3373 if (fx->fx & WINEDDBLTFX_NOTEARING)
3375 /* I don't think we need to do anything about this flag. */
3376 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
3378 if (fx->fx & WINEDDBLTFX_ROTATE180)
3380 tmp = dBottomRight;
3381 dBottomRight = dTopLeft;
3382 dTopLeft = tmp;
3383 tmp = dBottomLeft;
3384 dBottomLeft = dTopRight;
3385 dTopRight = tmp;
3386 dstxinc = dstxinc * -1;
3387 dstyinc = dstyinc * -1;
3389 if (fx->fx & WINEDDBLTFX_ROTATE270)
3391 tmp = dTopLeft;
3392 dTopLeft = dBottomLeft;
3393 dBottomLeft = dBottomRight;
3394 dBottomRight = dTopRight;
3395 dTopRight = tmp;
3396 tmpxy = dstxinc;
3397 dstxinc = dstyinc;
3398 dstyinc = tmpxy;
3399 dstxinc = dstxinc * -1;
3401 if (fx->fx & WINEDDBLTFX_ROTATE90)
3403 tmp = dTopLeft;
3404 dTopLeft = dTopRight;
3405 dTopRight = dBottomRight;
3406 dBottomRight = dBottomLeft;
3407 dBottomLeft = tmp;
3408 tmpxy = dstxinc;
3409 dstxinc = dstyinc;
3410 dstyinc = tmpxy;
3411 dstyinc = dstyinc * -1;
3413 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
3415 /* I don't think we need to do anything about this flag. */
3416 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
3418 dbuf = dTopLeft;
3419 flags &= ~(WINED3D_BLT_FX);
3422 #define COPY_COLORKEY_FX(type) \
3423 do { \
3424 const type *s; \
3425 type *d = (type *)dbuf, *dx, tmp; \
3426 for (y = sy = 0; y < dst_height; ++y, sy += yinc) \
3428 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
3429 dx = d; \
3430 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3432 tmp = s[sx >> 16]; \
3433 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
3434 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
3436 dx[0] = tmp; \
3438 dx = (type *)(((BYTE *)dx) + dstxinc); \
3440 d = (type *)(((BYTE *)d) + dstyinc); \
3442 } while(0)
3444 switch (bpp)
3446 case 1:
3447 COPY_COLORKEY_FX(BYTE);
3448 break;
3449 case 2:
3450 COPY_COLORKEY_FX(WORD);
3451 break;
3452 case 4:
3453 COPY_COLORKEY_FX(DWORD);
3454 break;
3455 case 3:
3457 const BYTE *s;
3458 BYTE *d = dbuf, *dx;
3459 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3461 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3462 dx = d;
3463 for (x = sx = 0; x < dst_width; ++x, sx+= xinc)
3465 DWORD pixel, dpixel = 0;
3466 s = sbuf + 3 * (sx>>16);
3467 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3468 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
3469 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
3470 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
3472 dx[0] = (pixel ) & 0xff;
3473 dx[1] = (pixel >> 8) & 0xff;
3474 dx[2] = (pixel >> 16) & 0xff;
3476 dx += dstxinc;
3478 d += dstyinc;
3480 break;
3482 default:
3483 FIXME("%s color-keyed blit not implemented for bpp %u.\n",
3484 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
3485 hr = WINED3DERR_NOTAVAILABLE;
3486 goto error;
3487 #undef COPY_COLORKEY_FX
3491 error:
3492 if (flags)
3493 FIXME(" Unsupported flags %#x.\n", flags);
3495 release:
3496 context_unmap_bo_address(context, &dst_data, GL_PIXEL_UNPACK_BUFFER);
3497 if (!same_sub_resource)
3498 context_unmap_bo_address(context, &src_data, GL_PIXEL_UNPACK_BUFFER);
3499 if (SUCCEEDED(hr) && dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture)
3501 SetRect(&dst_texture->swapchain->front_buffer_update,
3502 dst_box->left, dst_box->top, dst_box->right, dst_box->bottom);
3503 dst_texture->swapchain->swapchain_ops->swapchain_frontbuffer_updated(dst_texture->swapchain);
3505 if (converted_texture)
3506 wined3d_texture_decref(converted_texture);
3507 if (context)
3508 context_release(context);
3510 return hr;
3513 static void surface_cpu_blt_colour_fill(struct wined3d_rendertarget_view *view,
3514 const struct wined3d_box *box, const struct wined3d_color *colour)
3516 struct wined3d_device *device = view->resource->device;
3517 struct wined3d_context *context = NULL;
3518 struct wined3d_texture *texture;
3519 struct wined3d_bo_address data;
3520 unsigned int x, y, w, h, bpp;
3521 struct wined3d_map_desc map;
3522 DWORD map_binding;
3523 BYTE *row;
3524 DWORD c;
3526 TRACE("view %p, box %s, colour %s.\n", view, debug_box(box), debug_color(colour));
3528 if (view->format_flags & WINED3DFMT_FLAG_BLOCKS)
3530 FIXME("Not implemented for format %s.\n", debug_d3dformat(view->format->id));
3531 return;
3534 if (view->format->id != view->resource->format->id)
3535 FIXME("View format %s doesn't match resource format %s.\n",
3536 debug_d3dformat(view->format->id), debug_d3dformat(view->resource->format->id));
3538 if (view->resource->type == WINED3D_RTYPE_BUFFER)
3540 FIXME("Not implemented for buffers.\n");
3541 return;
3544 if (device->d3d_initialized)
3545 context = context_acquire(device, NULL, 0);
3547 c = wined3d_format_convert_from_float(view->format, colour);
3548 bpp = view->format->byte_count;
3549 w = box->right - box->left;
3550 h = box->bottom - box->top;
3552 texture = texture_from_resource(view->resource);
3553 map_binding = texture->resource.map_binding;
3554 if (!wined3d_texture_load_location(texture, view->sub_resource_idx, context, map_binding))
3555 ERR("Failed to load the sub-resource into %s.\n", wined3d_debug_location(map_binding));
3556 wined3d_texture_invalidate_location(texture, view->sub_resource_idx, ~map_binding);
3557 wined3d_texture_get_pitch(texture, view->sub_resource_idx % texture->level_count,
3558 &map.row_pitch, &map.slice_pitch);
3559 wined3d_texture_get_memory(texture, view->sub_resource_idx, &data, map_binding);
3560 map.data = context_map_bo_address(context, &data,
3561 texture->sub_resources[view->sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_WRITE);
3562 map.data = (BYTE *)map.data
3563 + (box->front * map.slice_pitch)
3564 + ((box->top / view->format->block_height) * map.row_pitch)
3565 + ((box->left / view->format->block_width) * view->format->block_byte_count);
3567 switch (bpp)
3569 case 1:
3570 for (x = 0; x < w; ++x)
3572 ((BYTE *)map.data)[x] = c;
3574 break;
3576 case 2:
3577 for (x = 0; x < w; ++x)
3579 ((WORD *)map.data)[x] = c;
3581 break;
3583 case 3:
3585 row = map.data;
3586 for (x = 0; x < w; ++x, row += 3)
3588 row[0] = (c ) & 0xff;
3589 row[1] = (c >> 8) & 0xff;
3590 row[2] = (c >> 16) & 0xff;
3592 break;
3594 case 4:
3595 for (x = 0; x < w; ++x)
3597 ((DWORD *)map.data)[x] = c;
3599 break;
3601 default:
3602 FIXME("Not implemented for bpp %u.\n", bpp);
3603 wined3d_resource_unmap(view->resource, view->sub_resource_idx);
3604 return;
3607 row = map.data;
3608 for (y = 1; y < h; ++y)
3610 row += map.row_pitch;
3611 memcpy(row, map.data, w * bpp);
3614 context_unmap_bo_address(context, &data, GL_PIXEL_UNPACK_BUFFER);
3615 if (context)
3616 context_release(context);
3619 static void cpu_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
3620 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
3621 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
3623 struct wined3d_color c = {depth, 0.0f, 0.0f, 0.0f};
3624 struct wined3d_rendertarget_view *view;
3625 struct wined3d_box box;
3626 unsigned int i, j;
3628 if (!rect_count)
3630 rect_count = 1;
3631 clear_rects = draw_rect;
3634 for (i = 0; i < rect_count; ++i)
3636 box.left = max(clear_rects[i].left, draw_rect->left);
3637 box.top = max(clear_rects[i].top, draw_rect->top);
3638 box.right = min(clear_rects[i].right, draw_rect->right);
3639 box.bottom = min(clear_rects[i].bottom, draw_rect->bottom);
3640 box.front = 0;
3641 box.back = 1;
3643 if (box.left >= box.right || box.top >= box.bottom)
3644 continue;
3646 if (flags & WINED3DCLEAR_TARGET)
3648 for (j = 0; j < rt_count; ++j)
3650 if ((view = fb->render_targets[j]))
3651 surface_cpu_blt_colour_fill(view, &box, colour);
3655 if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil))
3657 if ((view->format->depth_size && !(flags & WINED3DCLEAR_ZBUFFER))
3658 || (view->format->stencil_size && !(flags & WINED3DCLEAR_STENCIL)))
3659 FIXME("Clearing %#x on %s.\n", flags, debug_d3dformat(view->format->id));
3661 surface_cpu_blt_colour_fill(view, &box, &c);
3666 static DWORD cpu_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
3667 struct wined3d_context *context, struct wined3d_surface *src_surface, DWORD src_location,
3668 const RECT *src_rect, struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect,
3669 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter)
3671 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
3672 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
3673 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
3674 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
3675 struct wined3d_texture *dst_texture = dst_surface->container;
3676 struct wined3d_texture *src_texture = src_surface->container;
3677 struct wined3d_blt_fx fx;
3678 DWORD flags = 0;
3680 memset(&fx, 0, sizeof(fx));
3681 switch (op)
3683 case WINED3D_BLIT_OP_COLOR_BLIT:
3684 case WINED3D_BLIT_OP_DEPTH_BLIT:
3685 case WINED3D_BLIT_OP_RAW_BLIT:
3686 break;
3687 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
3688 flags |= WINED3D_BLT_ALPHA_TEST;
3689 break;
3690 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
3691 flags |= WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_FX;
3692 fx.src_color_key = *color_key;
3693 break;
3694 default:
3695 FIXME("Unhandled op %#x.\n", op);
3696 break;
3699 if (FAILED(surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
3700 src_texture, src_sub_resource_idx, &src_box, flags, &fx, filter)))
3701 ERR("Failed to blit.\n");
3702 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
3704 return dst_location | (dst_texture->sub_resources[dst_sub_resource_idx].locations
3705 & dst_texture->resource.map_binding);
3708 static const struct wined3d_blitter_ops cpu_blitter_ops =
3710 cpu_blitter_destroy,
3711 cpu_blitter_clear,
3712 cpu_blitter_blit,
3715 struct wined3d_blitter *wined3d_cpu_blitter_create(void)
3717 struct wined3d_blitter *blitter;
3719 if (!(blitter = heap_alloc(sizeof(*blitter))))
3720 return NULL;
3722 TRACE("Created blitter %p.\n", blitter);
3724 blitter->ops = &cpu_blitter_ops;
3725 blitter->next = NULL;
3727 return blitter;
3730 HRESULT wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3731 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
3732 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
3734 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
3735 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
3736 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
3737 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
3738 struct wined3d_texture_sub_resource *src_sub_resource, *dst_sub_resource;
3739 struct wined3d_texture *dst_texture = dst_surface->container;
3740 struct wined3d_texture *src_texture = src_surface->container;
3741 struct wined3d_device *device = dst_texture->resource.device;
3742 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3743 const struct wined3d_color_key *colour_key = NULL;
3744 DWORD dst_location, valid_locations;
3745 DWORD src_ds_flags, dst_ds_flags;
3746 struct wined3d_context *context;
3747 enum wined3d_blit_op blit_op;
3748 BOOL scale, convert, resolve;
3750 static const DWORD simple_blit = WINED3D_BLT_SRC_CKEY
3751 | WINED3D_BLT_SRC_CKEY_OVERRIDE
3752 | WINED3D_BLT_ALPHA_TEST
3753 | WINED3D_BLT_RAW;
3755 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
3756 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3757 flags, fx, debug_d3dtexturefiltertype(filter));
3758 TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage));
3760 if (fx)
3762 TRACE("fx %#x.\n", fx->fx);
3763 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
3764 fx->dst_color_key.color_space_low_value,
3765 fx->dst_color_key.color_space_high_value);
3766 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
3767 fx->src_color_key.color_space_low_value,
3768 fx->src_color_key.color_space_high_value);
3771 if (!fx || !(fx->fx))
3772 flags &= ~WINED3D_BLT_FX;
3774 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
3775 if (flags & WINED3D_BLT_DO_NOT_WAIT)
3777 static unsigned int once;
3779 if (!once++)
3780 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
3783 flags &= ~(WINED3D_BLT_SYNCHRONOUS | WINED3D_BLT_DO_NOT_WAIT | WINED3D_BLT_WAIT);
3785 if (!device->d3d_initialized)
3787 WARN("D3D not initialized, using fallback.\n");
3788 goto cpu;
3791 if (flags & ~simple_blit)
3793 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
3794 goto fallback;
3797 src_swapchain = src_texture->swapchain;
3798 dst_swapchain = dst_texture->swapchain;
3800 /* This isn't strictly needed. FBO blits for example could deal with
3801 * cross-swapchain blits by first downloading the source to a texture
3802 * before switching to the destination context. We just have this here to
3803 * not have to deal with the issue, since cross-swapchain blits should be
3804 * rare. */
3805 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
3807 FIXME("Using fallback for cross-swapchain blit.\n");
3808 goto fallback;
3811 scale = src_rect->right - src_rect->left != dst_rect->right - dst_rect->left
3812 || src_rect->bottom - src_rect->top != dst_rect->bottom - dst_rect->top;
3813 convert = src_texture->resource.format->id != dst_texture->resource.format->id;
3814 resolve = src_texture->resource.multisample_type != dst_texture->resource.multisample_type;
3816 dst_ds_flags = dst_texture->resource.format_flags
3817 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
3818 src_ds_flags = src_texture->resource.format_flags
3819 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
3821 if (src_ds_flags || dst_ds_flags)
3823 TRACE("Depth/stencil blit.\n");
3825 if (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)
3826 dst_location = dst_texture->resource.draw_binding;
3827 else
3828 dst_location = dst_texture->resource.map_binding;
3830 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3831 valid_locations = device->blitter->ops->blitter_blit(device->blitter,
3832 WINED3D_BLIT_OP_DEPTH_BLIT, context,
3833 src_surface, src_texture->resource.draw_binding, src_rect,
3834 dst_surface, dst_location, dst_rect, NULL, filter);
3835 context_release(context);
3837 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
3838 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
3840 return WINED3D_OK;
3843 TRACE("Colour blit.\n");
3845 dst_sub_resource = &dst_texture->sub_resources[dst_sub_resource_idx];
3846 src_sub_resource = &src_texture->sub_resources[src_sub_resource_idx];
3848 /* In principle this would apply to depth blits as well, but we don't
3849 * implement those in the CPU blitter at the moment. */
3850 if ((dst_sub_resource->locations & dst_texture->resource.map_binding)
3851 && (src_sub_resource->locations & src_texture->resource.map_binding))
3853 if (scale)
3854 TRACE("Not doing sysmem blit because of scaling.\n");
3855 else if (convert)
3856 TRACE("Not doing sysmem blit because of format conversion.\n");
3857 else
3858 goto cpu;
3861 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
3862 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3864 colour_key = &fx->src_color_key;
3865 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
3867 else if (flags & WINED3D_BLT_SRC_CKEY)
3869 colour_key = &src_texture->async.src_blt_color_key;
3870 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
3872 else if (flags & WINED3D_BLT_ALPHA_TEST)
3874 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
3876 else if ((src_sub_resource->locations & surface_simple_locations)
3877 && !(dst_sub_resource->locations & surface_simple_locations))
3879 /* Upload */
3880 if (scale)
3881 TRACE("Not doing upload because of scaling.\n");
3882 else if (convert)
3883 TRACE("Not doing upload because of format conversion.\n");
3884 else if (dst_texture->resource.format->conv_byte_count)
3885 TRACE("Not doing upload because the destination format needs conversion.\n");
3886 else
3888 POINT dst_point = {dst_rect->left, dst_rect->top};
3890 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, src_rect)))
3892 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
3894 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3895 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx,
3896 context, dst_texture->resource.draw_binding);
3897 context_release(context);
3899 return WINED3D_OK;
3903 else if (dst_swapchain && dst_swapchain->back_buffers
3904 && dst_texture == dst_swapchain->front_buffer
3905 && src_texture == dst_swapchain->back_buffers[0])
3907 /* Use present for back -> front blits. The idea behind this is that
3908 * present is potentially faster than a blit, in particular when FBO
3909 * blits aren't available. Some ddraw applications like Half-Life and
3910 * Prince of Persia 3D use Blt() from the backbuffer to the
3911 * frontbuffer instead of doing a Flip(). D3d8 and d3d9 applications
3912 * can't blit directly to the frontbuffer. */
3913 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
3915 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
3917 /* Set the swap effect to COPY, we don't want the backbuffer to become
3918 * undefined. */
3919 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
3920 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, 0);
3921 dst_swapchain->desc.swap_effect = swap_effect;
3923 return WINED3D_OK;
3925 else if ((flags & WINED3D_BLT_RAW) || (!scale && !convert && !resolve))
3927 blit_op = WINED3D_BLIT_OP_RAW_BLIT;
3930 if (dst_texture->resource.access & WINED3D_RESOURCE_ACCESS_GPU)
3931 dst_location = dst_texture->resource.draw_binding;
3932 else
3933 dst_location = dst_texture->resource.map_binding;
3935 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3936 valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context,
3937 src_surface, src_texture->resource.draw_binding, src_rect,
3938 dst_surface, dst_location, dst_rect, colour_key, filter);
3939 context_release(context);
3941 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
3942 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
3944 return WINED3D_OK;
3946 fallback:
3947 /* Special cases for render targets. */
3948 if (SUCCEEDED(surface_blt_special(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter)))
3949 return WINED3D_OK;
3951 cpu:
3952 return surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
3953 src_texture, src_sub_resource_idx, &src_box, flags, fx, filter);