msvcrt: _Gettnames() should respect user overrides.
[wine.git] / dlls / wined3d / surface.c
blobda22c7ad9aaf2e252c20859e06a19a100afc0701
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 surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
65 struct wined3d_vec3 *coords = info->texcoords;
66 struct float_rect f;
68 switch (target)
70 default:
71 FIXME("Unsupported texture target %#x.\n", target);
72 /* Fall back to GL_TEXTURE_2D */
73 case GL_TEXTURE_2D:
74 info->binding = GL_TEXTURE_BINDING_2D;
75 info->bind_target = GL_TEXTURE_2D;
76 info->tex_type = WINED3D_GL_RES_TYPE_TEX_2D;
77 coords[0].x = (float)rect->left / w;
78 coords[0].y = (float)rect->top / h;
79 coords[0].z = 0.0f;
81 coords[1].x = (float)rect->right / w;
82 coords[1].y = (float)rect->top / h;
83 coords[1].z = 0.0f;
85 coords[2].x = (float)rect->left / w;
86 coords[2].y = (float)rect->bottom / h;
87 coords[2].z = 0.0f;
89 coords[3].x = (float)rect->right / w;
90 coords[3].y = (float)rect->bottom / h;
91 coords[3].z = 0.0f;
92 break;
94 case GL_TEXTURE_RECTANGLE_ARB:
95 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
96 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
97 info->tex_type = WINED3D_GL_RES_TYPE_TEX_RECT;
98 coords[0].x = rect->left; coords[0].y = rect->top; coords[0].z = 0.0f;
99 coords[1].x = rect->right; coords[1].y = rect->top; coords[1].z = 0.0f;
100 coords[2].x = rect->left; coords[2].y = rect->bottom; coords[2].z = 0.0f;
101 coords[3].x = rect->right; coords[3].y = rect->bottom; coords[3].z = 0.0f;
102 break;
104 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
105 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
106 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
107 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
108 cube_coords_float(rect, w, h, &f);
110 coords[0].x = 1.0f; coords[0].y = -f.t; coords[0].z = -f.l;
111 coords[1].x = 1.0f; coords[1].y = -f.t; coords[1].z = -f.r;
112 coords[2].x = 1.0f; coords[2].y = -f.b; coords[2].z = -f.l;
113 coords[3].x = 1.0f; coords[3].y = -f.b; coords[3].z = -f.r;
114 break;
116 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
117 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
118 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
119 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
120 cube_coords_float(rect, w, h, &f);
122 coords[0].x = -1.0f; coords[0].y = -f.t; coords[0].z = f.l;
123 coords[1].x = -1.0f; coords[1].y = -f.t; coords[1].z = f.r;
124 coords[2].x = -1.0f; coords[2].y = -f.b; coords[2].z = f.l;
125 coords[3].x = -1.0f; coords[3].y = -f.b; coords[3].z = f.r;
126 break;
128 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
129 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
130 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
131 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
132 cube_coords_float(rect, w, h, &f);
134 coords[0].x = f.l; coords[0].y = 1.0f; coords[0].z = f.t;
135 coords[1].x = f.r; coords[1].y = 1.0f; coords[1].z = f.t;
136 coords[2].x = f.l; coords[2].y = 1.0f; coords[2].z = f.b;
137 coords[3].x = f.r; coords[3].y = 1.0f; coords[3].z = f.b;
138 break;
140 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
141 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
142 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
143 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
144 cube_coords_float(rect, w, h, &f);
146 coords[0].x = f.l; coords[0].y = -1.0f; coords[0].z = -f.t;
147 coords[1].x = f.r; coords[1].y = -1.0f; coords[1].z = -f.t;
148 coords[2].x = f.l; coords[2].y = -1.0f; coords[2].z = -f.b;
149 coords[3].x = f.r; coords[3].y = -1.0f; coords[3].z = -f.b;
150 break;
152 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
153 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
154 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
155 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
156 cube_coords_float(rect, w, h, &f);
158 coords[0].x = f.l; coords[0].y = -f.t; coords[0].z = 1.0f;
159 coords[1].x = f.r; coords[1].y = -f.t; coords[1].z = 1.0f;
160 coords[2].x = f.l; coords[2].y = -f.b; coords[2].z = 1.0f;
161 coords[3].x = f.r; coords[3].y = -f.b; coords[3].z = 1.0f;
162 break;
164 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
165 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
166 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
167 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
168 cube_coords_float(rect, w, h, &f);
170 coords[0].x = -f.l; coords[0].y = -f.t; coords[0].z = -1.0f;
171 coords[1].x = -f.r; coords[1].y = -f.t; coords[1].z = -1.0f;
172 coords[2].x = -f.l; coords[2].y = -f.b; coords[2].z = -1.0f;
173 coords[3].x = -f.r; coords[3].y = -f.b; coords[3].z = -1.0f;
174 break;
178 /* Context activation is done by the caller. */
179 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
180 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
182 const struct wined3d_gl_info *gl_info = context->gl_info;
183 struct wined3d_texture *texture = src_surface->container;
184 struct blt_info info;
186 surface_get_blt_info(src_surface->texture_target, src_rect,
187 wined3d_texture_get_level_pow2_width(texture, src_surface->texture_level),
188 wined3d_texture_get_level_pow2_height(texture, src_surface->texture_level), &info);
190 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
191 checkGLcall("glEnable(bind_target)");
193 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
195 /* Filtering for StretchRect */
196 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
197 checkGLcall("glTexParameteri");
198 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
199 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
200 checkGLcall("glTexParameteri");
201 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
202 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
203 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
204 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
205 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
206 checkGLcall("glTexEnvi");
208 /* Draw a quad */
209 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
210 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[0].x);
211 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
213 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[1].x);
214 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
216 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[2].x);
217 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
219 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[3].x);
220 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
221 gl_info->gl_ops.gl.p_glEnd();
223 /* Unbind the texture */
224 context_bind_texture(context, info.bind_target, 0);
226 /* We changed the filtering settings on the texture. Inform the
227 * container about this to get the filters reset properly next draw. */
228 texture->texture_rgb.sampler_desc.mag_filter = WINED3D_TEXF_POINT;
229 texture->texture_rgb.sampler_desc.min_filter = WINED3D_TEXF_POINT;
230 texture->texture_rgb.sampler_desc.mip_filter = WINED3D_TEXF_NONE;
231 texture->texture_rgb.sampler_desc.srgb_decode = FALSE;
234 /* Works correctly only for <= 4 bpp formats. */
235 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
237 masks[0] = ((1u << format->red_size) - 1) << format->red_offset;
238 masks[1] = ((1u << format->green_size) - 1) << format->green_offset;
239 masks[2] = ((1u << format->blue_size) - 1) << format->blue_offset;
242 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
244 unsigned int t;
246 t = wined3d_texture_get_level_width(surface->container, surface->texture_level);
247 if ((r->left && r->right) || abs(r->right - r->left) != t)
248 return FALSE;
249 t = wined3d_texture_get_level_height(surface->container, surface->texture_level);
250 if ((r->top && r->bottom) || abs(r->bottom - r->top) != t)
251 return FALSE;
252 return TRUE;
255 static void surface_depth_blt_fbo(const struct wined3d_device *device,
256 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
257 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
259 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
260 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
261 struct wined3d_texture *dst_texture = dst_surface->container;
262 struct wined3d_texture *src_texture = src_surface->container;
263 const struct wined3d_gl_info *gl_info;
264 struct wined3d_context *context;
265 DWORD src_mask, dst_mask;
266 GLbitfield gl_mask;
268 TRACE("device %p\n", device);
269 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
270 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
271 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
272 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
274 src_mask = src_texture->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
275 dst_mask = dst_texture->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
277 if (src_mask != dst_mask)
279 ERR("Incompatible formats %s and %s.\n",
280 debug_d3dformat(src_texture->resource.format->id),
281 debug_d3dformat(dst_texture->resource.format->id));
282 return;
285 if (!src_mask)
287 ERR("Not a depth / stencil format: %s.\n",
288 debug_d3dformat(src_texture->resource.format->id));
289 return;
292 gl_mask = 0;
293 if (src_mask & WINED3DFMT_FLAG_DEPTH)
294 gl_mask |= GL_DEPTH_BUFFER_BIT;
295 if (src_mask & WINED3DFMT_FLAG_STENCIL)
296 gl_mask |= GL_STENCIL_BUFFER_BIT;
298 context = context_acquire(device, NULL, 0);
299 if (!context->valid)
301 context_release(context);
302 WARN("Invalid context, skipping blit.\n");
303 return;
306 /* Make sure the locations are up-to-date. Loading the destination
307 * surface isn't required if the entire surface is overwritten. */
308 wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, src_location);
309 if (!surface_is_full_rect(dst_surface, dst_rect))
310 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
311 else
312 wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, dst_location);
314 gl_info = context->gl_info;
316 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
317 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
319 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
320 context_set_draw_buffer(context, GL_NONE);
321 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
322 context_invalidate_state(context, STATE_FRAMEBUFFER);
324 if (gl_mask & GL_DEPTH_BUFFER_BIT)
326 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
327 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
329 if (gl_mask & GL_STENCIL_BUFFER_BIT)
331 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
333 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
334 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
336 gl_info->gl_ops.gl.p_glStencilMask(~0U);
337 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
340 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
341 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
343 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
344 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
345 checkGLcall("glBlitFramebuffer()");
347 if (wined3d_settings.strict_draw_ordering)
348 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
350 context_release(context);
353 /* Blit between surface locations. Onscreen on different swapchains is not supported.
354 * Depth / stencil is not supported. Context activation is done by the caller. */
355 static void surface_blt_fbo(const struct wined3d_device *device,
356 struct wined3d_context *old_ctx, enum wined3d_texture_filter_type filter,
357 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
358 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
360 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
361 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
362 struct wined3d_texture *dst_texture = dst_surface->container;
363 struct wined3d_texture *src_texture = src_surface->container;
364 const struct wined3d_gl_info *gl_info;
365 struct wined3d_context *context = old_ctx;
366 struct wined3d_surface *required_rt, *restore_rt = NULL;
367 RECT src_rect, dst_rect;
368 GLenum gl_filter;
369 GLenum buffer;
371 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
372 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
373 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
374 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
375 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
377 src_rect = *src_rect_in;
378 dst_rect = *dst_rect_in;
380 switch (filter)
382 case WINED3D_TEXF_LINEAR:
383 gl_filter = GL_LINEAR;
384 break;
386 default:
387 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
388 case WINED3D_TEXF_NONE:
389 case WINED3D_TEXF_POINT:
390 gl_filter = GL_NEAREST;
391 break;
394 /* Resolve the source surface first if needed. */
395 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
396 && (src_texture->resource.format->id != dst_texture->resource.format->id
397 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
398 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
399 src_location = WINED3D_LOCATION_RB_RESOLVED;
401 /* Make sure the locations are up-to-date. Loading the destination
402 * surface isn't required if the entire surface is overwritten. (And is
403 * in fact harmful if we're being called by surface_load_location() with
404 * the purpose of loading the destination surface.) */
405 wined3d_texture_load_location(src_texture, src_sub_resource_idx, old_ctx, src_location);
406 if (!surface_is_full_rect(dst_surface, &dst_rect))
407 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, old_ctx, dst_location);
408 else
409 wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, old_ctx, dst_location);
412 if (src_location == WINED3D_LOCATION_DRAWABLE) required_rt = src_surface;
413 else if (dst_location == WINED3D_LOCATION_DRAWABLE) required_rt = dst_surface;
414 else required_rt = NULL;
416 restore_rt = context_get_rt_surface(old_ctx);
417 if (restore_rt != required_rt)
418 context = context_acquire(device, required_rt ? required_rt->container : NULL,
419 required_rt ? surface_get_sub_resource_idx(required_rt) : 0);
420 else
421 restore_rt = NULL;
423 if (!context->valid)
425 context_release(context);
426 WARN("Invalid context, skipping blit.\n");
427 return;
430 gl_info = context->gl_info;
432 if (src_location == WINED3D_LOCATION_DRAWABLE)
434 TRACE("Source surface %p is onscreen.\n", src_surface);
435 buffer = wined3d_texture_get_gl_buffer(src_texture);
436 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
438 else
440 TRACE("Source surface %p is offscreen.\n", src_surface);
441 buffer = GL_COLOR_ATTACHMENT0;
444 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
445 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
446 checkGLcall("glReadBuffer()");
447 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
449 if (dst_location == WINED3D_LOCATION_DRAWABLE)
451 TRACE("Destination surface %p is onscreen.\n", dst_surface);
452 buffer = wined3d_texture_get_gl_buffer(dst_texture);
453 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
455 else
457 TRACE("Destination surface %p is offscreen.\n", dst_surface);
458 buffer = GL_COLOR_ATTACHMENT0;
461 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
462 context_set_draw_buffer(context, buffer);
463 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
464 context_invalidate_state(context, STATE_FRAMEBUFFER);
466 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
467 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
468 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
469 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
470 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
472 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
473 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
475 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
476 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
477 checkGLcall("glBlitFramebuffer()");
479 if (wined3d_settings.strict_draw_ordering || (dst_location == WINED3D_LOCATION_DRAWABLE
480 && dst_texture->swapchain->front_buffer == dst_texture))
481 gl_info->gl_ops.gl.p_glFlush();
483 if (restore_rt)
484 context_restore(context, restore_rt);
487 static BOOL fbo_blitter_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
488 DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format, DWORD src_location,
489 DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format, DWORD dst_location)
491 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
492 return FALSE;
494 /* Source and/or destination need to be on the GL side */
495 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
496 return FALSE;
498 switch (blit_op)
500 case WINED3D_BLIT_OP_COLOR_BLIT:
501 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
502 || (src_usage & WINED3DUSAGE_RENDERTARGET)))
503 return FALSE;
504 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
505 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
506 return FALSE;
507 if ((src_format->id != dst_format->id || dst_location == WINED3D_LOCATION_DRAWABLE)
508 && (!is_identity_fixup(src_format->color_fixup) || !is_identity_fixup(dst_format->color_fixup)))
509 return FALSE;
510 break;
512 case WINED3D_BLIT_OP_DEPTH_BLIT:
513 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
514 return FALSE;
515 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
516 return FALSE;
517 /* Accept pure swizzle fixups for depth formats. In general we
518 * ignore the stencil component (if present) at the moment and the
519 * swizzle is not relevant with just the depth component. */
520 if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup)
521 || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup))
522 return FALSE;
523 break;
525 default:
526 return FALSE;
529 return TRUE;
532 /* This call just downloads data, the caller is responsible for binding the
533 * correct texture. */
534 /* Context activation is done by the caller. */
535 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
536 DWORD dst_location)
538 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
539 struct wined3d_texture *texture = surface->container;
540 const struct wined3d_format *format = texture->resource.format;
541 struct wined3d_texture_sub_resource *sub_resource;
542 unsigned int dst_row_pitch, dst_slice_pitch;
543 unsigned int src_row_pitch, src_slice_pitch;
544 struct wined3d_bo_address data;
545 BYTE *temporary_mem = NULL;
546 void *mem;
548 /* Only support read back of converted P8 surfaces. */
549 if (texture->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
551 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
552 return;
555 sub_resource = &texture->sub_resources[sub_resource_idx];
557 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
559 /* NP2 emulation is not allowed on array textures. */
560 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
561 ERR("Array texture %p uses NP2 emulation.\n", texture);
563 WARN_(d3d_perf)("Downloading all miplevel layers to get the surface data for a single sub-resource.\n");
565 if (!(temporary_mem = wined3d_calloc(texture->layer_count, sub_resource->size)))
567 ERR("Out of memory.\n");
568 return;
572 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
574 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
576 wined3d_texture_get_pitch(texture, surface->texture_level, &dst_row_pitch, &dst_slice_pitch);
577 wined3d_format_calculate_pitch(format, texture->resource.device->surface_alignment,
578 wined3d_texture_get_level_pow2_width(texture, surface->texture_level),
579 wined3d_texture_get_level_pow2_height(texture, surface->texture_level),
580 &src_row_pitch, &src_slice_pitch);
581 if (!(temporary_mem = HeapAlloc(GetProcessHeap(), 0, src_slice_pitch)))
583 ERR("Out of memory.\n");
584 return;
587 if (data.buffer_object)
588 ERR("NP2 emulated texture uses PBO unexpectedly.\n");
589 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
590 ERR("Unexpected compressed format for NP2 emulated texture.\n");
593 if (temporary_mem)
595 mem = temporary_mem;
597 else if (data.buffer_object)
599 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
600 checkGLcall("glBindBuffer");
601 mem = data.addr;
603 else
605 mem = data.addr;
608 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
610 TRACE("Downloading compressed surface %p, level %u, format %#x, type %#x, data %p.\n",
611 surface, surface->texture_level, format->glFormat, format->glType, mem);
613 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target, surface->texture_level, mem));
614 checkGLcall("glGetCompressedTexImage");
616 else
618 TRACE("Downloading surface %p, level %u, format %#x, type %#x, data %p.\n",
619 surface, surface->texture_level, format->glFormat, format->glType, mem);
621 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
622 format->glFormat, format->glType, mem);
623 checkGLcall("glGetTexImage");
626 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
628 const BYTE *src_data;
629 unsigned int h, y;
630 BYTE *dst_data;
632 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
633 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
634 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
636 * We're doing this...
638 * instead of boxing the texture :
639 * |<-texture width ->| -->pow2width| /\
640 * |111111111111111111| | |
641 * |222 Texture 222222| boxed empty | texture height
642 * |3333 Data 33333333| | |
643 * |444444444444444444| | \/
644 * ----------------------------------- |
645 * | boxed empty | boxed empty | pow2height
646 * | | | \/
647 * -----------------------------------
650 * we're repacking the data to the expected texture width
652 * |<-texture width ->| -->pow2width| /\
653 * |111111111111111111222222222222222| |
654 * |222333333333333333333444444444444| texture height
655 * |444444 | |
656 * | | \/
657 * | | |
658 * | empty | pow2height
659 * | | \/
660 * -----------------------------------
662 * == is the same as
664 * |<-texture width ->| /\
665 * |111111111111111111|
666 * |222222222222222222|texture height
667 * |333333333333333333|
668 * |444444444444444444| \/
669 * --------------------
671 * This also means that any references to surface memory should work with the data as if it were a
672 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
674 * internally the texture is still stored in a boxed format so any references to textureName will
675 * get a boxed texture with width pow2width and not a texture of width resource.width. */
676 src_data = mem;
677 dst_data = data.addr;
678 TRACE("Repacking the surface data from pitch %u to pitch %u.\n", src_row_pitch, dst_row_pitch);
679 h = wined3d_texture_get_level_height(texture, surface->texture_level);
680 for (y = 0; y < h; ++y)
682 memcpy(dst_data, src_data, dst_row_pitch);
683 src_data += src_row_pitch;
684 dst_data += dst_row_pitch;
687 else if (temporary_mem)
689 void *src_data = temporary_mem + surface->texture_layer * sub_resource->size;
690 if (data.buffer_object)
692 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
693 checkGLcall("glBindBuffer");
694 GL_EXTCALL(glBufferSubData(GL_PIXEL_PACK_BUFFER, 0, sub_resource->size, src_data));
695 checkGLcall("glBufferSubData");
697 else
699 memcpy(data.addr, src_data, sub_resource->size);
703 if (data.buffer_object)
705 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
706 checkGLcall("glBindBuffer");
709 HeapFree(GetProcessHeap(), 0, temporary_mem);
712 /* This call just uploads data, the caller is responsible for binding the
713 * correct texture. */
714 /* Context activation is done by the caller. */
715 void wined3d_surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
716 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
717 BOOL srgb, const struct wined3d_const_bo_address *data)
719 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
720 struct wined3d_texture *texture = surface->container;
721 UINT update_w = src_rect->right - src_rect->left;
722 UINT update_h = src_rect->bottom - src_rect->top;
724 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
725 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
726 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
728 if (texture->sub_resources[sub_resource_idx].map_count)
730 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
731 texture->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
734 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE)
736 update_h *= format->height_scale.numerator;
737 update_h /= format->height_scale.denominator;
740 if (data->buffer_object)
742 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
743 checkGLcall("glBindBuffer");
746 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
748 unsigned int dst_row_pitch, dst_slice_pitch;
749 const BYTE *addr = data->addr;
750 GLenum internal;
752 addr += (src_rect->top / format->block_height) * src_pitch;
753 addr += (src_rect->left / format->block_width) * format->block_byte_count;
755 if (srgb)
756 internal = format->glGammaInternal;
757 else if (texture->resource.usage & WINED3DUSAGE_RENDERTARGET
758 && wined3d_resource_is_offscreen(&texture->resource))
759 internal = format->rtInternal;
760 else
761 internal = format->glInternal;
763 wined3d_format_calculate_pitch(format, 1, update_w, update_h, &dst_row_pitch, &dst_slice_pitch);
765 TRACE("Uploading compressed data, target %#x, level %u, layer %u, x %d, y %d, w %u, h %u, "
766 "format %#x, image_size %#x, addr %p.\n",
767 surface->texture_target, surface->texture_level, surface->texture_layer,
768 dst_point->x, dst_point->y, update_w, update_h, internal, dst_slice_pitch, addr);
770 if (dst_row_pitch == src_pitch)
772 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
774 GL_EXTCALL(glCompressedTexSubImage3D(surface->texture_target, surface->texture_level,
775 dst_point->x, dst_point->y, surface->texture_layer, update_w, update_h, 1,
776 internal, dst_slice_pitch, addr));
778 else
780 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
781 dst_point->x, dst_point->y, update_w, update_h,
782 internal, dst_slice_pitch, addr));
785 else
787 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
788 UINT row, y;
790 /* glCompressedTexSubImage2D() ignores pixel store state, so we
791 * can't use the unpack row length like for glTexSubImage2D. */
792 for (row = 0, y = dst_point->y; row < row_count; ++row)
794 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
796 GL_EXTCALL(glCompressedTexSubImage3D(surface->texture_target, surface->texture_level,
797 dst_point->x, y, surface->texture_layer, update_w, format->block_height, 1,
798 internal, dst_row_pitch, addr));
800 else
802 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
803 dst_point->x, y, update_w, format->block_height, internal, dst_row_pitch, addr));
806 y += format->block_height;
807 addr += src_pitch;
810 checkGLcall("Upload compressed surface data");
812 else
814 const BYTE *addr = data->addr;
816 addr += src_rect->top * src_pitch;
817 addr += src_rect->left * format->byte_count;
819 TRACE("Uploading data, target %#x, level %u, layer %u, x %d, y %d, w %u, h %u, "
820 "format %#x, type %#x, addr %p.\n",
821 surface->texture_target, surface->texture_level, surface->texture_layer,
822 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
824 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
825 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
827 GL_EXTCALL(glTexSubImage3D(surface->texture_target, surface->texture_level,
828 dst_point->x, dst_point->y, surface->texture_layer, update_w, update_h, 1,
829 format->glFormat, format->glType, addr));
831 else
833 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
834 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
836 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
837 checkGLcall("Upload surface data");
840 if (data->buffer_object)
842 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
843 checkGLcall("glBindBuffer");
846 if (wined3d_settings.strict_draw_ordering)
847 gl_info->gl_ops.gl.p_glFlush();
849 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
851 struct wined3d_device *device = texture->resource.device;
852 unsigned int i;
854 for (i = 0; i < device->context_count; ++i)
856 context_surface_update(device->contexts[i], surface);
861 static HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
862 struct wined3d_surface *src_surface, const RECT *src_rect)
864 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
865 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
866 struct wined3d_texture *src_texture = src_surface->container;
867 struct wined3d_texture *dst_texture = dst_surface->container;
868 unsigned int src_row_pitch, src_slice_pitch;
869 const struct wined3d_gl_info *gl_info;
870 struct wined3d_context *context;
871 struct wined3d_bo_address data;
872 UINT update_w, update_h;
874 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
875 dst_surface, wine_dbgstr_point(dst_point),
876 src_surface, wine_dbgstr_rect(src_rect));
878 context = context_acquire(dst_texture->resource.device, NULL, 0);
879 gl_info = context->gl_info;
881 /* Only load the surface for partial updates. For newly allocated texture
882 * the texture wouldn't be the current location, and we'd upload zeroes
883 * just to overwrite them again. */
884 update_w = src_rect->right - src_rect->left;
885 update_h = src_rect->bottom - src_rect->top;
886 if (update_w == wined3d_texture_get_level_width(dst_texture, dst_surface->texture_level)
887 && update_h == wined3d_texture_get_level_height(dst_texture, dst_surface->texture_level))
888 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
889 else
890 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
891 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
893 wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &data,
894 src_texture->sub_resources[src_sub_resource_idx].locations);
895 wined3d_texture_get_pitch(src_texture, src_surface->texture_level, &src_row_pitch, &src_slice_pitch);
897 wined3d_surface_upload_data(dst_surface, gl_info, src_texture->resource.format, src_rect,
898 src_row_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
900 context_release(context);
902 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
903 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
905 return WINED3D_OK;
908 /* In D3D the depth stencil dimensions have to be greater than or equal to the
909 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
910 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
911 /* Context activation is done by the caller. */
912 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_rendertarget_info *rt)
914 const struct wined3d_gl_info *gl_info = &surface->container->resource.device->adapter->gl_info;
915 struct wined3d_renderbuffer_entry *entry;
916 unsigned int src_width, src_height;
917 unsigned int width, height;
918 GLuint renderbuffer = 0;
920 if (rt && rt->resource->format->id != WINED3DFMT_NULL)
922 struct wined3d_texture *texture;
923 unsigned int level;
925 if (rt->resource->type == WINED3D_RTYPE_BUFFER)
927 FIXME("Unsupported resource type %s.\n", debug_d3dresourcetype(rt->resource->type));
928 return;
930 texture = wined3d_texture_from_resource(rt->resource);
931 level = rt->sub_resource_idx % texture->level_count;
933 width = wined3d_texture_get_level_pow2_width(texture, level);
934 height = wined3d_texture_get_level_pow2_height(texture, level);
936 else
938 width = wined3d_texture_get_level_pow2_width(surface->container, surface->texture_level);
939 height = wined3d_texture_get_level_pow2_height(surface->container, surface->texture_level);
942 src_width = wined3d_texture_get_level_pow2_width(surface->container, surface->texture_level);
943 src_height = wined3d_texture_get_level_pow2_height(surface->container, surface->texture_level);
945 /* A depth stencil smaller than the render target is not valid */
946 if (width > src_width || height > src_height) return;
948 /* Remove any renderbuffer set if the sizes match */
949 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
950 || (width == src_width && height == src_height))
952 surface->current_renderbuffer = NULL;
953 return;
956 /* Look if we've already got a renderbuffer of the correct dimensions */
957 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
959 if (entry->width == width && entry->height == height)
961 renderbuffer = entry->id;
962 surface->current_renderbuffer = entry;
963 break;
967 if (!renderbuffer)
969 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
970 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
971 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
972 surface->container->resource.format->glInternal, width, height);
974 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
975 entry->width = width;
976 entry->height = height;
977 entry->id = renderbuffer;
978 list_add_head(&surface->renderbuffers, &entry->entry);
980 surface->current_renderbuffer = entry;
983 checkGLcall("set_compatible_renderbuffer");
986 /* See also float_16_to_32() in wined3d_private.h */
987 static inline unsigned short float_32_to_16(const float *in)
989 int exp = 0;
990 float tmp = fabsf(*in);
991 unsigned int mantissa;
992 unsigned short ret;
994 /* Deal with special numbers */
995 if (*in == 0.0f)
996 return 0x0000;
997 if (isnan(*in))
998 return 0x7c01;
999 if (isinf(*in))
1000 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1002 if (tmp < (float)(1u << 10))
1006 tmp = tmp * 2.0f;
1007 exp--;
1008 } while (tmp < (float)(1u << 10));
1010 else if (tmp >= (float)(1u << 11))
1014 tmp /= 2.0f;
1015 exp++;
1016 } while (tmp >= (float)(1u << 11));
1019 mantissa = (unsigned int)tmp;
1020 if (tmp - mantissa >= 0.5f)
1021 ++mantissa; /* Round to nearest, away from zero. */
1023 exp += 10; /* Normalize the mantissa. */
1024 exp += 15; /* Exponent is encoded with excess 15. */
1026 if (exp > 30) /* too big */
1028 ret = 0x7c00; /* INF */
1030 else if (exp <= 0)
1032 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1033 while (exp <= 0)
1035 mantissa = mantissa >> 1;
1036 ++exp;
1038 ret = mantissa & 0x3ff;
1040 else
1042 ret = (exp << 10) | (mantissa & 0x3ff);
1045 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1046 return ret;
1049 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
1050 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1052 unsigned short *dst_s;
1053 const float *src_f;
1054 unsigned int x, y;
1056 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1058 for (y = 0; y < h; ++y)
1060 src_f = (const float *)(src + y * pitch_in);
1061 dst_s = (unsigned short *) (dst + y * pitch_out);
1062 for (x = 0; x < w; ++x)
1064 dst_s[x] = float_32_to_16(src_f + x);
1069 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
1070 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1072 static const unsigned char convert_5to8[] =
1074 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
1075 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
1076 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
1077 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
1079 static const unsigned char convert_6to8[] =
1081 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
1082 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
1083 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
1084 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
1085 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
1086 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
1087 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
1088 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
1090 unsigned int x, y;
1092 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1094 for (y = 0; y < h; ++y)
1096 const WORD *src_line = (const WORD *)(src + y * pitch_in);
1097 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1098 for (x = 0; x < w; ++x)
1100 WORD pixel = src_line[x];
1101 dst_line[x] = 0xff000000u
1102 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
1103 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
1104 | convert_5to8[(pixel & 0x001fu)];
1109 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
1110 * in both cases we're just setting the X / Alpha channel to 0xff. */
1111 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
1112 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1114 unsigned int x, y;
1116 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1118 for (y = 0; y < h; ++y)
1120 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
1121 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1123 for (x = 0; x < w; ++x)
1125 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
1130 static inline BYTE cliptobyte(int x)
1132 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
1135 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
1136 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1138 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1139 unsigned int x, y;
1141 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1143 for (y = 0; y < h; ++y)
1145 const BYTE *src_line = src + y * pitch_in;
1146 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1147 for (x = 0; x < w; ++x)
1149 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1150 * C = Y - 16; D = U - 128; E = V - 128;
1151 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1152 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1153 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1154 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1155 * U and V are shared between the pixels. */
1156 if (!(x & 1)) /* For every even pixel, read new U and V. */
1158 d = (int) src_line[1] - 128;
1159 e = (int) src_line[3] - 128;
1160 r2 = 409 * e + 128;
1161 g2 = - 100 * d - 208 * e + 128;
1162 b2 = 516 * d + 128;
1164 c2 = 298 * ((int) src_line[0] - 16);
1165 dst_line[x] = 0xff000000
1166 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
1167 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
1168 | cliptobyte((c2 + b2) >> 8); /* blue */
1169 /* Scale RGB values to 0..255 range,
1170 * then clip them if still not in range (may be negative),
1171 * then shift them within DWORD if necessary. */
1172 src_line += 2;
1177 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
1178 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1180 unsigned int x, y;
1181 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1183 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
1185 for (y = 0; y < h; ++y)
1187 const BYTE *src_line = src + y * pitch_in;
1188 WORD *dst_line = (WORD *)(dst + y * pitch_out);
1189 for (x = 0; x < w; ++x)
1191 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1192 * C = Y - 16; D = U - 128; E = V - 128;
1193 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1194 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1195 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1196 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1197 * U and V are shared between the pixels. */
1198 if (!(x & 1)) /* For every even pixel, read new U and V. */
1200 d = (int) src_line[1] - 128;
1201 e = (int) src_line[3] - 128;
1202 r2 = 409 * e + 128;
1203 g2 = - 100 * d - 208 * e + 128;
1204 b2 = 516 * d + 128;
1206 c2 = 298 * ((int) src_line[0] - 16);
1207 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
1208 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
1209 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
1210 /* Scale RGB values to 0..255 range,
1211 * then clip them if still not in range (may be negative),
1212 * then shift them within DWORD if necessary. */
1213 src_line += 2;
1218 struct d3dfmt_converter_desc
1220 enum wined3d_format_id from, to;
1221 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
1224 static const struct d3dfmt_converter_desc converters[] =
1226 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
1227 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
1228 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1229 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1230 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
1231 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
1234 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
1235 enum wined3d_format_id to)
1237 unsigned int i;
1239 for (i = 0; i < ARRAY_SIZE(converters); ++i)
1241 if (converters[i].from == from && converters[i].to == to)
1242 return &converters[i];
1245 return NULL;
1248 static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture,
1249 unsigned int sub_resource_idx, const struct wined3d_format *dst_format)
1251 unsigned int texture_level = sub_resource_idx % src_texture->level_count;
1252 const struct wined3d_format *src_format = src_texture->resource.format;
1253 struct wined3d_device *device = src_texture->resource.device;
1254 const struct d3dfmt_converter_desc *conv = NULL;
1255 const struct wined3d_gl_info *gl_info = NULL;
1256 unsigned int src_row_pitch, src_slice_pitch;
1257 struct wined3d_context *context = NULL;
1258 struct wined3d_texture *dst_texture;
1259 struct wined3d_bo_address src_data;
1260 struct wined3d_resource_desc desc;
1261 DWORD map_binding;
1263 if (!(conv = find_converter(src_format->id, dst_format->id)) && (!device->d3d_initialized
1264 || !is_identity_fixup(src_format->color_fixup) || src_format->convert
1265 || !is_identity_fixup(dst_format->color_fixup) || dst_format->convert
1266 || (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)))
1268 FIXME("Cannot find a conversion function from format %s to %s.\n",
1269 debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
1270 return NULL;
1273 /* FIXME: Multisampled conversion? */
1274 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
1275 desc.format = dst_format->id;
1276 desc.multisample_type = WINED3D_MULTISAMPLE_NONE;
1277 desc.multisample_quality = 0;
1278 desc.usage = WINED3DUSAGE_PRIVATE;
1279 desc.pool = WINED3D_POOL_SCRATCH;
1280 desc.width = wined3d_texture_get_level_width(src_texture, texture_level);
1281 desc.height = wined3d_texture_get_level_height(src_texture, texture_level);
1282 desc.depth = 1;
1283 desc.size = 0;
1284 if (FAILED(wined3d_texture_create(device, &desc, 1, 1,
1285 WINED3D_TEXTURE_CREATE_MAPPABLE | WINED3D_TEXTURE_CREATE_DISCARD,
1286 NULL, NULL, &wined3d_null_parent_ops, &dst_texture)))
1288 ERR("Failed to create a destination texture for conversion.\n");
1289 return NULL;
1292 if (device->d3d_initialized)
1294 context = context_acquire(device, NULL, 0);
1295 gl_info = context->gl_info;
1298 map_binding = src_texture->resource.map_binding;
1299 if (!wined3d_texture_load_location(src_texture, sub_resource_idx, context, map_binding))
1300 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
1301 wined3d_texture_get_pitch(src_texture, texture_level, &src_row_pitch, &src_slice_pitch);
1302 wined3d_texture_get_memory(src_texture, sub_resource_idx, &src_data, map_binding);
1304 if (conv)
1306 unsigned int dst_row_pitch, dst_slice_pitch;
1307 struct wined3d_bo_address dst_data;
1308 const BYTE *src;
1309 BYTE *dst;
1311 map_binding = dst_texture->resource.map_binding;
1312 if (!wined3d_texture_load_location(dst_texture, 0, context, map_binding))
1313 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
1314 wined3d_texture_get_pitch(dst_texture, 0, &dst_row_pitch, &dst_slice_pitch);
1315 wined3d_texture_get_memory(dst_texture, 0, &dst_data, map_binding);
1317 src = context_map_bo_address(context, &src_data,
1318 src_texture->sub_resources[sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, 0);
1319 dst = context_map_bo_address(context,
1320 &dst_data, dst_texture->sub_resources[0].size, GL_PIXEL_UNPACK_BUFFER, 0);
1322 conv->convert(src, dst, src_row_pitch, dst_row_pitch, desc.width, desc.height);
1324 wined3d_texture_invalidate_location(dst_texture, 0, ~map_binding);
1325 context_unmap_bo_address(context, &dst_data, GL_PIXEL_UNPACK_BUFFER);
1326 context_unmap_bo_address(context, &src_data, GL_PIXEL_UNPACK_BUFFER);
1328 else
1330 RECT src_rect = {0, 0, desc.width, desc.height};
1331 POINT dst_point = {0, 0};
1333 TRACE("Using upload conversion.\n");
1335 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
1336 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
1337 wined3d_surface_upload_data(dst_texture->sub_resources[0].u.surface, gl_info, src_format,
1338 &src_rect, src_row_pitch, &dst_point, FALSE, wined3d_const_bo_address(&src_data));
1340 wined3d_texture_validate_location(dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB);
1341 wined3d_texture_invalidate_location(dst_texture, 0, ~WINED3D_LOCATION_TEXTURE_RGB);
1344 if (context)
1345 context_release(context);
1347 return dst_texture;
1350 static void read_from_framebuffer(struct wined3d_surface *surface,
1351 struct wined3d_context *old_ctx, DWORD dst_location)
1353 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
1354 struct wined3d_texture *texture = surface->container;
1355 struct wined3d_device *device = texture->resource.device;
1356 const struct wined3d_gl_info *gl_info;
1357 struct wined3d_context *context = old_ctx;
1358 struct wined3d_surface *restore_rt = NULL;
1359 unsigned int row_pitch, slice_pitch;
1360 unsigned int width, height;
1361 BYTE *mem;
1362 BYTE *row, *top, *bottom;
1363 int i;
1364 BOOL srcIsUpsideDown;
1365 struct wined3d_bo_address data;
1367 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
1369 restore_rt = context_get_rt_surface(old_ctx);
1370 if (restore_rt != surface)
1371 context = context_acquire(device, texture, sub_resource_idx);
1372 else
1373 restore_rt = NULL;
1375 context_apply_blit_state(context, device);
1376 gl_info = context->gl_info;
1378 /* Select the correct read buffer, and give some debug output.
1379 * There is no need to keep track of the current read buffer or reset it, every part of the code
1380 * that reads sets the read buffer as desired.
1382 if (wined3d_resource_is_offscreen(&texture->resource))
1384 /* Mapping the primary render target which is not on a swapchain.
1385 * Read from the back buffer. */
1386 TRACE("Mapping offscreen render target.\n");
1387 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1388 srcIsUpsideDown = TRUE;
1390 else
1392 /* Onscreen surfaces are always part of a swapchain */
1393 GLenum buffer = wined3d_texture_get_gl_buffer(texture);
1394 TRACE("Mapping %#x buffer.\n", buffer);
1395 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
1396 checkGLcall("glReadBuffer");
1397 srcIsUpsideDown = FALSE;
1400 if (data.buffer_object)
1402 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1403 checkGLcall("glBindBuffer");
1406 wined3d_texture_get_pitch(texture, surface->texture_level, &row_pitch, &slice_pitch);
1408 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1409 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / texture->resource.format->byte_count);
1410 checkGLcall("glPixelStorei");
1412 width = wined3d_texture_get_level_width(texture, surface->texture_level);
1413 height = wined3d_texture_get_level_height(texture, surface->texture_level);
1414 gl_info->gl_ops.gl.p_glReadPixels(0, 0, width, height,
1415 texture->resource.format->glFormat,
1416 texture->resource.format->glType, data.addr);
1417 checkGLcall("glReadPixels");
1419 /* Reset previous pixel store pack state */
1420 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
1421 checkGLcall("glPixelStorei");
1423 if (!srcIsUpsideDown)
1425 /* glReadPixels returns the image upside down, and there is no way to
1426 * prevent this. Flip the lines in software. */
1428 if (!(row = HeapAlloc(GetProcessHeap(), 0, row_pitch)))
1429 goto error;
1431 if (data.buffer_object)
1433 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
1434 checkGLcall("glMapBuffer");
1436 else
1437 mem = data.addr;
1439 top = mem;
1440 bottom = mem + row_pitch * (height - 1);
1441 for (i = 0; i < height / 2; i++)
1443 memcpy(row, top, row_pitch);
1444 memcpy(top, bottom, row_pitch);
1445 memcpy(bottom, row, row_pitch);
1446 top += row_pitch;
1447 bottom -= row_pitch;
1449 HeapFree(GetProcessHeap(), 0, row);
1451 if (data.buffer_object)
1452 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
1455 error:
1456 if (data.buffer_object)
1458 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1459 checkGLcall("glBindBuffer");
1462 if (restore_rt)
1463 context_restore(context, restore_rt);
1466 /* Read the framebuffer contents into a texture. Note that this function
1467 * doesn't do any kind of flipping. Using this on an onscreen surface will
1468 * result in a flipped D3D texture.
1470 * Context activation is done by the caller. This function may temporarily
1471 * switch to a different context and restore the original one before return. */
1472 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb, struct wined3d_context *old_ctx)
1474 struct wined3d_texture *texture = surface->container;
1475 struct wined3d_device *device = texture->resource.device;
1476 const struct wined3d_gl_info *gl_info;
1477 struct wined3d_context *context = old_ctx;
1478 struct wined3d_surface *restore_rt = NULL;
1480 restore_rt = context_get_rt_surface(old_ctx);
1481 if (restore_rt != surface)
1482 context = context_acquire(device, texture, surface_get_sub_resource_idx(surface));
1483 else
1484 restore_rt = NULL;
1486 gl_info = context->gl_info;
1487 device_invalidate_state(device, STATE_FRAMEBUFFER);
1489 wined3d_texture_prepare_texture(texture, context, srgb);
1490 wined3d_texture_bind_and_dirtify(texture, context, srgb);
1492 TRACE("Reading back offscreen render target %p.\n", surface);
1494 if (wined3d_resource_is_offscreen(&texture->resource))
1495 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1496 else
1497 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(texture));
1498 checkGLcall("glReadBuffer");
1500 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
1501 0, 0, 0, 0, wined3d_texture_get_level_width(texture, surface->texture_level),
1502 wined3d_texture_get_level_height(texture, surface->texture_level));
1503 checkGLcall("glCopyTexSubImage2D");
1505 if (restore_rt)
1506 context_restore(context, restore_rt);
1509 /* Does a direct frame buffer -> texture copy. Stretching is done with single
1510 * pixel copy calls. */
1511 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
1512 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
1514 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
1515 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1516 struct wined3d_texture *src_texture = src_surface->container;
1517 struct wined3d_texture *dst_texture = dst_surface->container;
1518 struct wined3d_device *device = dst_texture->resource.device;
1519 const struct wined3d_gl_info *gl_info;
1520 float xrel, yrel;
1521 struct wined3d_context *context;
1522 BOOL upsidedown = FALSE;
1523 RECT dst_rect = *dst_rect_in;
1524 unsigned int src_height;
1526 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1527 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1529 if(dst_rect.top > dst_rect.bottom) {
1530 UINT tmp = dst_rect.bottom;
1531 dst_rect.bottom = dst_rect.top;
1532 dst_rect.top = tmp;
1533 upsidedown = TRUE;
1536 context = context_acquire(device, src_texture, src_sub_resource_idx);
1537 gl_info = context->gl_info;
1538 context_apply_blit_state(context, device);
1539 wined3d_texture_load(dst_texture, context, FALSE);
1541 /* Bind the target texture */
1542 context_bind_texture(context, dst_texture->target, dst_texture->texture_rgb.name);
1543 if (wined3d_resource_is_offscreen(&src_texture->resource))
1545 TRACE("Reading from an offscreen target\n");
1546 upsidedown = !upsidedown;
1547 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1549 else
1551 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
1553 checkGLcall("glReadBuffer");
1555 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
1556 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
1558 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1560 FIXME_(d3d_perf)("Doing a pixel by pixel copy from the framebuffer to a texture.\n");
1562 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1563 ERR("Texture filtering not supported in direct blit.\n");
1565 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1566 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1568 ERR("Texture filtering not supported in direct blit\n");
1571 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
1572 if (upsidedown
1573 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1574 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1576 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
1577 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1578 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
1579 src_rect->left, src_height - src_rect->bottom,
1580 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1582 else
1584 LONG row;
1585 UINT yoffset = src_height - src_rect->top + dst_rect.top - 1;
1586 /* I have to process this row by row to swap the image,
1587 * otherwise it would be upside down, so stretching in y direction
1588 * doesn't cost extra time
1590 * However, stretching in x direction can be avoided if not necessary
1592 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
1593 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1595 /* Well, that stuff works, but it's very slow.
1596 * find a better way instead
1598 LONG col;
1600 for (col = dst_rect.left; col < dst_rect.right; ++col)
1602 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1603 dst_rect.left + col /* x offset */, row /* y offset */,
1604 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
1607 else
1609 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1610 dst_rect.left /* x offset */, row /* y offset */,
1611 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
1615 checkGLcall("glCopyTexSubImage2D");
1617 context_release(context);
1619 /* The texture is now most up to date - If the surface is a render target
1620 * and has a drawable, this path is never entered. */
1621 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1622 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1625 /* Uses the hardware to stretch and flip the image */
1626 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
1627 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
1629 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
1630 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1631 unsigned int src_width, src_height, src_pow2_width, src_pow2_height;
1632 struct wined3d_texture *src_texture = src_surface->container;
1633 struct wined3d_texture *dst_texture = dst_surface->container;
1634 struct wined3d_device *device = dst_texture->resource.device;
1635 GLuint src, backup = 0;
1636 float left, right, top, bottom; /* Texture coordinates */
1637 const struct wined3d_gl_info *gl_info;
1638 struct wined3d_context *context;
1639 GLenum drawBuffer = GL_BACK;
1640 GLenum offscreen_buffer;
1641 GLenum texture_target;
1642 BOOL noBackBufferBackup;
1643 BOOL src_offscreen;
1644 BOOL upsidedown = FALSE;
1645 RECT dst_rect = *dst_rect_in;
1647 TRACE("Using hwstretch blit\n");
1648 /* Activate the Proper context for reading from the source surface, set it up for blitting */
1649 context = context_acquire(device, src_texture, src_sub_resource_idx);
1650 gl_info = context->gl_info;
1651 context_apply_blit_state(context, device);
1652 wined3d_texture_load(dst_texture, context, FALSE);
1654 offscreen_buffer = context_get_offscreen_gl_buffer(context);
1655 src_width = wined3d_texture_get_level_width(src_texture, src_surface->texture_level);
1656 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
1657 src_pow2_width = wined3d_texture_get_level_pow2_width(src_texture, src_surface->texture_level);
1658 src_pow2_height = wined3d_texture_get_level_pow2_height(src_texture, src_surface->texture_level);
1660 src_offscreen = wined3d_resource_is_offscreen(&src_texture->resource);
1661 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
1662 if (!noBackBufferBackup && !src_texture->texture_rgb.name)
1664 /* Get it a description */
1665 wined3d_texture_load(src_texture, context, FALSE);
1668 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
1669 * This way we don't have to wait for the 2nd readback to finish to leave this function.
1671 if (context->aux_buffers >= 2)
1673 /* Got more than one aux buffer? Use the 2nd aux buffer */
1674 drawBuffer = GL_AUX1;
1676 else if ((!src_offscreen || offscreen_buffer == GL_BACK) && context->aux_buffers >= 1)
1678 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
1679 drawBuffer = GL_AUX0;
1682 if (noBackBufferBackup)
1684 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
1685 checkGLcall("glGenTextures");
1686 context_bind_texture(context, GL_TEXTURE_2D, backup);
1687 texture_target = GL_TEXTURE_2D;
1689 else
1691 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
1692 * we are reading from the back buffer, the backup can be used as source texture
1694 texture_target = src_surface->texture_target;
1695 context_bind_texture(context, texture_target, src_texture->texture_rgb.name);
1696 gl_info->gl_ops.gl.p_glEnable(texture_target);
1697 checkGLcall("glEnable(texture_target)");
1699 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
1700 surface_get_sub_resource(src_surface)->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
1703 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1704 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1706 if(dst_rect.top > dst_rect.bottom) {
1707 UINT tmp = dst_rect.bottom;
1708 dst_rect.bottom = dst_rect.top;
1709 dst_rect.top = tmp;
1710 upsidedown = TRUE;
1713 if (src_offscreen)
1715 TRACE("Reading from an offscreen target\n");
1716 upsidedown = !upsidedown;
1717 gl_info->gl_ops.gl.p_glReadBuffer(offscreen_buffer);
1719 else
1721 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
1724 /* TODO: Only back up the part that will be overwritten */
1725 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, src_width, src_height);
1727 checkGLcall("glCopyTexSubImage2D");
1729 /* No issue with overriding these - the sampler is dirty due to blit usage */
1730 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
1731 checkGLcall("glTexParameteri");
1732 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
1733 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
1734 checkGLcall("glTexParameteri");
1736 if (!src_texture->swapchain || src_texture == src_texture->swapchain->back_buffers[0])
1738 src = backup ? backup : src_texture->texture_rgb.name;
1740 else
1742 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
1743 checkGLcall("glReadBuffer(GL_FRONT)");
1745 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
1746 checkGLcall("glGenTextures(1, &src)");
1747 context_bind_texture(context, GL_TEXTURE_2D, src);
1749 /* TODO: Only copy the part that will be read. Use src_rect->left,
1750 * src_rect->bottom as origin, but with the width watch out for power
1751 * of 2 sizes. */
1752 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_pow2_width,
1753 src_pow2_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
1754 checkGLcall("glTexImage2D");
1755 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, src_width, src_height);
1757 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1758 checkGLcall("glTexParameteri");
1759 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1760 checkGLcall("glTexParameteri");
1762 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
1763 checkGLcall("glReadBuffer(GL_BACK)");
1765 if (texture_target != GL_TEXTURE_2D)
1767 gl_info->gl_ops.gl.p_glDisable(texture_target);
1768 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
1769 texture_target = GL_TEXTURE_2D;
1772 checkGLcall("glEnd and previous");
1774 left = src_rect->left;
1775 right = src_rect->right;
1777 if (!upsidedown)
1779 top = src_height - src_rect->top;
1780 bottom = src_height - src_rect->bottom;
1782 else
1784 top = src_height - src_rect->bottom;
1785 bottom = src_height - src_rect->top;
1788 if (src_texture->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
1790 left /= src_pow2_width;
1791 right /= src_pow2_width;
1792 top /= src_pow2_height;
1793 bottom /= src_pow2_height;
1796 /* draw the source texture stretched and upside down. The correct surface is bound already */
1797 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1798 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1800 context_set_draw_buffer(context, drawBuffer);
1801 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
1803 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
1804 /* bottom left */
1805 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
1806 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
1808 /* top left */
1809 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
1810 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
1812 /* top right */
1813 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
1814 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1816 /* bottom right */
1817 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
1818 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
1819 gl_info->gl_ops.gl.p_glEnd();
1820 checkGLcall("glEnd and previous");
1822 if (texture_target != dst_surface->texture_target)
1824 gl_info->gl_ops.gl.p_glDisable(texture_target);
1825 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
1826 texture_target = dst_surface->texture_target;
1829 /* Now read the stretched and upside down image into the destination texture */
1830 context_bind_texture(context, texture_target, dst_texture->texture_rgb.name);
1831 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
1833 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
1834 0, 0, /* We blitted the image to the origin */
1835 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1836 checkGLcall("glCopyTexSubImage2D");
1838 if (drawBuffer == GL_BACK)
1840 /* Write the back buffer backup back. */
1841 if (backup)
1843 if (texture_target != GL_TEXTURE_2D)
1845 gl_info->gl_ops.gl.p_glDisable(texture_target);
1846 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
1847 texture_target = GL_TEXTURE_2D;
1849 context_bind_texture(context, GL_TEXTURE_2D, backup);
1851 else
1853 if (texture_target != src_surface->texture_target)
1855 gl_info->gl_ops.gl.p_glDisable(texture_target);
1856 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
1857 texture_target = src_surface->texture_target;
1859 context_bind_texture(context, src_surface->texture_target, src_texture->texture_rgb.name);
1862 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
1863 /* top left */
1864 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
1865 gl_info->gl_ops.gl.p_glVertex2i(0, src_height);
1867 /* bottom left */
1868 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)src_height / (float)src_pow2_height);
1869 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
1871 /* bottom right */
1872 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width,
1873 (float)src_height / (float)src_pow2_height);
1874 gl_info->gl_ops.gl.p_glVertex2i(src_width, 0);
1876 /* top right */
1877 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width, 0.0f);
1878 gl_info->gl_ops.gl.p_glVertex2i(src_width, src_height);
1879 gl_info->gl_ops.gl.p_glEnd();
1881 gl_info->gl_ops.gl.p_glDisable(texture_target);
1882 checkGLcall("glDisable(texture_target)");
1884 /* Cleanup */
1885 if (src != src_texture->texture_rgb.name && src != backup)
1887 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
1888 checkGLcall("glDeleteTextures(1, &src)");
1890 if (backup)
1892 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
1893 checkGLcall("glDeleteTextures(1, &backup)");
1896 if (wined3d_settings.strict_draw_ordering)
1897 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
1899 context_release(context);
1901 /* The texture is now most up to date - If the surface is a render target
1902 * and has a drawable, this path is never entered. */
1903 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1904 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1907 /* Front buffer coordinates are always full screen coordinates, but our GL
1908 * drawable is limited to the window's client area. The sysmem and texture
1909 * copies do have the full screen size. Note that GL has a bottom-left
1910 * origin, while D3D has a top-left origin. */
1911 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
1913 struct wined3d_texture *texture = surface->container;
1914 POINT offset = {0, 0};
1915 UINT drawable_height;
1916 RECT windowsize;
1918 if (!texture->swapchain)
1919 return;
1921 if (texture == texture->swapchain->front_buffer)
1923 ScreenToClient(window, &offset);
1924 OffsetRect(rect, offset.x, offset.y);
1927 GetClientRect(window, &windowsize);
1928 drawable_height = windowsize.bottom - windowsize.top;
1930 rect->top = drawable_height - rect->top;
1931 rect->bottom = drawable_height - rect->bottom;
1934 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
1935 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
1936 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
1938 struct wined3d_texture *dst_texture = dst_surface->container;
1939 struct wined3d_device *device = dst_texture->resource.device;
1940 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
1941 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1942 struct wined3d_texture *src_texture;
1944 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
1945 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
1946 flags, fx, debug_d3dtexturefiltertype(filter));
1948 /* Get the swapchain. One of the surfaces has to be a primary surface */
1949 if (dst_texture->resource.pool == WINED3D_POOL_SYSTEM_MEM)
1951 WARN("Destination is in sysmem, rejecting gl blt\n");
1952 return WINED3DERR_INVALIDCALL;
1955 dst_swapchain = dst_texture->swapchain;
1957 if (src_surface)
1959 src_texture = src_surface->container;
1960 if (src_texture->resource.pool == WINED3D_POOL_SYSTEM_MEM)
1962 WARN("Src is in sysmem, rejecting gl blt\n");
1963 return WINED3DERR_INVALIDCALL;
1966 src_swapchain = src_texture->swapchain;
1968 else
1970 src_texture = NULL;
1971 src_swapchain = NULL;
1974 /* Early sort out of cases where no render target is used */
1975 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
1977 TRACE("No surface is render target, not using hardware blit.\n");
1978 return WINED3DERR_INVALIDCALL;
1981 /* No destination color keying supported */
1982 if (flags & (WINED3D_BLT_DST_CKEY | WINED3D_BLT_DST_CKEY_OVERRIDE))
1984 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
1985 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
1986 return WINED3DERR_INVALIDCALL;
1989 if (dst_swapchain && dst_swapchain == src_swapchain)
1991 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
1992 return WINED3DERR_INVALIDCALL;
1995 if (dst_swapchain && src_swapchain)
1997 FIXME("Implement hardware blit between two different swapchains\n");
1998 return WINED3DERR_INVALIDCALL;
2001 if (dst_swapchain)
2003 /* Handled with regular texture -> swapchain blit */
2004 if (src_surface == rt)
2005 TRACE("Blit from active render target to a swapchain\n");
2007 else if (src_swapchain && dst_surface == rt)
2009 FIXME("Implement blit from a swapchain to the active render target\n");
2010 return WINED3DERR_INVALIDCALL;
2013 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
2015 unsigned int src_width, src_height;
2016 /* Blit from render target to texture */
2017 BOOL stretchx;
2019 /* P8 read back is not implemented */
2020 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT
2021 || dst_texture->resource.format->id == WINED3DFMT_P8_UINT)
2023 TRACE("P8 read back not supported by frame buffer to texture blit\n");
2024 return WINED3DERR_INVALIDCALL;
2027 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_SRC_CKEY_OVERRIDE))
2029 TRACE("Color keying not supported by frame buffer to texture blit\n");
2030 return WINED3DERR_INVALIDCALL;
2031 /* Destination color key is checked above */
2034 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
2035 stretchx = TRUE;
2036 else
2037 stretchx = FALSE;
2039 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2040 * flip the image nor scale it.
2042 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2043 * -> If the app wants an image width an unscaled width, copy it line per line
2044 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
2045 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2046 * back buffer. This is slower than reading line per line, thus not used for flipping
2047 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2048 * pixel by pixel. */
2049 src_width = wined3d_texture_get_level_width(src_texture, src_surface->texture_level);
2050 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
2051 if (!stretchx || dst_rect->right - dst_rect->left > src_width
2052 || dst_rect->bottom - dst_rect->top > src_height)
2054 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
2055 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
2057 else
2059 TRACE("Using hardware stretching to flip / stretch the texture.\n");
2060 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
2063 return WINED3D_OK;
2066 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
2067 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
2068 return WINED3DERR_INVALIDCALL;
2071 /* Context activation is done by the caller. */
2072 static BOOL surface_load_sysmem(struct wined3d_surface *surface,
2073 struct wined3d_context *context, DWORD dst_location)
2075 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2076 const struct wined3d_gl_info *gl_info = context->gl_info;
2077 struct wined3d_texture *texture = surface->container;
2078 struct wined3d_texture_sub_resource *sub_resource;
2080 sub_resource = &texture->sub_resources[sub_resource_idx];
2081 wined3d_texture_prepare_location(texture, sub_resource_idx, context, dst_location);
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;
2097 if (!(texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2098 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
2100 read_from_framebuffer(surface, context, dst_location);
2101 return TRUE;
2104 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
2105 surface, wined3d_debug_location(sub_resource->locations));
2106 return FALSE;
2109 /* Context activation is done by the caller. */
2110 static BOOL surface_load_drawable(struct wined3d_surface *surface,
2111 struct wined3d_context *context)
2113 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2114 struct wined3d_texture *texture = surface->container;
2115 struct wined3d_surface *restore_rt = NULL;
2116 struct wined3d_device *device;
2117 RECT r;
2119 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2121 DWORD current = texture->sub_resources[sub_resource_idx].locations;
2122 FIXME("Unimplemented copy from %s for depth/stencil buffers.\n",
2123 wined3d_debug_location(current));
2124 return FALSE;
2127 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
2128 && wined3d_resource_is_offscreen(&texture->resource))
2130 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
2131 return FALSE;
2134 device = texture->resource.device;
2135 restore_rt = context_get_rt_surface(context);
2136 if (restore_rt != surface)
2137 context = context_acquire(device, texture, sub_resource_idx);
2138 else
2139 restore_rt = NULL;
2141 SetRect(&r, 0, 0, wined3d_texture_get_level_width(texture, surface->texture_level),
2142 wined3d_texture_get_level_height(texture, surface->texture_level));
2143 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
2144 device->blitter->ops->blitter_blit(device->blitter, WINED3D_BLIT_OP_COLOR_BLIT, context,
2145 surface, WINED3D_LOCATION_TEXTURE_RGB, &r,
2146 surface, WINED3D_LOCATION_DRAWABLE, &r,
2147 NULL, WINED3D_TEXF_POINT);
2149 if (restore_rt)
2150 context_restore(context, restore_rt);
2152 return TRUE;
2155 static BOOL surface_load_texture(struct wined3d_surface *surface,
2156 struct wined3d_context *context, BOOL srgb)
2158 unsigned int width, height, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch;
2159 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2160 const struct wined3d_gl_info *gl_info = context->gl_info;
2161 struct wined3d_texture *texture = surface->container;
2162 struct wined3d_device *device = texture->resource.device;
2163 const struct wined3d_color_key_conversion *conversion;
2164 struct wined3d_texture_sub_resource *sub_resource;
2165 struct wined3d_bo_address data;
2166 BYTE *src_mem, *dst_mem = NULL;
2167 struct wined3d_format format;
2168 POINT dst_point = {0, 0};
2169 RECT src_rect;
2170 BOOL depth;
2172 depth = texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL;
2173 sub_resource = surface_get_sub_resource(surface);
2175 if (!depth && wined3d_settings.offscreen_rendering_mode != ORM_FBO
2176 && wined3d_resource_is_offscreen(&texture->resource)
2177 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
2179 surface_load_fb_texture(surface, srgb, context);
2181 return TRUE;
2184 width = wined3d_texture_get_level_width(texture, surface->texture_level);
2185 height = wined3d_texture_get_level_height(texture, surface->texture_level);
2186 SetRect(&src_rect, 0, 0, width, height);
2188 if (!depth && sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
2189 && (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
2190 && fbo_blitter_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
2191 texture->resource.usage, texture->resource.pool,
2192 texture->resource.format, WINED3D_LOCATION_TEXTURE_RGB,
2193 texture->resource.usage, texture->resource.pool,
2194 texture->resource.format, WINED3D_LOCATION_TEXTURE_SRGB))
2196 if (srgb)
2197 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
2198 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
2199 else
2200 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
2201 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
2203 return TRUE;
2206 if (!depth && sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
2207 && (!srgb || (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)))
2209 DWORD src_location = sub_resource->locations & WINED3D_LOCATION_RB_RESOLVED ?
2210 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
2211 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2213 if (fbo_blitter_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
2214 texture->resource.usage, texture->resource.pool, texture->resource.format, src_location,
2215 texture->resource.usage, texture->resource.pool, texture->resource.format, dst_location))
2216 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, src_location,
2217 &src_rect, surface, dst_location, &src_rect);
2219 return TRUE;
2222 /* Upload from system memory */
2224 if (srgb)
2226 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | texture->resource.map_binding))
2227 == WINED3D_LOCATION_TEXTURE_RGB)
2229 FIXME_(d3d_perf)("Downloading RGB surface %p to reload it as sRGB.\n", surface);
2230 wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding);
2233 else
2235 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | texture->resource.map_binding))
2236 == WINED3D_LOCATION_TEXTURE_SRGB)
2238 FIXME_(d3d_perf)("Downloading sRGB surface %p to reload it as RGB.\n", surface);
2239 wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding);
2243 if (!(sub_resource->locations & surface_simple_locations))
2245 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
2246 /* Lets hope we get it from somewhere... */
2247 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM);
2250 wined3d_texture_prepare_texture(texture, context, srgb);
2251 wined3d_texture_bind_and_dirtify(texture, context, srgb);
2252 wined3d_texture_get_pitch(texture, surface->texture_level, &src_row_pitch, &src_slice_pitch);
2254 format = *texture->resource.format;
2255 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
2256 format = *wined3d_get_format(gl_info, conversion->dst_format, texture->resource.usage);
2258 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
2259 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
2260 * getting called. */
2261 if ((format.convert || conversion) && texture->sub_resources[sub_resource_idx].buffer_object)
2263 TRACE("Removing the pbo attached to surface %p.\n", surface);
2265 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM);
2266 wined3d_texture_set_map_binding(texture, WINED3D_LOCATION_SYSMEM);
2269 wined3d_texture_get_memory(texture, sub_resource_idx, &data, sub_resource->locations);
2270 if (format.convert)
2272 /* This code is entered for texture formats which need a fixup. */
2273 format.byte_count = format.conv_byte_count;
2274 wined3d_format_calculate_pitch(&format, 1, width, height, &dst_row_pitch, &dst_slice_pitch);
2276 src_mem = context_map_bo_address(context, &data, src_slice_pitch,
2277 GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READONLY);
2278 if (!(dst_mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
2280 ERR("Out of memory (%u).\n", dst_slice_pitch);
2281 context_release(context);
2282 return FALSE;
2284 format.convert(src_mem, dst_mem, src_row_pitch, src_slice_pitch,
2285 dst_row_pitch, dst_slice_pitch, width, height, 1);
2286 src_row_pitch = dst_row_pitch;
2287 context_unmap_bo_address(context, &data, GL_PIXEL_UNPACK_BUFFER);
2289 data.buffer_object = 0;
2290 data.addr = dst_mem;
2292 else if (conversion)
2294 /* This code is only entered for color keying fixups */
2295 struct wined3d_palette *palette = NULL;
2297 wined3d_format_calculate_pitch(&format, device->surface_alignment,
2298 width, height, &dst_row_pitch, &dst_slice_pitch);
2300 src_mem = context_map_bo_address(context, &data, src_slice_pitch,
2301 GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READONLY);
2302 if (!(dst_mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
2304 ERR("Out of memory (%u).\n", dst_slice_pitch);
2305 context_release(context);
2306 return FALSE;
2308 if (texture->swapchain && texture->swapchain->palette)
2309 palette = texture->swapchain->palette;
2310 conversion->convert(src_mem, src_row_pitch, dst_mem, dst_row_pitch,
2311 width, height, palette, &texture->async.gl_color_key);
2312 src_row_pitch = dst_row_pitch;
2313 context_unmap_bo_address(context, &data, GL_PIXEL_UNPACK_BUFFER);
2315 data.buffer_object = 0;
2316 data.addr = dst_mem;
2319 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
2320 src_row_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
2322 HeapFree(GetProcessHeap(), 0, dst_mem);
2324 return TRUE;
2327 /* Context activation is done by the caller. */
2328 static BOOL surface_load_renderbuffer(struct wined3d_surface *surface, struct wined3d_context *context,
2329 DWORD dst_location)
2331 struct wined3d_texture *texture = surface->container;
2332 const RECT rect = {0, 0,
2333 wined3d_texture_get_level_width(texture, surface->texture_level),
2334 wined3d_texture_get_level_height(texture, surface->texture_level)};
2335 DWORD locations = surface_get_sub_resource(surface)->locations;
2336 DWORD src_location;
2338 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2340 FIXME("Unimplemented copy from %s for depth/stencil buffers.\n",
2341 wined3d_debug_location(locations));
2342 return FALSE;
2345 if (locations & WINED3D_LOCATION_RB_MULTISAMPLE)
2346 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
2347 else if (locations & WINED3D_LOCATION_RB_RESOLVED)
2348 src_location = WINED3D_LOCATION_RB_RESOLVED;
2349 else if (locations & WINED3D_LOCATION_TEXTURE_SRGB)
2350 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
2351 else /* surface_blt_fbo will load the source location if necessary. */
2352 src_location = WINED3D_LOCATION_TEXTURE_RGB;
2354 surface_blt_fbo(texture->resource.device, context, WINED3D_TEXF_POINT,
2355 surface, src_location, &rect, surface, dst_location, &rect);
2357 return TRUE;
2360 /* Context activation is done by the caller. Context may be NULL in ddraw-only mode. */
2361 BOOL surface_load_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
2363 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
2365 switch (location)
2367 case WINED3D_LOCATION_USER_MEMORY:
2368 case WINED3D_LOCATION_SYSMEM:
2369 case WINED3D_LOCATION_BUFFER:
2370 return surface_load_sysmem(surface, context, location);
2372 case WINED3D_LOCATION_DRAWABLE:
2373 return surface_load_drawable(surface, context);
2375 case WINED3D_LOCATION_RB_RESOLVED:
2376 case WINED3D_LOCATION_RB_MULTISAMPLE:
2377 return surface_load_renderbuffer(surface, context, location);
2379 case WINED3D_LOCATION_TEXTURE_RGB:
2380 case WINED3D_LOCATION_TEXTURE_SRGB:
2381 return surface_load_texture(surface, context,
2382 location == WINED3D_LOCATION_TEXTURE_SRGB);
2384 default:
2385 ERR("Don't know how to handle location %#x.\n", location);
2386 return FALSE;
2390 /* Context activation is done by the caller. */
2391 static void fbo_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2393 struct wined3d_blitter *next;
2395 if ((next = blitter->next))
2396 next->ops->blitter_destroy(next, context);
2398 HeapFree(GetProcessHeap(), 0, blitter);
2401 static void fbo_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
2402 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
2403 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
2405 struct wined3d_blitter *next;
2407 if ((next = blitter->next))
2408 next->ops->blitter_clear(next, device, rt_count, fb, rect_count,
2409 clear_rects, draw_rect, flags, colour, depth, stencil);
2412 static DWORD fbo_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
2413 struct wined3d_context *context, struct wined3d_surface *src_surface, DWORD src_location,
2414 const RECT *src_rect, struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect,
2415 const struct wined3d_color_key *colour_key, enum wined3d_texture_filter_type filter)
2417 struct wined3d_resource *src_resource = &src_surface->container->resource;
2418 struct wined3d_resource *dst_resource = &dst_surface->container->resource;
2419 struct wined3d_device *device = dst_resource->device;
2420 enum wined3d_blit_op blit_op = op;
2421 struct wined3d_blitter *next;
2423 if (blit_op == WINED3D_BLIT_OP_RAW_BLIT && dst_resource->format->id == src_resource->format->id)
2425 if (dst_resource->format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
2426 blit_op = WINED3D_BLIT_OP_DEPTH_BLIT;
2427 else
2428 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
2431 if (!fbo_blitter_supported(&device->adapter->gl_info, blit_op,
2432 src_resource->usage, src_resource->pool, src_resource->format, src_location,
2433 src_resource->usage, dst_resource->pool, dst_resource->format, dst_location))
2435 if ((next = blitter->next))
2436 return next->ops->blitter_blit(next, op, context, src_surface, src_location,
2437 src_rect, dst_surface, dst_location, dst_rect, colour_key, filter);
2440 if (blit_op == WINED3D_BLIT_OP_COLOR_BLIT)
2442 TRACE("Colour blit.\n");
2443 surface_blt_fbo(device, context, filter, src_surface, src_location,
2444 src_rect, dst_surface, dst_location, dst_rect);
2445 return dst_location;
2448 if (blit_op == WINED3D_BLIT_OP_DEPTH_BLIT)
2450 TRACE("Depth/stencil blit.\n");
2451 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
2452 return dst_location;
2455 ERR("This blitter does not implement blit op %#x.\n", blit_op);
2456 return dst_location;
2459 static const struct wined3d_blitter_ops fbo_blitter_ops =
2461 fbo_blitter_destroy,
2462 fbo_blitter_clear,
2463 fbo_blitter_blit,
2466 void wined3d_fbo_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
2468 struct wined3d_blitter *blitter;
2470 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
2471 return;
2473 if (!(blitter = HeapAlloc(GetProcessHeap(), 0, sizeof(*blitter))))
2474 return;
2476 TRACE("Created blitter %p.\n", blitter);
2478 blitter->ops = &fbo_blitter_ops;
2479 blitter->next = *next;
2480 *next = blitter;
2483 /* Context activation is done by the caller. */
2484 static void raw_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2486 struct wined3d_blitter *next;
2488 if ((next = blitter->next))
2489 next->ops->blitter_destroy(next, context);
2491 HeapFree(GetProcessHeap(), 0, blitter);
2494 /* Context activation is done by the caller. */
2495 static void raw_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
2496 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
2497 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
2499 struct wined3d_blitter *next;
2501 if (!(next = blitter->next))
2503 ERR("No blitter to handle clear.\n");
2504 return;
2507 TRACE("Forwarding to blitter %p.\n", next);
2508 next->ops->blitter_clear(next, device, rt_count, fb, rect_count,
2509 clear_rects, draw_rect, flags, colour, depth, stencil);
2512 /* Context activation is done by the caller. */
2513 static DWORD raw_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
2514 struct wined3d_context *context, struct wined3d_surface *src_surface, DWORD src_location,
2515 const RECT *src_rect, struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect,
2516 const struct wined3d_color_key *colour_key, enum wined3d_texture_filter_type filter)
2518 const struct wined3d_gl_info *gl_info = context->gl_info;
2519 unsigned int src_sub_resource_idx, dst_sub_resource_idx;
2520 struct wined3d_texture *src_texture, *dst_texture;
2521 struct wined3d_blitter *next;
2522 GLuint src_name, dst_name;
2523 DWORD location;
2525 src_texture = src_surface->container;
2526 dst_texture = dst_surface->container;
2528 /* If we would need to copy from a renderbuffer or drawable, we'd probably
2529 * be better of using the FBO blitter directly, since we'd need to use it
2530 * to copy the resource contents to the texture anyway. */
2531 if (op != WINED3D_BLIT_OP_RAW_BLIT
2532 || (src_texture->resource.format->id == dst_texture->resource.format->id
2533 && (!(src_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
2534 || !(dst_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB)))))
2536 if (!(next = blitter->next))
2538 ERR("No blitter to handle blit op %#x.\n", op);
2539 return dst_location;
2542 TRACE("Forwarding to blitter %p.\n", next);
2543 return next->ops->blitter_blit(next, op, context, src_surface, src_location,
2544 src_rect, dst_surface, dst_location, dst_rect, colour_key, filter);
2547 TRACE("Blit using ARB_copy_image.\n");
2549 src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
2550 dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
2552 location = src_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB);
2553 if (!location)
2554 location = src_texture->flags & WINED3D_TEXTURE_IS_SRGB
2555 ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2556 if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, location))
2557 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(location));
2558 src_name = wined3d_texture_get_texture_name(src_texture, context, location == WINED3D_LOCATION_TEXTURE_SRGB);
2560 location = dst_location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB);
2561 if (!location)
2562 location = dst_texture->flags & WINED3D_TEXTURE_IS_SRGB
2563 ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2564 if (surface_is_full_rect(dst_surface, dst_rect))
2566 if (!wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, location))
2567 ERR("Failed to prepare the destination sub-resource into %s.\n", wined3d_debug_location(location));
2569 else
2571 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, location))
2572 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(location));
2574 dst_name = wined3d_texture_get_texture_name(dst_texture, context, location == WINED3D_LOCATION_TEXTURE_SRGB);
2576 GL_EXTCALL(glCopyImageSubData(src_name, src_texture->target, src_surface->texture_level,
2577 src_rect->left, src_rect->top, src_surface->texture_layer,
2578 dst_name, dst_texture->target, dst_surface->texture_level,
2579 dst_rect->left, dst_rect->top, dst_surface->texture_layer,
2580 src_rect->right - src_rect->left, src_rect->bottom - src_rect->top, 1));
2581 checkGLcall("copy image data");
2583 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, location);
2584 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~location);
2585 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location))
2586 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(dst_location));
2588 return dst_location | location;
2591 static const struct wined3d_blitter_ops raw_blitter_ops =
2593 raw_blitter_destroy,
2594 raw_blitter_clear,
2595 raw_blitter_blit,
2598 void wined3d_raw_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
2600 struct wined3d_blitter *blitter;
2602 if (!gl_info->supported[ARB_COPY_IMAGE])
2603 return;
2605 if (!(blitter = HeapAlloc(GetProcessHeap(), 0, sizeof(*blitter))))
2606 return;
2608 TRACE("Created blitter %p.\n", blitter);
2610 blitter->ops = &raw_blitter_ops;
2611 blitter->next = *next;
2612 *next = blitter;
2615 /* Context activation is done by the caller. */
2616 static void ffp_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2618 struct wined3d_blitter *next;
2620 if ((next = blitter->next))
2621 next->ops->blitter_destroy(next, context);
2623 HeapFree(GetProcessHeap(), 0, blitter);
2626 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info,
2627 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
2628 DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format, DWORD src_location,
2629 DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format, DWORD dst_location)
2631 BOOL decompress;
2633 decompress = src_format && (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
2634 && !(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED);
2635 if (!decompress && (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM))
2637 TRACE("Source or destination is in system memory.\n");
2638 return FALSE;
2641 if (blit_op == WINED3D_BLIT_OP_RAW_BLIT && dst_format->id == src_format->id)
2643 if (dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
2644 blit_op = WINED3D_BLIT_OP_DEPTH_BLIT;
2645 else
2646 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
2649 switch (blit_op)
2651 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
2652 if (d3d_info->shader_color_key)
2654 TRACE("Color keying requires converted textures.\n");
2655 return FALSE;
2657 case WINED3D_BLIT_OP_COLOR_BLIT:
2658 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
2659 if (!gl_info->supported[WINED3D_GL_LEGACY_CONTEXT])
2660 return FALSE;
2662 if (TRACE_ON(d3d))
2664 TRACE("Checking support for fixup:\n");
2665 dump_color_fixup_desc(src_format->color_fixup);
2668 /* We only support identity conversions. */
2669 if (!is_identity_fixup(src_format->color_fixup)
2670 || !is_identity_fixup(dst_format->color_fixup))
2672 if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER
2673 && dst_format->id == src_format->id && dst_location == WINED3D_LOCATION_DRAWABLE)
2675 WARN("Claiming fixup support because of ORM_BACKBUFFER.\n");
2677 else
2679 TRACE("Fixups are not supported.\n");
2680 return FALSE;
2684 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
2686 TRACE("Can only blit to render targets.\n");
2687 return FALSE;
2689 return TRUE;
2691 default:
2692 TRACE("Unsupported blit operation %#x.\n", blit_op);
2693 return FALSE;
2697 static BOOL ffp_blitter_use_cpu_clear(struct wined3d_rendertarget_view *view)
2699 struct wined3d_resource *resource;
2700 struct wined3d_texture *texture;
2701 DWORD locations;
2703 resource = view->resource;
2704 if (resource->type == WINED3D_RTYPE_BUFFER)
2705 return resource->pool == WINED3D_POOL_SYSTEM_MEM;
2707 texture = texture_from_resource(resource);
2708 locations = texture->sub_resources[view->sub_resource_idx].locations;
2709 if (locations & (resource->map_binding | WINED3D_LOCATION_DISCARDED))
2710 return resource->pool == WINED3D_POOL_SYSTEM_MEM || (texture->flags & WINED3D_TEXTURE_PIN_SYSMEM);
2712 return resource->pool == WINED3D_POOL_SYSTEM_MEM && !(texture->flags & WINED3D_TEXTURE_CONVERTED);
2715 static void ffp_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
2716 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
2717 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
2719 struct wined3d_rendertarget_view *view;
2720 struct wined3d_blitter *next;
2721 DWORD next_flags = 0;
2722 unsigned int i;
2724 if (flags & WINED3DCLEAR_TARGET)
2726 for (i = 0; i < rt_count; ++i)
2728 if (!(view = fb->render_targets[i]))
2729 continue;
2731 if (ffp_blitter_use_cpu_clear(view)
2732 || (!(view->resource->usage & WINED3DUSAGE_RENDERTARGET)
2733 && (wined3d_settings.offscreen_rendering_mode != ORM_FBO
2734 || !(view->format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE))))
2736 next_flags |= WINED3DCLEAR_TARGET;
2737 flags &= ~WINED3DCLEAR_TARGET;
2738 break;
2741 /* FIXME: We should reject colour fills on formats with fixups,
2742 * but this would break P8 colour fills for example. */
2746 if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil)
2747 && (!view->format->depth_size || (flags & WINED3DCLEAR_ZBUFFER))
2748 && (!view->format->stencil_size || (flags & WINED3DCLEAR_STENCIL))
2749 && ffp_blitter_use_cpu_clear(view))
2751 next_flags |= flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL);
2752 flags &= ~(WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL);
2755 if (flags)
2756 device_clear_render_targets(device, rt_count, fb, rect_count,
2757 clear_rects, draw_rect, flags, colour, depth, stencil);
2759 if (next_flags && (next = blitter->next))
2760 next->ops->blitter_clear(next, device, rt_count, fb, rect_count,
2761 clear_rects, draw_rect, next_flags, colour, depth, stencil);
2764 static DWORD ffp_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
2765 struct wined3d_context *context, struct wined3d_surface *src_surface, DWORD src_location,
2766 const RECT *src_rect, struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect,
2767 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter)
2769 struct wined3d_texture *src_texture = src_surface->container;
2770 struct wined3d_texture *dst_texture = dst_surface->container;
2771 const struct wined3d_gl_info *gl_info = context->gl_info;
2772 struct wined3d_resource *src_resource, *dst_resource;
2773 struct wined3d_color_key old_blt_key;
2774 struct wined3d_device *device;
2775 struct wined3d_blitter *next;
2776 DWORD old_color_key_flags;
2777 RECT r;
2779 src_resource = &src_texture->resource;
2780 dst_resource = &dst_texture->resource;
2781 device = dst_resource->device;
2783 if (!ffp_blit_supported(&device->adapter->gl_info, &device->adapter->d3d_info, op,
2784 src_resource->usage, src_resource->pool, src_resource->format, src_location,
2785 dst_resource->usage, dst_resource->pool, dst_resource->format, dst_location))
2787 if ((next = blitter->next))
2788 return next->ops->blitter_blit(next, op, context, src_surface, src_location,
2789 src_rect, dst_surface, dst_location, dst_rect, color_key, filter);
2792 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
2794 old_blt_key = src_texture->async.src_blt_color_key;
2795 old_color_key_flags = src_texture->async.color_key_flags;
2796 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT, color_key);
2798 /* Make sure the surface is up-to-date. This should probably use
2799 * surface_load_location() and worry about the destination surface too,
2800 * unless we're overwriting it completely. */
2801 wined3d_texture_load(src_texture, context, FALSE);
2803 /* Activate the destination context, set it up for blitting. */
2804 context_apply_blit_state(context, device);
2806 if (dst_location == WINED3D_LOCATION_DRAWABLE)
2808 r = *dst_rect;
2809 surface_translate_drawable_coords(dst_surface, context->win_handle, &r);
2810 dst_rect = &r;
2813 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
2815 GLenum buffer;
2817 if (dst_location == WINED3D_LOCATION_DRAWABLE)
2819 TRACE("Destination surface %p is onscreen.\n", dst_surface);
2820 buffer = wined3d_texture_get_gl_buffer(dst_texture);
2822 else
2824 TRACE("Destination surface %p is offscreen.\n", dst_surface);
2825 buffer = GL_COLOR_ATTACHMENT0;
2827 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
2828 context_set_draw_buffer(context, buffer);
2829 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
2830 context_invalidate_state(context, STATE_FRAMEBUFFER);
2833 gl_info->gl_ops.gl.p_glEnable(src_texture->target);
2834 checkGLcall("glEnable(target)");
2836 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST || color_key)
2838 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
2839 checkGLcall("glEnable(GL_ALPHA_TEST)");
2842 if (color_key)
2844 /* For P8 surfaces, the alpha component contains the palette index.
2845 * Which means that the colorkey is one of the palette entries. In
2846 * other cases pixels that should be masked away have alpha set to 0. */
2847 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT)
2848 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
2849 (float)src_texture->async.src_blt_color_key.color_space_low_value / 255.0f);
2850 else
2851 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
2852 checkGLcall("glAlphaFunc");
2855 draw_textured_quad(src_surface, context, src_rect, dst_rect, filter);
2857 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST || color_key)
2859 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2860 checkGLcall("glDisable(GL_ALPHA_TEST)");
2863 /* Leave the OpenGL state valid for blitting. */
2864 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
2865 checkGLcall("glDisable(GL_TEXTURE_2D)");
2866 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
2868 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
2869 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
2871 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
2873 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
2874 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
2877 if (wined3d_settings.strict_draw_ordering
2878 || (dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture))
2879 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2881 /* Restore the color key parameters */
2882 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT,
2883 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
2885 return dst_location;
2888 static const struct wined3d_blitter_ops ffp_blitter_ops =
2890 ffp_blitter_destroy,
2891 ffp_blitter_clear,
2892 ffp_blitter_blit,
2895 void wined3d_ffp_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
2897 struct wined3d_blitter *blitter;
2899 if (!(blitter = HeapAlloc(GetProcessHeap(), 0, sizeof(*blitter))))
2900 return;
2902 TRACE("Created blitter %p.\n", blitter);
2904 blitter->ops = &ffp_blitter_ops;
2905 blitter->next = *next;
2906 *next = blitter;
2909 /* Context activation is done by the caller. */
2910 static void cpu_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2912 struct wined3d_blitter *next;
2914 if ((next = blitter->next))
2915 next->ops->blitter_destroy(next, context);
2917 HeapFree(GetProcessHeap(), 0, blitter);
2920 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
2921 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
2922 const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx)
2924 UINT row_block_count;
2925 const BYTE *src_row;
2926 BYTE *dst_row;
2927 UINT x, y;
2929 src_row = src_data;
2930 dst_row = dst_data;
2932 row_block_count = (update_w + format->block_width - 1) / format->block_width;
2934 if (!flags)
2936 for (y = 0; y < update_h; y += format->block_height)
2938 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
2939 src_row += src_pitch;
2940 dst_row += dst_pitch;
2943 return WINED3D_OK;
2946 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
2948 src_row += (((update_h / format->block_height) - 1) * src_pitch);
2950 switch (format->id)
2952 case WINED3DFMT_DXT1:
2953 for (y = 0; y < update_h; y += format->block_height)
2955 struct block
2957 WORD color[2];
2958 BYTE control_row[4];
2961 const struct block *s = (const struct block *)src_row;
2962 struct block *d = (struct block *)dst_row;
2964 for (x = 0; x < row_block_count; ++x)
2966 d[x].color[0] = s[x].color[0];
2967 d[x].color[1] = s[x].color[1];
2968 d[x].control_row[0] = s[x].control_row[3];
2969 d[x].control_row[1] = s[x].control_row[2];
2970 d[x].control_row[2] = s[x].control_row[1];
2971 d[x].control_row[3] = s[x].control_row[0];
2973 src_row -= src_pitch;
2974 dst_row += dst_pitch;
2976 return WINED3D_OK;
2978 case WINED3DFMT_DXT2:
2979 case WINED3DFMT_DXT3:
2980 for (y = 0; y < update_h; y += format->block_height)
2982 struct block
2984 WORD alpha_row[4];
2985 WORD color[2];
2986 BYTE control_row[4];
2989 const struct block *s = (const struct block *)src_row;
2990 struct block *d = (struct block *)dst_row;
2992 for (x = 0; x < row_block_count; ++x)
2994 d[x].alpha_row[0] = s[x].alpha_row[3];
2995 d[x].alpha_row[1] = s[x].alpha_row[2];
2996 d[x].alpha_row[2] = s[x].alpha_row[1];
2997 d[x].alpha_row[3] = s[x].alpha_row[0];
2998 d[x].color[0] = s[x].color[0];
2999 d[x].color[1] = s[x].color[1];
3000 d[x].control_row[0] = s[x].control_row[3];
3001 d[x].control_row[1] = s[x].control_row[2];
3002 d[x].control_row[2] = s[x].control_row[1];
3003 d[x].control_row[3] = s[x].control_row[0];
3005 src_row -= src_pitch;
3006 dst_row += dst_pitch;
3008 return WINED3D_OK;
3010 default:
3011 FIXME("Compressed flip not implemented for format %s.\n",
3012 debug_d3dformat(format->id));
3013 return E_NOTIMPL;
3017 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
3018 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
3020 return E_NOTIMPL;
3023 static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
3024 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
3025 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
3026 enum wined3d_texture_filter_type filter)
3028 unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count;
3029 struct wined3d_device *device = dst_texture->resource.device;
3030 const struct wined3d_format *src_format, *dst_format;
3031 struct wined3d_texture *converted_texture = NULL;
3032 struct wined3d_bo_address src_data, dst_data;
3033 unsigned int src_fmt_flags, dst_fmt_flags;
3034 struct wined3d_map_desc dst_map, src_map;
3035 struct wined3d_context *context = NULL;
3036 unsigned int x, sx, xinc, y, sy, yinc;
3037 unsigned int texture_level;
3038 HRESULT hr = WINED3D_OK;
3039 BOOL same_sub_resource;
3040 DWORD map_binding;
3041 const BYTE *sbase;
3042 const BYTE *sbuf;
3043 BYTE *dbuf;
3045 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
3046 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
3047 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture,
3048 src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
3050 if (device->d3d_initialized)
3051 context = context_acquire(device, NULL, 0);
3053 if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx)
3055 same_sub_resource = TRUE;
3057 map_binding = dst_texture->resource.map_binding;
3058 texture_level = dst_sub_resource_idx % dst_texture->level_count;
3059 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
3060 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
3061 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
3062 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
3063 wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
3064 dst_map.data = context_map_bo_address(context, &dst_data,
3065 dst_texture->sub_resources[dst_sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, 0);
3067 src_map = dst_map;
3068 src_format = dst_texture->resource.format;
3069 dst_format = src_format;
3070 dst_fmt_flags = dst_texture->resource.format_flags;
3071 src_fmt_flags = dst_fmt_flags;
3073 else
3075 same_sub_resource = FALSE;
3076 dst_format = dst_texture->resource.format;
3077 dst_fmt_flags = dst_texture->resource.format_flags;
3078 if (!(flags & WINED3D_BLT_RAW) && dst_texture->resource.format->id != src_texture->resource.format->id)
3080 if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format)))
3082 FIXME("Cannot convert %s to %s.\n", debug_d3dformat(src_texture->resource.format->id),
3083 debug_d3dformat(dst_texture->resource.format->id));
3084 if (context)
3085 context_release(context);
3086 return WINED3DERR_NOTAVAILABLE;
3088 src_texture = converted_texture;
3089 src_sub_resource_idx = 0;
3091 src_format = src_texture->resource.format;
3092 src_fmt_flags = src_texture->resource.format_flags;
3094 map_binding = src_texture->resource.map_binding;
3095 texture_level = src_sub_resource_idx % src_texture->level_count;
3096 if (!wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, map_binding))
3097 ERR("Failed to load the source sub-resource into %s.\n", wined3d_debug_location(map_binding));
3098 wined3d_texture_get_pitch(src_texture, texture_level, &src_map.row_pitch, &src_map.slice_pitch);
3099 wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &src_data, map_binding);
3100 src_map.data = context_map_bo_address(context, &src_data,
3101 src_texture->sub_resources[src_sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, 0);
3103 map_binding = dst_texture->resource.map_binding;
3104 texture_level = dst_sub_resource_idx % dst_texture->level_count;
3105 if (!wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, map_binding))
3106 ERR("Failed to load the destination sub-resource into %s.\n", wined3d_debug_location(map_binding));
3107 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~map_binding);
3108 wined3d_texture_get_pitch(dst_texture, texture_level, &dst_map.row_pitch, &dst_map.slice_pitch);
3109 wined3d_texture_get_memory(dst_texture, dst_sub_resource_idx, &dst_data, map_binding);
3110 dst_map.data = context_map_bo_address(context, &dst_data,
3111 dst_texture->sub_resources[dst_sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, 0);
3113 flags &= ~WINED3D_BLT_RAW;
3115 bpp = dst_format->byte_count;
3116 src_height = src_box->bottom - src_box->top;
3117 src_width = src_box->right - src_box->left;
3118 dst_height = dst_box->bottom - dst_box->top;
3119 dst_width = dst_box->right - dst_box->left;
3120 row_byte_count = dst_width * bpp;
3122 sbase = (BYTE *)src_map.data
3123 + ((src_box->top / src_format->block_height) * src_map.row_pitch)
3124 + ((src_box->left / src_format->block_width) * src_format->block_byte_count);
3125 dbuf = (BYTE *)dst_map.data
3126 + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch)
3127 + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count);
3129 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
3131 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
3133 if (same_sub_resource)
3135 FIXME("Only plain blits supported on compressed surfaces.\n");
3136 hr = E_NOTIMPL;
3137 goto release;
3140 if (src_height != dst_height || src_width != dst_width)
3142 WARN("Stretching not supported on compressed surfaces.\n");
3143 hr = WINED3DERR_INVALIDCALL;
3144 goto release;
3147 hr = surface_cpu_blt_compressed(sbase, dbuf,
3148 src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height,
3149 src_format, flags, fx);
3150 goto release;
3153 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
3154 && (src_width != dst_width || src_height != dst_height))
3156 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
3157 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
3160 xinc = (src_width << 16) / dst_width;
3161 yinc = (src_height << 16) / dst_height;
3163 if (!flags)
3165 /* No effects, we can cheat here. */
3166 if (dst_width == src_width)
3168 if (dst_height == src_height)
3170 /* No stretching in either direction. This needs to be as fast
3171 * as possible. */
3172 sbuf = sbase;
3174 /* Check for overlapping surfaces. */
3175 if (!same_sub_resource || dst_box->top < src_box->top
3176 || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
3178 /* No overlap, or dst above src, so copy from top downwards. */
3179 for (y = 0; y < dst_height; ++y)
3181 memcpy(dbuf, sbuf, row_byte_count);
3182 sbuf += src_map.row_pitch;
3183 dbuf += dst_map.row_pitch;
3186 else if (dst_box->top > src_box->top)
3188 /* Copy from bottom upwards. */
3189 sbuf += src_map.row_pitch * dst_height;
3190 dbuf += dst_map.row_pitch * dst_height;
3191 for (y = 0; y < dst_height; ++y)
3193 sbuf -= src_map.row_pitch;
3194 dbuf -= dst_map.row_pitch;
3195 memcpy(dbuf, sbuf, row_byte_count);
3198 else
3200 /* Src and dst overlapping on the same line, use memmove. */
3201 for (y = 0; y < dst_height; ++y)
3203 memmove(dbuf, sbuf, row_byte_count);
3204 sbuf += src_map.row_pitch;
3205 dbuf += dst_map.row_pitch;
3209 else
3211 /* Stretching in y direction only. */
3212 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3214 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3215 memcpy(dbuf, sbuf, row_byte_count);
3216 dbuf += dst_map.row_pitch;
3220 else
3222 /* Stretching in X direction. */
3223 unsigned int last_sy = ~0u;
3224 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3226 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3228 if ((sy >> 16) == (last_sy >> 16))
3230 /* This source row is the same as last source row -
3231 * Copy the already stretched row. */
3232 memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count);
3234 else
3236 #define STRETCH_ROW(type) \
3237 do { \
3238 const type *s = (const type *)sbuf; \
3239 type *d = (type *)dbuf; \
3240 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3241 d[x] = s[sx >> 16]; \
3242 } while(0)
3244 switch(bpp)
3246 case 1:
3247 STRETCH_ROW(BYTE);
3248 break;
3249 case 2:
3250 STRETCH_ROW(WORD);
3251 break;
3252 case 4:
3253 STRETCH_ROW(DWORD);
3254 break;
3255 case 3:
3257 const BYTE *s;
3258 BYTE *d = dbuf;
3259 for (x = sx = 0; x < dst_width; x++, sx+= xinc)
3261 DWORD pixel;
3263 s = sbuf + 3 * (sx >> 16);
3264 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3265 d[0] = (pixel ) & 0xff;
3266 d[1] = (pixel >> 8) & 0xff;
3267 d[2] = (pixel >> 16) & 0xff;
3268 d += 3;
3270 break;
3272 default:
3273 FIXME("Stretched blit not implemented for bpp %u.\n", bpp * 8);
3274 hr = WINED3DERR_NOTAVAILABLE;
3275 goto error;
3277 #undef STRETCH_ROW
3279 dbuf += dst_map.row_pitch;
3280 last_sy = sy;
3284 else
3286 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
3287 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
3288 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
3289 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3290 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
3292 /* The color keying flags are checked for correctness in ddraw. */
3293 if (flags & WINED3D_BLT_SRC_CKEY)
3295 keylow = src_texture->async.src_blt_color_key.color_space_low_value;
3296 keyhigh = src_texture->async.src_blt_color_key.color_space_high_value;
3298 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3300 keylow = fx->src_color_key.color_space_low_value;
3301 keyhigh = fx->src_color_key.color_space_high_value;
3304 if (flags & WINED3D_BLT_DST_CKEY)
3306 /* Destination color keys are taken from the source surface! */
3307 destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value;
3308 destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value;
3310 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
3312 destkeylow = fx->dst_color_key.color_space_low_value;
3313 destkeyhigh = fx->dst_color_key.color_space_high_value;
3316 if (bpp == 1)
3318 keymask = 0xff;
3320 else
3322 DWORD masks[3];
3323 get_color_masks(src_format, masks);
3324 keymask = masks[0] | masks[1] | masks[2];
3326 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3327 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
3330 if (flags & WINED3D_BLT_FX)
3332 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
3333 LONG tmpxy;
3334 dTopLeft = dbuf;
3335 dTopRight = dbuf + ((dst_width - 1) * bpp);
3336 dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
3337 dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
3339 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
3341 /* I don't think we need to do anything about this flag. */
3342 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
3344 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
3346 tmp = dTopRight;
3347 dTopRight = dTopLeft;
3348 dTopLeft = tmp;
3349 tmp = dBottomRight;
3350 dBottomRight = dBottomLeft;
3351 dBottomLeft = tmp;
3352 dstxinc = dstxinc * -1;
3354 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
3356 tmp = dTopLeft;
3357 dTopLeft = dBottomLeft;
3358 dBottomLeft = tmp;
3359 tmp = dTopRight;
3360 dTopRight = dBottomRight;
3361 dBottomRight = tmp;
3362 dstyinc = dstyinc * -1;
3364 if (fx->fx & WINEDDBLTFX_NOTEARING)
3366 /* I don't think we need to do anything about this flag. */
3367 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
3369 if (fx->fx & WINEDDBLTFX_ROTATE180)
3371 tmp = dBottomRight;
3372 dBottomRight = dTopLeft;
3373 dTopLeft = tmp;
3374 tmp = dBottomLeft;
3375 dBottomLeft = dTopRight;
3376 dTopRight = tmp;
3377 dstxinc = dstxinc * -1;
3378 dstyinc = dstyinc * -1;
3380 if (fx->fx & WINEDDBLTFX_ROTATE270)
3382 tmp = dTopLeft;
3383 dTopLeft = dBottomLeft;
3384 dBottomLeft = dBottomRight;
3385 dBottomRight = dTopRight;
3386 dTopRight = tmp;
3387 tmpxy = dstxinc;
3388 dstxinc = dstyinc;
3389 dstyinc = tmpxy;
3390 dstxinc = dstxinc * -1;
3392 if (fx->fx & WINEDDBLTFX_ROTATE90)
3394 tmp = dTopLeft;
3395 dTopLeft = dTopRight;
3396 dTopRight = dBottomRight;
3397 dBottomRight = dBottomLeft;
3398 dBottomLeft = tmp;
3399 tmpxy = dstxinc;
3400 dstxinc = dstyinc;
3401 dstyinc = tmpxy;
3402 dstyinc = dstyinc * -1;
3404 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
3406 /* I don't think we need to do anything about this flag. */
3407 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
3409 dbuf = dTopLeft;
3410 flags &= ~(WINED3D_BLT_FX);
3413 #define COPY_COLORKEY_FX(type) \
3414 do { \
3415 const type *s; \
3416 type *d = (type *)dbuf, *dx, tmp; \
3417 for (y = sy = 0; y < dst_height; ++y, sy += yinc) \
3419 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
3420 dx = d; \
3421 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3423 tmp = s[sx >> 16]; \
3424 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
3425 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
3427 dx[0] = tmp; \
3429 dx = (type *)(((BYTE *)dx) + dstxinc); \
3431 d = (type *)(((BYTE *)d) + dstyinc); \
3433 } while(0)
3435 switch (bpp)
3437 case 1:
3438 COPY_COLORKEY_FX(BYTE);
3439 break;
3440 case 2:
3441 COPY_COLORKEY_FX(WORD);
3442 break;
3443 case 4:
3444 COPY_COLORKEY_FX(DWORD);
3445 break;
3446 case 3:
3448 const BYTE *s;
3449 BYTE *d = dbuf, *dx;
3450 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3452 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3453 dx = d;
3454 for (x = sx = 0; x < dst_width; ++x, sx+= xinc)
3456 DWORD pixel, dpixel = 0;
3457 s = sbuf + 3 * (sx>>16);
3458 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3459 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
3460 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
3461 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
3463 dx[0] = (pixel ) & 0xff;
3464 dx[1] = (pixel >> 8) & 0xff;
3465 dx[2] = (pixel >> 16) & 0xff;
3467 dx += dstxinc;
3469 d += dstyinc;
3471 break;
3473 default:
3474 FIXME("%s color-keyed blit not implemented for bpp %u.\n",
3475 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
3476 hr = WINED3DERR_NOTAVAILABLE;
3477 goto error;
3478 #undef COPY_COLORKEY_FX
3482 error:
3483 if (flags)
3484 FIXME(" Unsupported flags %#x.\n", flags);
3486 release:
3487 context_unmap_bo_address(context, &dst_data, GL_PIXEL_UNPACK_BUFFER);
3488 if (!same_sub_resource)
3489 context_unmap_bo_address(context, &src_data, GL_PIXEL_UNPACK_BUFFER);
3490 if (SUCCEEDED(hr) && dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture)
3492 SetRect(&dst_texture->swapchain->front_buffer_update,
3493 dst_box->left, dst_box->top, dst_box->right, dst_box->bottom);
3494 dst_texture->swapchain->swapchain_ops->swapchain_frontbuffer_updated(dst_texture->swapchain);
3496 if (converted_texture)
3497 wined3d_texture_decref(converted_texture);
3498 if (context)
3499 context_release(context);
3501 return hr;
3504 static void surface_cpu_blt_colour_fill(struct wined3d_rendertarget_view *view,
3505 const struct wined3d_box *box, const struct wined3d_color *colour)
3507 struct wined3d_device *device = view->resource->device;
3508 struct wined3d_context *context = NULL;
3509 struct wined3d_texture *texture;
3510 struct wined3d_bo_address data;
3511 unsigned int x, y, w, h, bpp;
3512 struct wined3d_map_desc map;
3513 DWORD map_binding;
3514 BYTE *row;
3515 DWORD c;
3517 TRACE("view %p, box %s, colour %s.\n", view, debug_box(box), debug_color(colour));
3519 if (view->format_flags & WINED3DFMT_FLAG_BLOCKS)
3521 FIXME("Not implemented for format %s.\n", debug_d3dformat(view->format->id));
3522 return;
3525 if (view->format->id != view->resource->format->id)
3526 FIXME("View format %s doesn't match resource format %s.\n",
3527 debug_d3dformat(view->format->id), debug_d3dformat(view->resource->format->id));
3529 if (view->resource->type == WINED3D_RTYPE_BUFFER)
3531 FIXME("Not implemented for buffers.\n");
3532 return;
3535 if (device->d3d_initialized)
3536 context = context_acquire(device, NULL, 0);
3538 c = wined3d_format_convert_from_float(view->format, colour);
3539 bpp = view->format->byte_count;
3540 w = box->right - box->left;
3541 h = box->bottom - box->top;
3543 texture = texture_from_resource(view->resource);
3544 map_binding = texture->resource.map_binding;
3545 if (!wined3d_texture_load_location(texture, view->sub_resource_idx, context, map_binding))
3546 ERR("Failed to load the sub-resource into %s.\n", wined3d_debug_location(map_binding));
3547 wined3d_texture_invalidate_location(texture, view->sub_resource_idx, ~map_binding);
3548 wined3d_texture_get_pitch(texture, view->sub_resource_idx % texture->level_count,
3549 &map.row_pitch, &map.slice_pitch);
3550 wined3d_texture_get_memory(texture, view->sub_resource_idx, &data, map_binding);
3551 map.data = context_map_bo_address(context, &data,
3552 texture->sub_resources[view->sub_resource_idx].size, GL_PIXEL_UNPACK_BUFFER, 0);
3553 map.data = (BYTE *)map.data
3554 + (box->front * map.slice_pitch)
3555 + ((box->top / view->format->block_height) * map.row_pitch)
3556 + ((box->left / view->format->block_width) * view->format->block_byte_count);
3558 switch (bpp)
3560 case 1:
3561 for (x = 0; x < w; ++x)
3563 ((BYTE *)map.data)[x] = c;
3565 break;
3567 case 2:
3568 for (x = 0; x < w; ++x)
3570 ((WORD *)map.data)[x] = c;
3572 break;
3574 case 3:
3576 row = map.data;
3577 for (x = 0; x < w; ++x, row += 3)
3579 row[0] = (c ) & 0xff;
3580 row[1] = (c >> 8) & 0xff;
3581 row[2] = (c >> 16) & 0xff;
3583 break;
3585 case 4:
3586 for (x = 0; x < w; ++x)
3588 ((DWORD *)map.data)[x] = c;
3590 break;
3592 default:
3593 FIXME("Not implemented for bpp %u.\n", bpp);
3594 wined3d_resource_unmap(view->resource, view->sub_resource_idx);
3595 return;
3598 row = map.data;
3599 for (y = 1; y < h; ++y)
3601 row += map.row_pitch;
3602 memcpy(row, map.data, w * bpp);
3605 context_unmap_bo_address(context, &data, GL_PIXEL_UNPACK_BUFFER);
3606 if (context)
3607 context_release(context);
3610 static void cpu_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
3611 unsigned int rt_count, const struct wined3d_fb_state *fb, unsigned int rect_count, const RECT *clear_rects,
3612 const RECT *draw_rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
3614 struct wined3d_color c = {depth, 0.0f, 0.0f, 0.0f};
3615 struct wined3d_rendertarget_view *view;
3616 struct wined3d_box box;
3617 unsigned int i, j;
3619 if (!rect_count)
3621 rect_count = 1;
3622 clear_rects = draw_rect;
3625 for (i = 0; i < rect_count; ++i)
3627 box.left = max(clear_rects[i].left, draw_rect->left);
3628 box.top = max(clear_rects[i].top, draw_rect->top);
3629 box.right = min(clear_rects[i].right, draw_rect->right);
3630 box.bottom = min(clear_rects[i].bottom, draw_rect->bottom);
3631 box.front = 0;
3632 box.back = 1;
3634 if (box.left >= box.right || box.top >= box.bottom)
3635 continue;
3637 if (flags & WINED3DCLEAR_TARGET)
3639 for (j = 0; j < rt_count; ++j)
3641 if ((view = fb->render_targets[j]))
3642 surface_cpu_blt_colour_fill(view, &box, colour);
3646 if ((flags & (WINED3DCLEAR_ZBUFFER | WINED3DCLEAR_STENCIL)) && (view = fb->depth_stencil))
3648 if ((view->format->depth_size && !(flags & WINED3DCLEAR_ZBUFFER))
3649 || (view->format->stencil_size && !(flags & WINED3DCLEAR_STENCIL)))
3650 FIXME("Clearing %#x on %s.\n", flags, debug_d3dformat(view->format->id));
3652 surface_cpu_blt_colour_fill(view, &box, &c);
3657 static DWORD cpu_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
3658 struct wined3d_context *context, struct wined3d_surface *src_surface, DWORD src_location,
3659 const RECT *src_rect, struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect,
3660 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter)
3662 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
3663 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
3664 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
3665 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
3666 struct wined3d_texture *dst_texture = dst_surface->container;
3667 struct wined3d_texture *src_texture = src_surface->container;
3668 struct wined3d_blt_fx fx;
3669 DWORD flags = 0;
3671 memset(&fx, 0, sizeof(fx));
3672 switch (op)
3674 case WINED3D_BLIT_OP_COLOR_BLIT:
3675 case WINED3D_BLIT_OP_DEPTH_BLIT:
3676 case WINED3D_BLIT_OP_RAW_BLIT:
3677 break;
3678 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
3679 flags |= WINED3D_BLT_ALPHA_TEST;
3680 break;
3681 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
3682 flags |= WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_FX;
3683 fx.src_color_key = *color_key;
3684 break;
3685 default:
3686 FIXME("Unhandled op %#x.\n", op);
3687 break;
3690 if (FAILED(surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
3691 src_texture, src_sub_resource_idx, &src_box, flags, &fx, filter)))
3692 ERR("Failed to blit.\n");
3693 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
3695 return dst_location | (dst_texture->sub_resources[dst_sub_resource_idx].locations
3696 & dst_texture->resource.map_binding);
3699 static const struct wined3d_blitter_ops cpu_blitter_ops =
3701 cpu_blitter_destroy,
3702 cpu_blitter_clear,
3703 cpu_blitter_blit,
3706 struct wined3d_blitter *wined3d_cpu_blitter_create(void)
3708 struct wined3d_blitter *blitter;
3710 if (!(blitter = HeapAlloc(GetProcessHeap(), 0, sizeof(*blitter))))
3711 return NULL;
3713 TRACE("Created blitter %p.\n", blitter);
3715 blitter->ops = &cpu_blitter_ops;
3716 blitter->next = NULL;
3718 return blitter;
3721 HRESULT wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3722 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
3723 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
3725 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
3726 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
3727 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
3728 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
3729 struct wined3d_texture_sub_resource *src_sub_resource, *dst_sub_resource;
3730 struct wined3d_texture *dst_texture = dst_surface->container;
3731 struct wined3d_texture *src_texture = src_surface->container;
3732 struct wined3d_device *device = dst_texture->resource.device;
3733 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3734 const struct wined3d_color_key *colour_key = NULL;
3735 DWORD dst_location, valid_locations;
3736 DWORD src_ds_flags, dst_ds_flags;
3737 struct wined3d_context *context;
3738 enum wined3d_blit_op blit_op;
3739 BOOL scale, convert;
3741 static const DWORD simple_blit = WINED3D_BLT_SRC_CKEY
3742 | WINED3D_BLT_SRC_CKEY_OVERRIDE
3743 | WINED3D_BLT_ALPHA_TEST
3744 | WINED3D_BLT_RAW;
3746 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
3747 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3748 flags, fx, debug_d3dtexturefiltertype(filter));
3749 TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage));
3751 if (fx)
3753 TRACE("fx %#x.\n", fx->fx);
3754 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
3755 fx->dst_color_key.color_space_low_value,
3756 fx->dst_color_key.color_space_high_value);
3757 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
3758 fx->src_color_key.color_space_low_value,
3759 fx->src_color_key.color_space_high_value);
3762 if (!fx || !(fx->fx))
3763 flags &= ~WINED3D_BLT_FX;
3765 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
3766 if (flags & WINED3D_BLT_DO_NOT_WAIT)
3768 static unsigned int once;
3770 if (!once++)
3771 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
3774 flags &= ~(WINED3D_BLT_SYNCHRONOUS | WINED3D_BLT_DO_NOT_WAIT | WINED3D_BLT_WAIT);
3776 if (!device->d3d_initialized)
3778 WARN("D3D not initialized, using fallback.\n");
3779 goto cpu;
3782 if (flags & ~simple_blit)
3784 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
3785 goto fallback;
3788 src_swapchain = src_texture->swapchain;
3789 dst_swapchain = dst_texture->swapchain;
3791 /* This isn't strictly needed. FBO blits for example could deal with
3792 * cross-swapchain blits by first downloading the source to a texture
3793 * before switching to the destination context. We just have this here to
3794 * not have to deal with the issue, since cross-swapchain blits should be
3795 * rare. */
3796 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
3798 FIXME("Using fallback for cross-swapchain blit.\n");
3799 goto fallback;
3802 scale = src_rect->right - src_rect->left != dst_rect->right - dst_rect->left
3803 || src_rect->bottom - src_rect->top != dst_rect->bottom - dst_rect->top;
3804 convert = src_texture->resource.format->id != dst_texture->resource.format->id;
3806 dst_ds_flags = dst_texture->resource.format_flags
3807 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
3808 src_ds_flags = src_texture->resource.format_flags
3809 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
3811 if (src_ds_flags || dst_ds_flags)
3813 TRACE("Depth/stencil blit.\n");
3815 if (dst_texture->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3816 dst_location = dst_texture->resource.map_binding;
3817 else
3818 dst_location = dst_texture->resource.draw_binding;
3820 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3821 valid_locations = device->blitter->ops->blitter_blit(device->blitter,
3822 WINED3D_BLIT_OP_DEPTH_BLIT, context,
3823 src_surface, src_texture->resource.draw_binding, src_rect,
3824 dst_surface, dst_location, dst_rect, NULL, filter);
3825 context_release(context);
3827 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
3828 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
3830 return WINED3D_OK;
3833 TRACE("Colour blit.\n");
3835 dst_sub_resource = &dst_texture->sub_resources[dst_sub_resource_idx];
3836 src_sub_resource = &src_texture->sub_resources[src_sub_resource_idx];
3838 /* In principle this would apply to depth blits as well, but we don't
3839 * implement those in the CPU blitter at the moment. */
3840 if ((dst_sub_resource->locations & dst_texture->resource.map_binding)
3841 && (src_sub_resource->locations & src_texture->resource.map_binding))
3843 if (scale)
3844 TRACE("Not doing sysmem blit because of scaling.\n");
3845 else if (convert)
3846 TRACE("Not doing sysmem blit because of format conversion.\n");
3847 else
3848 goto cpu;
3851 blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
3852 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3854 colour_key = &fx->src_color_key;
3855 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
3857 else if (flags & WINED3D_BLT_SRC_CKEY)
3859 colour_key = &src_texture->async.src_blt_color_key;
3860 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
3862 else if (flags & WINED3D_BLT_ALPHA_TEST)
3864 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
3866 else if ((src_sub_resource->locations & surface_simple_locations)
3867 && !(dst_sub_resource->locations & surface_simple_locations))
3869 /* Upload */
3870 if (scale)
3871 TRACE("Not doing upload because of scaling.\n");
3872 else if (convert)
3873 TRACE("Not doing upload because of format conversion.\n");
3874 else if (dst_texture->resource.format->convert)
3875 TRACE("Not doing upload because the destination format needs conversion.\n");
3876 else
3878 POINT dst_point = {dst_rect->left, dst_rect->top};
3880 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, src_rect)))
3882 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
3884 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3885 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx,
3886 context, dst_texture->resource.draw_binding);
3887 context_release(context);
3889 return WINED3D_OK;
3893 else if (dst_swapchain && dst_swapchain->back_buffers
3894 && dst_texture == dst_swapchain->front_buffer
3895 && src_texture == dst_swapchain->back_buffers[0])
3897 /* Use present for back -> front blits. The idea behind this is that
3898 * present is potentially faster than a blit, in particular when FBO
3899 * blits aren't available. Some ddraw applications like Half-Life and
3900 * Prince of Persia 3D use Blt() from the backbuffer to the
3901 * frontbuffer instead of doing a Flip(). D3d8 and d3d9 applications
3902 * can't blit directly to the frontbuffer. */
3903 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
3905 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
3907 /* Set the swap effect to COPY, we don't want the backbuffer to become
3908 * undefined. */
3909 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
3910 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, 0);
3911 dst_swapchain->desc.swap_effect = swap_effect;
3913 return WINED3D_OK;
3915 else if ((flags & WINED3D_BLT_RAW) || (!scale && !convert))
3917 blit_op = WINED3D_BLIT_OP_RAW_BLIT;
3920 if (dst_texture->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3921 dst_location = dst_texture->resource.map_binding;
3922 else
3923 dst_location = dst_texture->resource.draw_binding;
3925 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3926 valid_locations = device->blitter->ops->blitter_blit(device->blitter, blit_op, context,
3927 src_surface, src_texture->resource.draw_binding, src_rect,
3928 dst_surface, dst_location, dst_rect, colour_key, filter);
3929 context_release(context);
3931 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, valid_locations);
3932 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~valid_locations);
3934 return WINED3D_OK;
3936 fallback:
3937 /* Special cases for render targets. */
3938 if (SUCCEEDED(surface_blt_special(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter)))
3939 return WINED3D_OK;
3941 cpu:
3942 return surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
3943 src_texture, src_sub_resource_idx, &src_box, flags, fx, filter);