wined3d: Merge wined3d_blitter_ops.color_fill() and wined3d_blitter_ops.depth_fill().
[wine.git] / dlls / wined3d / surface.c
blob237b6047acf1381baef36a8007e9591ce9318ff2
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 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
180 if (rect_in)
181 *rect_out = *rect_in;
182 else
184 const struct wined3d_texture *texture = surface->container;
186 SetRect(rect_out, 0, 0, wined3d_texture_get_level_width(texture, surface->texture_level),
187 wined3d_texture_get_level_height(texture, surface->texture_level));
191 /* Context activation is done by the caller. */
192 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
193 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
195 const struct wined3d_gl_info *gl_info = context->gl_info;
196 struct wined3d_texture *texture = src_surface->container;
197 struct blt_info info;
199 surface_get_blt_info(src_surface->texture_target, src_rect,
200 wined3d_texture_get_level_pow2_width(texture, src_surface->texture_level),
201 wined3d_texture_get_level_pow2_height(texture, src_surface->texture_level), &info);
203 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
204 checkGLcall("glEnable(bind_target)");
206 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
208 /* Filtering for StretchRect */
209 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
210 checkGLcall("glTexParameteri");
211 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
212 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
213 checkGLcall("glTexParameteri");
214 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
215 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
216 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
217 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
218 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
219 checkGLcall("glTexEnvi");
221 /* Draw a quad */
222 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
223 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[0].x);
224 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
226 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[1].x);
227 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
229 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[2].x);
230 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
232 gl_info->gl_ops.gl.p_glTexCoord3fv(&info.texcoords[3].x);
233 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
234 gl_info->gl_ops.gl.p_glEnd();
236 /* Unbind the texture */
237 context_bind_texture(context, info.bind_target, 0);
239 /* We changed the filtering settings on the texture. Inform the
240 * container about this to get the filters reset properly next draw. */
241 texture->texture_rgb.sampler_desc.mag_filter = WINED3D_TEXF_POINT;
242 texture->texture_rgb.sampler_desc.min_filter = WINED3D_TEXF_POINT;
243 texture->texture_rgb.sampler_desc.mip_filter = WINED3D_TEXF_NONE;
244 texture->texture_rgb.sampler_desc.srgb_decode = FALSE;
247 /* Works correctly only for <= 4 bpp formats. */
248 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
250 masks[0] = ((1u << format->red_size) - 1) << format->red_offset;
251 masks[1] = ((1u << format->green_size) - 1) << format->green_offset;
252 masks[2] = ((1u << format->blue_size) - 1) << format->blue_offset;
255 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
257 unsigned int t;
259 t = wined3d_texture_get_level_width(surface->container, surface->texture_level);
260 if ((r->left && r->right) || abs(r->right - r->left) != t)
261 return FALSE;
262 t = wined3d_texture_get_level_height(surface->container, surface->texture_level);
263 if ((r->top && r->bottom) || abs(r->bottom - r->top) != t)
264 return FALSE;
265 return TRUE;
268 static void surface_depth_blt_fbo(const struct wined3d_device *device,
269 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
270 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
272 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
273 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
274 struct wined3d_texture *dst_texture = dst_surface->container;
275 struct wined3d_texture *src_texture = src_surface->container;
276 const struct wined3d_gl_info *gl_info;
277 struct wined3d_context *context;
278 DWORD src_mask, dst_mask;
279 GLbitfield gl_mask;
281 TRACE("device %p\n", device);
282 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
283 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
284 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
285 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
287 src_mask = src_texture->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
288 dst_mask = dst_texture->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
290 if (src_mask != dst_mask)
292 ERR("Incompatible formats %s and %s.\n",
293 debug_d3dformat(src_texture->resource.format->id),
294 debug_d3dformat(dst_texture->resource.format->id));
295 return;
298 if (!src_mask)
300 ERR("Not a depth / stencil format: %s.\n",
301 debug_d3dformat(src_texture->resource.format->id));
302 return;
305 gl_mask = 0;
306 if (src_mask & WINED3DFMT_FLAG_DEPTH)
307 gl_mask |= GL_DEPTH_BUFFER_BIT;
308 if (src_mask & WINED3DFMT_FLAG_STENCIL)
309 gl_mask |= GL_STENCIL_BUFFER_BIT;
311 context = context_acquire(device, NULL, 0);
312 if (!context->valid)
314 context_release(context);
315 WARN("Invalid context, skipping blit.\n");
316 return;
319 /* Make sure the locations are up-to-date. Loading the destination
320 * surface isn't required if the entire surface is overwritten. */
321 wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, src_location);
322 if (!surface_is_full_rect(dst_surface, dst_rect))
323 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
324 else
325 wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, dst_location);
327 gl_info = context->gl_info;
329 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
330 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
332 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
333 context_set_draw_buffer(context, GL_NONE);
334 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
335 context_invalidate_state(context, STATE_FRAMEBUFFER);
337 if (gl_mask & GL_DEPTH_BUFFER_BIT)
339 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
340 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
342 if (gl_mask & GL_STENCIL_BUFFER_BIT)
344 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
346 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
347 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
349 gl_info->gl_ops.gl.p_glStencilMask(~0U);
350 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
353 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
354 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
356 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
357 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
358 checkGLcall("glBlitFramebuffer()");
360 if (wined3d_settings.strict_draw_ordering)
361 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
363 context_release(context);
366 /* Blit between surface locations. Onscreen on different swapchains is not supported.
367 * Depth / stencil is not supported. Context activation is done by the caller. */
368 static void surface_blt_fbo(const struct wined3d_device *device,
369 struct wined3d_context *old_ctx, enum wined3d_texture_filter_type filter,
370 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
371 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
373 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
374 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
375 struct wined3d_texture *dst_texture = dst_surface->container;
376 struct wined3d_texture *src_texture = src_surface->container;
377 const struct wined3d_gl_info *gl_info;
378 struct wined3d_context *context = old_ctx;
379 struct wined3d_surface *required_rt, *restore_rt = NULL;
380 RECT src_rect, dst_rect;
381 GLenum gl_filter;
382 GLenum buffer;
384 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
385 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
386 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
387 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
388 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
390 src_rect = *src_rect_in;
391 dst_rect = *dst_rect_in;
393 switch (filter)
395 case WINED3D_TEXF_LINEAR:
396 gl_filter = GL_LINEAR;
397 break;
399 default:
400 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
401 case WINED3D_TEXF_NONE:
402 case WINED3D_TEXF_POINT:
403 gl_filter = GL_NEAREST;
404 break;
407 /* Resolve the source surface first if needed. */
408 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
409 && (src_texture->resource.format->id != dst_texture->resource.format->id
410 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
411 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
412 src_location = WINED3D_LOCATION_RB_RESOLVED;
414 /* Make sure the locations are up-to-date. Loading the destination
415 * surface isn't required if the entire surface is overwritten. (And is
416 * in fact harmful if we're being called by surface_load_location() with
417 * the purpose of loading the destination surface.) */
418 wined3d_texture_load_location(src_texture, src_sub_resource_idx, old_ctx, src_location);
419 if (!surface_is_full_rect(dst_surface, &dst_rect))
420 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, old_ctx, dst_location);
421 else
422 wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, old_ctx, dst_location);
425 if (src_location == WINED3D_LOCATION_DRAWABLE) required_rt = src_surface;
426 else if (dst_location == WINED3D_LOCATION_DRAWABLE) required_rt = dst_surface;
427 else required_rt = NULL;
429 restore_rt = context_get_rt_surface(old_ctx);
430 if (restore_rt != required_rt)
431 context = context_acquire(device, required_rt ? required_rt->container : NULL,
432 required_rt ? surface_get_sub_resource_idx(required_rt) : 0);
433 else
434 restore_rt = NULL;
436 if (!context->valid)
438 context_release(context);
439 WARN("Invalid context, skipping blit.\n");
440 return;
443 gl_info = context->gl_info;
445 if (src_location == WINED3D_LOCATION_DRAWABLE)
447 TRACE("Source surface %p is onscreen.\n", src_surface);
448 buffer = wined3d_texture_get_gl_buffer(src_texture);
449 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
451 else
453 TRACE("Source surface %p is offscreen.\n", src_surface);
454 buffer = GL_COLOR_ATTACHMENT0;
457 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
458 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
459 checkGLcall("glReadBuffer()");
460 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
462 if (dst_location == WINED3D_LOCATION_DRAWABLE)
464 TRACE("Destination surface %p is onscreen.\n", dst_surface);
465 buffer = wined3d_texture_get_gl_buffer(dst_texture);
466 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
468 else
470 TRACE("Destination surface %p is offscreen.\n", dst_surface);
471 buffer = GL_COLOR_ATTACHMENT0;
474 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
475 context_set_draw_buffer(context, buffer);
476 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
477 context_invalidate_state(context, STATE_FRAMEBUFFER);
479 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
480 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
481 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
482 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
483 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
485 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
486 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
488 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
489 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
490 checkGLcall("glBlitFramebuffer()");
492 if (wined3d_settings.strict_draw_ordering || (dst_location == WINED3D_LOCATION_DRAWABLE
493 && dst_texture->swapchain->front_buffer == dst_texture))
494 gl_info->gl_ops.gl.p_glFlush();
496 if (restore_rt)
497 context_restore(context, restore_rt);
500 static BOOL fbo_blitter_supported(const struct wined3d_gl_info *gl_info,
501 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
502 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
503 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
505 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
506 return FALSE;
508 /* Source and/or destination need to be on the GL side */
509 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
510 return FALSE;
512 switch (blit_op)
514 case WINED3D_BLIT_OP_COLOR_BLIT:
515 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
516 || (src_usage & WINED3DUSAGE_RENDERTARGET)))
517 return FALSE;
518 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
519 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
520 return FALSE;
521 if (!(src_format->id == dst_format->id
522 || (is_identity_fixup(src_format->color_fixup)
523 && is_identity_fixup(dst_format->color_fixup))))
524 return FALSE;
525 break;
527 case WINED3D_BLIT_OP_DEPTH_BLIT:
528 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
529 return FALSE;
530 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
531 return FALSE;
532 /* Accept pure swizzle fixups for depth formats. In general we
533 * ignore the stencil component (if present) at the moment and the
534 * swizzle is not relevant with just the depth component. */
535 if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup)
536 || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup))
537 return FALSE;
538 break;
540 default:
541 return FALSE;
544 return TRUE;
547 /* This call just downloads data, the caller is responsible for binding the
548 * correct texture. */
549 /* Context activation is done by the caller. */
550 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
551 DWORD dst_location)
553 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
554 struct wined3d_texture *texture = surface->container;
555 const struct wined3d_format *format = texture->resource.format;
556 struct wined3d_texture_sub_resource *sub_resource;
557 unsigned int dst_row_pitch, dst_slice_pitch;
558 unsigned int src_row_pitch, src_slice_pitch;
559 struct wined3d_bo_address data;
560 BYTE *temporary_mem = NULL;
561 void *mem;
563 /* Only support read back of converted P8 surfaces. */
564 if (texture->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
566 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
567 return;
570 sub_resource = &texture->sub_resources[sub_resource_idx];
572 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
574 /* NP2 emulation is not allowed on array textures. */
575 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
576 ERR("Array texture %p uses NP2 emulation.\n", texture);
578 WARN_(d3d_perf)("Downloading all miplevel layers to get the surface data for a single sub-resource.\n");
580 if (!(temporary_mem = wined3d_calloc(texture->layer_count, sub_resource->size)))
582 ERR("Out of memory.\n");
583 return;
587 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
589 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
591 wined3d_texture_get_pitch(texture, surface->texture_level, &dst_row_pitch, &dst_slice_pitch);
592 wined3d_format_calculate_pitch(format, texture->resource.device->surface_alignment,
593 wined3d_texture_get_level_pow2_width(texture, surface->texture_level),
594 wined3d_texture_get_level_pow2_height(texture, surface->texture_level),
595 &src_row_pitch, &src_slice_pitch);
596 if (!(temporary_mem = HeapAlloc(GetProcessHeap(), 0, src_slice_pitch)))
598 ERR("Out of memory.\n");
599 return;
602 if (data.buffer_object)
603 ERR("NP2 emulated texture uses PBO unexpectedly.\n");
604 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
605 ERR("Unexpected compressed format for NP2 emulated texture.\n");
608 if (temporary_mem)
610 mem = temporary_mem;
612 else if (data.buffer_object)
614 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
615 checkGLcall("glBindBuffer");
616 mem = data.addr;
618 else
620 mem = data.addr;
623 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
625 TRACE("Downloading compressed surface %p, level %u, format %#x, type %#x, data %p.\n",
626 surface, surface->texture_level, format->glFormat, format->glType, mem);
628 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target, surface->texture_level, mem));
629 checkGLcall("glGetCompressedTexImage");
631 else
633 TRACE("Downloading surface %p, level %u, format %#x, type %#x, data %p.\n",
634 surface, surface->texture_level, format->glFormat, format->glType, mem);
636 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
637 format->glFormat, format->glType, mem);
638 checkGLcall("glGetTexImage");
641 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
643 const BYTE *src_data;
644 unsigned int h, y;
645 BYTE *dst_data;
647 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
648 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
649 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
651 * We're doing this...
653 * instead of boxing the texture :
654 * |<-texture width ->| -->pow2width| /\
655 * |111111111111111111| | |
656 * |222 Texture 222222| boxed empty | texture height
657 * |3333 Data 33333333| | |
658 * |444444444444444444| | \/
659 * ----------------------------------- |
660 * | boxed empty | boxed empty | pow2height
661 * | | | \/
662 * -----------------------------------
665 * we're repacking the data to the expected texture width
667 * |<-texture width ->| -->pow2width| /\
668 * |111111111111111111222222222222222| |
669 * |222333333333333333333444444444444| texture height
670 * |444444 | |
671 * | | \/
672 * | | |
673 * | empty | pow2height
674 * | | \/
675 * -----------------------------------
677 * == is the same as
679 * |<-texture width ->| /\
680 * |111111111111111111|
681 * |222222222222222222|texture height
682 * |333333333333333333|
683 * |444444444444444444| \/
684 * --------------------
686 * This also means that any references to surface memory should work with the data as if it were a
687 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
689 * internally the texture is still stored in a boxed format so any references to textureName will
690 * get a boxed texture with width pow2width and not a texture of width resource.width. */
691 src_data = mem;
692 dst_data = data.addr;
693 TRACE("Repacking the surface data from pitch %u to pitch %u.\n", src_row_pitch, dst_row_pitch);
694 h = wined3d_texture_get_level_height(texture, surface->texture_level);
695 for (y = 0; y < h; ++y)
697 memcpy(dst_data, src_data, dst_row_pitch);
698 src_data += src_row_pitch;
699 dst_data += dst_row_pitch;
702 else if (temporary_mem)
704 void *src_data = temporary_mem + surface->texture_layer * sub_resource->size;
705 if (data.buffer_object)
707 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
708 checkGLcall("glBindBuffer");
709 GL_EXTCALL(glBufferSubData(GL_PIXEL_PACK_BUFFER, 0, sub_resource->size, src_data));
710 checkGLcall("glBufferSubData");
712 else
714 memcpy(data.addr, src_data, sub_resource->size);
718 if (data.buffer_object)
720 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
721 checkGLcall("glBindBuffer");
724 HeapFree(GetProcessHeap(), 0, temporary_mem);
727 /* This call just uploads data, the caller is responsible for binding the
728 * correct texture. */
729 /* Context activation is done by the caller. */
730 void wined3d_surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
731 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
732 BOOL srgb, const struct wined3d_const_bo_address *data)
734 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
735 struct wined3d_texture *texture = surface->container;
736 UINT update_w = src_rect->right - src_rect->left;
737 UINT update_h = src_rect->bottom - src_rect->top;
739 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
740 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
741 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
743 if (texture->sub_resources[sub_resource_idx].map_count)
745 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
746 texture->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
749 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE)
751 update_h *= format->height_scale.numerator;
752 update_h /= format->height_scale.denominator;
755 if (data->buffer_object)
757 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
758 checkGLcall("glBindBuffer");
761 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
763 unsigned int dst_row_pitch, dst_slice_pitch;
764 const BYTE *addr = data->addr;
765 GLenum internal;
767 addr += (src_rect->top / format->block_height) * src_pitch;
768 addr += (src_rect->left / format->block_width) * format->block_byte_count;
770 if (srgb)
771 internal = format->glGammaInternal;
772 else if (texture->resource.usage & WINED3DUSAGE_RENDERTARGET
773 && wined3d_resource_is_offscreen(&texture->resource))
774 internal = format->rtInternal;
775 else
776 internal = format->glInternal;
778 wined3d_format_calculate_pitch(format, 1, update_w, update_h, &dst_row_pitch, &dst_slice_pitch);
780 TRACE("Uploading compressed data, target %#x, level %u, layer %u, x %d, y %d, w %u, h %u, "
781 "format %#x, image_size %#x, addr %p.\n",
782 surface->texture_target, surface->texture_level, surface->texture_layer,
783 dst_point->x, dst_point->y, update_w, update_h, internal, dst_slice_pitch, addr);
785 if (dst_row_pitch == src_pitch)
787 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
789 GL_EXTCALL(glCompressedTexSubImage3D(surface->texture_target, surface->texture_level,
790 dst_point->x, dst_point->y, surface->texture_layer, update_w, update_h, 1,
791 internal, dst_slice_pitch, addr));
793 else
795 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
796 dst_point->x, dst_point->y, update_w, update_h,
797 internal, dst_slice_pitch, addr));
800 else
802 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
803 UINT row, y;
805 /* glCompressedTexSubImage2D() ignores pixel store state, so we
806 * can't use the unpack row length like for glTexSubImage2D. */
807 for (row = 0, y = dst_point->y; row < row_count; ++row)
809 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
811 GL_EXTCALL(glCompressedTexSubImage3D(surface->texture_target, surface->texture_level,
812 dst_point->x, y, surface->texture_layer, update_w, format->block_height, 1,
813 internal, dst_row_pitch, addr));
815 else
817 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
818 dst_point->x, y, update_w, format->block_height, internal, dst_row_pitch, addr));
821 y += format->block_height;
822 addr += src_pitch;
825 checkGLcall("Upload compressed surface data");
827 else
829 const BYTE *addr = data->addr;
831 addr += src_rect->top * src_pitch;
832 addr += src_rect->left * format->byte_count;
834 TRACE("Uploading data, target %#x, level %u, layer %u, x %d, y %d, w %u, h %u, "
835 "format %#x, type %#x, addr %p.\n",
836 surface->texture_target, surface->texture_level, surface->texture_layer,
837 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
839 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
840 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
842 GL_EXTCALL(glTexSubImage3D(surface->texture_target, surface->texture_level,
843 dst_point->x, dst_point->y, surface->texture_layer, update_w, update_h, 1,
844 format->glFormat, format->glType, addr));
846 else
848 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
849 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
851 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
852 checkGLcall("Upload surface data");
855 if (data->buffer_object)
857 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
858 checkGLcall("glBindBuffer");
861 if (wined3d_settings.strict_draw_ordering)
862 gl_info->gl_ops.gl.p_glFlush();
864 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
866 struct wined3d_device *device = texture->resource.device;
867 unsigned int i;
869 for (i = 0; i < device->context_count; ++i)
871 context_surface_update(device->contexts[i], surface);
876 static BOOL wined3d_surface_check_rect_dimensions(struct wined3d_surface *surface, const RECT *rect)
878 struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
880 return SUCCEEDED(wined3d_texture_check_box_dimensions(surface->container, surface->texture_level, &box));
883 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
884 struct wined3d_surface *src_surface, const RECT *src_rect)
886 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
887 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
888 struct wined3d_texture *src_texture = src_surface->container;
889 struct wined3d_texture *dst_texture = dst_surface->container;
890 unsigned int src_row_pitch, src_slice_pitch;
891 const struct wined3d_format *src_format;
892 const struct wined3d_format *dst_format;
893 const struct wined3d_gl_info *gl_info;
894 struct wined3d_context *context;
895 struct wined3d_bo_address data;
896 UINT update_w, update_h;
897 RECT r, dst_rect;
898 POINT p;
900 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
901 dst_surface, wine_dbgstr_point(dst_point),
902 src_surface, wine_dbgstr_rect(src_rect));
904 src_format = src_texture->resource.format;
905 dst_format = dst_texture->resource.format;
907 if (src_format->id != dst_format->id)
909 WARN("Source and destination surfaces should have the same format.\n");
910 return WINED3DERR_INVALIDCALL;
913 if (!dst_point)
915 p.x = 0;
916 p.y = 0;
917 dst_point = &p;
919 else if (dst_point->x < 0 || dst_point->y < 0)
921 WARN("Invalid destination point.\n");
922 return WINED3DERR_INVALIDCALL;
925 if (!src_rect)
927 SetRect(&r, 0, 0, wined3d_texture_get_level_width(src_texture, src_surface->texture_level),
928 wined3d_texture_get_level_height(src_texture, src_surface->texture_level));
929 src_rect = &r;
932 if (!wined3d_surface_check_rect_dimensions(src_surface, src_rect))
934 WARN("Source rectangle not block-aligned.\n");
935 return WINED3DERR_INVALIDCALL;
938 update_w = src_rect->right - src_rect->left;
939 update_h = src_rect->bottom - src_rect->top;
940 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
941 if (!wined3d_surface_check_rect_dimensions(dst_surface, &dst_rect))
943 WARN("Destination rectangle not block-aligned.\n");
944 return WINED3DERR_INVALIDCALL;
947 /* Use wined3d_texture_blt() instead of uploading directly if we need conversion. */
948 if (dst_format->convert || wined3d_format_get_color_key_conversion(dst_texture, FALSE))
949 return wined3d_texture_blt(dst_texture, dst_sub_resource_idx, &dst_rect,
950 src_texture, src_sub_resource_idx, src_rect, 0, NULL, WINED3D_TEXF_POINT);
952 context = context_acquire(dst_texture->resource.device, NULL, 0);
953 gl_info = context->gl_info;
955 /* Only load the surface for partial updates. For newly allocated texture
956 * the texture wouldn't be the current location, and we'd upload zeroes
957 * just to overwrite them again. */
958 if (update_w == wined3d_texture_get_level_width(dst_texture, dst_surface->texture_level)
959 && update_h == wined3d_texture_get_level_height(dst_texture, dst_surface->texture_level))
960 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
961 else
962 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
963 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
965 wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &data,
966 src_texture->sub_resources[src_sub_resource_idx].locations);
967 wined3d_texture_get_pitch(src_texture, src_surface->texture_level, &src_row_pitch, &src_slice_pitch);
969 wined3d_surface_upload_data(dst_surface, gl_info, src_format, src_rect,
970 src_row_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
972 context_release(context);
974 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
975 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
977 return WINED3D_OK;
980 /* In D3D the depth stencil dimensions have to be greater than or equal to the
981 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
982 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
983 /* Context activation is done by the caller. */
984 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
986 const struct wined3d_gl_info *gl_info = &surface->container->resource.device->adapter->gl_info;
987 struct wined3d_renderbuffer_entry *entry;
988 GLuint renderbuffer = 0;
989 unsigned int src_width, src_height;
990 unsigned int width, height;
992 if (rt && rt->container->resource.format->id != WINED3DFMT_NULL)
994 width = wined3d_texture_get_level_pow2_width(rt->container, rt->texture_level);
995 height = wined3d_texture_get_level_pow2_height(rt->container, rt->texture_level);
997 else
999 width = wined3d_texture_get_level_pow2_width(surface->container, surface->texture_level);
1000 height = wined3d_texture_get_level_pow2_height(surface->container, surface->texture_level);
1003 src_width = wined3d_texture_get_level_pow2_width(surface->container, surface->texture_level);
1004 src_height = wined3d_texture_get_level_pow2_height(surface->container, surface->texture_level);
1006 /* A depth stencil smaller than the render target is not valid */
1007 if (width > src_width || height > src_height) return;
1009 /* Remove any renderbuffer set if the sizes match */
1010 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1011 || (width == src_width && height == src_height))
1013 surface->current_renderbuffer = NULL;
1014 return;
1017 /* Look if we've already got a renderbuffer of the correct dimensions */
1018 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1020 if (entry->width == width && entry->height == height)
1022 renderbuffer = entry->id;
1023 surface->current_renderbuffer = entry;
1024 break;
1028 if (!renderbuffer)
1030 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1031 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1032 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1033 surface->container->resource.format->glInternal, width, height);
1035 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1036 entry->width = width;
1037 entry->height = height;
1038 entry->id = renderbuffer;
1039 list_add_head(&surface->renderbuffers, &entry->entry);
1041 surface->current_renderbuffer = entry;
1044 checkGLcall("set_compatible_renderbuffer");
1047 /* See also float_16_to_32() in wined3d_private.h */
1048 static inline unsigned short float_32_to_16(const float *in)
1050 int exp = 0;
1051 float tmp = fabsf(*in);
1052 unsigned int mantissa;
1053 unsigned short ret;
1055 /* Deal with special numbers */
1056 if (*in == 0.0f)
1057 return 0x0000;
1058 if (isnan(*in))
1059 return 0x7c01;
1060 if (isinf(*in))
1061 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1063 if (tmp < (float)(1u << 10))
1067 tmp = tmp * 2.0f;
1068 exp--;
1069 } while (tmp < (float)(1u << 10));
1071 else if (tmp >= (float)(1u << 11))
1075 tmp /= 2.0f;
1076 exp++;
1077 } while (tmp >= (float)(1u << 11));
1080 mantissa = (unsigned int)tmp;
1081 if (tmp - mantissa >= 0.5f)
1082 ++mantissa; /* Round to nearest, away from zero. */
1084 exp += 10; /* Normalize the mantissa. */
1085 exp += 15; /* Exponent is encoded with excess 15. */
1087 if (exp > 30) /* too big */
1089 ret = 0x7c00; /* INF */
1091 else if (exp <= 0)
1093 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1094 while (exp <= 0)
1096 mantissa = mantissa >> 1;
1097 ++exp;
1099 ret = mantissa & 0x3ff;
1101 else
1103 ret = (exp << 10) | (mantissa & 0x3ff);
1106 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1107 return ret;
1110 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
1111 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1113 unsigned short *dst_s;
1114 const float *src_f;
1115 unsigned int x, y;
1117 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1119 for (y = 0; y < h; ++y)
1121 src_f = (const float *)(src + y * pitch_in);
1122 dst_s = (unsigned short *) (dst + y * pitch_out);
1123 for (x = 0; x < w; ++x)
1125 dst_s[x] = float_32_to_16(src_f + x);
1130 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
1131 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1133 static const unsigned char convert_5to8[] =
1135 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
1136 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
1137 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
1138 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
1140 static const unsigned char convert_6to8[] =
1142 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
1143 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
1144 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
1145 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
1146 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
1147 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
1148 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
1149 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
1151 unsigned int x, y;
1153 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1155 for (y = 0; y < h; ++y)
1157 const WORD *src_line = (const WORD *)(src + y * pitch_in);
1158 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1159 for (x = 0; x < w; ++x)
1161 WORD pixel = src_line[x];
1162 dst_line[x] = 0xff000000u
1163 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
1164 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
1165 | convert_5to8[(pixel & 0x001fu)];
1170 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
1171 * in both cases we're just setting the X / Alpha channel to 0xff. */
1172 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
1173 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1175 unsigned int x, y;
1177 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1179 for (y = 0; y < h; ++y)
1181 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
1182 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1184 for (x = 0; x < w; ++x)
1186 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
1191 static inline BYTE cliptobyte(int x)
1193 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
1196 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
1197 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1199 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1200 unsigned int x, y;
1202 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1204 for (y = 0; y < h; ++y)
1206 const BYTE *src_line = src + y * pitch_in;
1207 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1208 for (x = 0; x < w; ++x)
1210 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1211 * C = Y - 16; D = U - 128; E = V - 128;
1212 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1213 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1214 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1215 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1216 * U and V are shared between the pixels. */
1217 if (!(x & 1)) /* For every even pixel, read new U and V. */
1219 d = (int) src_line[1] - 128;
1220 e = (int) src_line[3] - 128;
1221 r2 = 409 * e + 128;
1222 g2 = - 100 * d - 208 * e + 128;
1223 b2 = 516 * d + 128;
1225 c2 = 298 * ((int) src_line[0] - 16);
1226 dst_line[x] = 0xff000000
1227 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
1228 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
1229 | cliptobyte((c2 + b2) >> 8); /* blue */
1230 /* Scale RGB values to 0..255 range,
1231 * then clip them if still not in range (may be negative),
1232 * then shift them within DWORD if necessary. */
1233 src_line += 2;
1238 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
1239 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1241 unsigned int x, y;
1242 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1244 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
1246 for (y = 0; y < h; ++y)
1248 const BYTE *src_line = src + y * pitch_in;
1249 WORD *dst_line = (WORD *)(dst + y * pitch_out);
1250 for (x = 0; x < w; ++x)
1252 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1253 * C = Y - 16; D = U - 128; E = V - 128;
1254 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1255 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1256 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1257 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1258 * U and V are shared between the pixels. */
1259 if (!(x & 1)) /* For every even pixel, read new U and V. */
1261 d = (int) src_line[1] - 128;
1262 e = (int) src_line[3] - 128;
1263 r2 = 409 * e + 128;
1264 g2 = - 100 * d - 208 * e + 128;
1265 b2 = 516 * d + 128;
1267 c2 = 298 * ((int) src_line[0] - 16);
1268 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
1269 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
1270 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
1271 /* Scale RGB values to 0..255 range,
1272 * then clip them if still not in range (may be negative),
1273 * then shift them within DWORD if necessary. */
1274 src_line += 2;
1279 struct d3dfmt_converter_desc
1281 enum wined3d_format_id from, to;
1282 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
1285 static const struct d3dfmt_converter_desc converters[] =
1287 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
1288 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
1289 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1290 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1291 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
1292 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
1295 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
1296 enum wined3d_format_id to)
1298 unsigned int i;
1300 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
1302 if (converters[i].from == from && converters[i].to == to)
1303 return &converters[i];
1306 return NULL;
1309 static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture,
1310 unsigned int sub_resource_idx, const struct wined3d_format *dst_format)
1312 unsigned int texture_level = sub_resource_idx % src_texture->level_count;
1313 const struct wined3d_format *src_format = src_texture->resource.format;
1314 struct wined3d_device *device = src_texture->resource.device;
1315 const struct d3dfmt_converter_desc *conv = NULL;
1316 struct wined3d_texture *dst_texture;
1317 struct wined3d_resource_desc desc;
1318 struct wined3d_map_desc src_map;
1320 if (!(conv = find_converter(src_format->id, dst_format->id)) && (!device->d3d_initialized
1321 || !is_identity_fixup(src_format->color_fixup) || src_format->convert
1322 || !is_identity_fixup(dst_format->color_fixup) || dst_format->convert
1323 || (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)))
1325 FIXME("Cannot find a conversion function from format %s to %s.\n",
1326 debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
1327 return NULL;
1330 /* FIXME: Multisampled conversion? */
1331 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
1332 desc.format = dst_format->id;
1333 desc.multisample_type = WINED3D_MULTISAMPLE_NONE;
1334 desc.multisample_quality = 0;
1335 desc.usage = 0;
1336 desc.pool = WINED3D_POOL_SCRATCH;
1337 desc.width = wined3d_texture_get_level_width(src_texture, texture_level);
1338 desc.height = wined3d_texture_get_level_height(src_texture, texture_level);
1339 desc.depth = 1;
1340 desc.size = 0;
1341 if (FAILED(wined3d_texture_create(device, &desc, 1, 1,
1342 WINED3D_TEXTURE_CREATE_MAPPABLE | WINED3D_TEXTURE_CREATE_DISCARD,
1343 NULL, NULL, &wined3d_null_parent_ops, &dst_texture)))
1345 ERR("Failed to create a destination texture for conversion.\n");
1346 return NULL;
1349 memset(&src_map, 0, sizeof(src_map));
1350 if (FAILED(wined3d_resource_map(&src_texture->resource, sub_resource_idx,
1351 &src_map, NULL, WINED3D_MAP_READONLY)))
1353 ERR("Failed to map the source texture.\n");
1354 wined3d_texture_decref(dst_texture);
1355 return NULL;
1357 if (conv)
1359 struct wined3d_map_desc dst_map;
1361 memset(&dst_map, 0, sizeof(dst_map));
1362 if (FAILED(wined3d_resource_map(&dst_texture->resource, 0, &dst_map, NULL, 0)))
1364 ERR("Failed to map the destination texture.\n");
1365 wined3d_resource_unmap(&src_texture->resource, sub_resource_idx);
1366 wined3d_texture_decref(dst_texture);
1367 return NULL;
1370 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch, desc.width, desc.height);
1372 wined3d_resource_unmap(&dst_texture->resource, 0);
1374 else
1376 struct wined3d_bo_address data = {0, src_map.data};
1377 RECT src_rect = {0, 0, desc.width, desc.height};
1378 const struct wined3d_gl_info *gl_info;
1379 struct wined3d_context *context;
1380 POINT dst_point = {0, 0};
1382 TRACE("Using upload conversion.\n");
1383 context = context_acquire(device, NULL, 0);
1384 gl_info = context->gl_info;
1386 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
1387 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
1388 wined3d_surface_upload_data(dst_texture->sub_resources[0].u.surface, gl_info, src_format,
1389 &src_rect, src_map.row_pitch, &dst_point, FALSE, wined3d_const_bo_address(&data));
1391 context_release(context);
1393 wined3d_texture_validate_location(dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB);
1394 wined3d_texture_invalidate_location(dst_texture, 0, ~WINED3D_LOCATION_TEXTURE_RGB);
1396 wined3d_resource_unmap(&src_texture->resource, sub_resource_idx);
1398 return dst_texture;
1401 static void read_from_framebuffer(struct wined3d_surface *surface,
1402 struct wined3d_context *old_ctx, DWORD dst_location)
1404 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
1405 struct wined3d_texture *texture = surface->container;
1406 struct wined3d_device *device = texture->resource.device;
1407 const struct wined3d_gl_info *gl_info;
1408 struct wined3d_context *context = old_ctx;
1409 struct wined3d_surface *restore_rt = NULL;
1410 unsigned int row_pitch, slice_pitch;
1411 unsigned int width, height;
1412 BYTE *mem;
1413 BYTE *row, *top, *bottom;
1414 int i;
1415 BOOL srcIsUpsideDown;
1416 struct wined3d_bo_address data;
1418 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
1420 restore_rt = context_get_rt_surface(old_ctx);
1421 if (restore_rt != surface)
1422 context = context_acquire(device, texture, sub_resource_idx);
1423 else
1424 restore_rt = NULL;
1426 context_apply_blit_state(context, device);
1427 gl_info = context->gl_info;
1429 /* Select the correct read buffer, and give some debug output.
1430 * There is no need to keep track of the current read buffer or reset it, every part of the code
1431 * that reads sets the read buffer as desired.
1433 if (wined3d_resource_is_offscreen(&texture->resource))
1435 /* Mapping the primary render target which is not on a swapchain.
1436 * Read from the back buffer. */
1437 TRACE("Mapping offscreen render target.\n");
1438 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1439 srcIsUpsideDown = TRUE;
1441 else
1443 /* Onscreen surfaces are always part of a swapchain */
1444 GLenum buffer = wined3d_texture_get_gl_buffer(texture);
1445 TRACE("Mapping %#x buffer.\n", buffer);
1446 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
1447 checkGLcall("glReadBuffer");
1448 srcIsUpsideDown = FALSE;
1451 if (data.buffer_object)
1453 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1454 checkGLcall("glBindBuffer");
1457 wined3d_texture_get_pitch(texture, surface->texture_level, &row_pitch, &slice_pitch);
1459 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1460 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / texture->resource.format->byte_count);
1461 checkGLcall("glPixelStorei");
1463 width = wined3d_texture_get_level_width(texture, surface->texture_level);
1464 height = wined3d_texture_get_level_height(texture, surface->texture_level);
1465 gl_info->gl_ops.gl.p_glReadPixels(0, 0, width, height,
1466 texture->resource.format->glFormat,
1467 texture->resource.format->glType, data.addr);
1468 checkGLcall("glReadPixels");
1470 /* Reset previous pixel store pack state */
1471 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
1472 checkGLcall("glPixelStorei");
1474 if (!srcIsUpsideDown)
1476 /* glReadPixels returns the image upside down, and there is no way to
1477 * prevent this. Flip the lines in software. */
1479 if (!(row = HeapAlloc(GetProcessHeap(), 0, row_pitch)))
1480 goto error;
1482 if (data.buffer_object)
1484 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
1485 checkGLcall("glMapBuffer");
1487 else
1488 mem = data.addr;
1490 top = mem;
1491 bottom = mem + row_pitch * (height - 1);
1492 for (i = 0; i < height / 2; i++)
1494 memcpy(row, top, row_pitch);
1495 memcpy(top, bottom, row_pitch);
1496 memcpy(bottom, row, row_pitch);
1497 top += row_pitch;
1498 bottom -= row_pitch;
1500 HeapFree(GetProcessHeap(), 0, row);
1502 if (data.buffer_object)
1503 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
1506 error:
1507 if (data.buffer_object)
1509 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1510 checkGLcall("glBindBuffer");
1513 if (restore_rt)
1514 context_restore(context, restore_rt);
1517 /* Read the framebuffer contents into a texture. Note that this function
1518 * doesn't do any kind of flipping. Using this on an onscreen surface will
1519 * result in a flipped D3D texture.
1521 * Context activation is done by the caller. This function may temporarily
1522 * switch to a different context and restore the original one before return. */
1523 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb, struct wined3d_context *old_ctx)
1525 struct wined3d_texture *texture = surface->container;
1526 struct wined3d_device *device = texture->resource.device;
1527 const struct wined3d_gl_info *gl_info;
1528 struct wined3d_context *context = old_ctx;
1529 struct wined3d_surface *restore_rt = NULL;
1531 restore_rt = context_get_rt_surface(old_ctx);
1532 if (restore_rt != surface)
1533 context = context_acquire(device, texture, surface_get_sub_resource_idx(surface));
1534 else
1535 restore_rt = NULL;
1537 gl_info = context->gl_info;
1538 device_invalidate_state(device, STATE_FRAMEBUFFER);
1540 wined3d_texture_prepare_texture(texture, context, srgb);
1541 wined3d_texture_bind_and_dirtify(texture, context, srgb);
1543 TRACE("Reading back offscreen render target %p.\n", surface);
1545 if (wined3d_resource_is_offscreen(&texture->resource))
1546 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1547 else
1548 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(texture));
1549 checkGLcall("glReadBuffer");
1551 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
1552 0, 0, 0, 0, wined3d_texture_get_level_width(texture, surface->texture_level),
1553 wined3d_texture_get_level_height(texture, surface->texture_level));
1554 checkGLcall("glCopyTexSubImage2D");
1556 if (restore_rt)
1557 context_restore(context, restore_rt);
1560 /* Does a direct frame buffer -> texture copy. Stretching is done with single
1561 * pixel copy calls. */
1562 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
1563 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
1565 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
1566 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1567 struct wined3d_texture *src_texture = src_surface->container;
1568 struct wined3d_texture *dst_texture = dst_surface->container;
1569 struct wined3d_device *device = dst_texture->resource.device;
1570 const struct wined3d_gl_info *gl_info;
1571 float xrel, yrel;
1572 struct wined3d_context *context;
1573 BOOL upsidedown = FALSE;
1574 RECT dst_rect = *dst_rect_in;
1575 unsigned int src_height;
1577 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1578 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1580 if(dst_rect.top > dst_rect.bottom) {
1581 UINT tmp = dst_rect.bottom;
1582 dst_rect.bottom = dst_rect.top;
1583 dst_rect.top = tmp;
1584 upsidedown = TRUE;
1587 context = context_acquire(device, src_texture, src_sub_resource_idx);
1588 gl_info = context->gl_info;
1589 context_apply_blit_state(context, device);
1590 wined3d_texture_load(dst_texture, context, FALSE);
1592 /* Bind the target texture */
1593 context_bind_texture(context, dst_texture->target, dst_texture->texture_rgb.name);
1594 if (wined3d_resource_is_offscreen(&src_texture->resource))
1596 TRACE("Reading from an offscreen target\n");
1597 upsidedown = !upsidedown;
1598 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1600 else
1602 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
1604 checkGLcall("glReadBuffer");
1606 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
1607 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
1609 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1611 FIXME_(d3d_perf)("Doing a pixel by pixel copy from the framebuffer to a texture.\n");
1613 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1614 ERR("Texture filtering not supported in direct blit.\n");
1616 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1617 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1619 ERR("Texture filtering not supported in direct blit\n");
1622 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
1623 if (upsidedown
1624 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1625 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1627 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
1628 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1629 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
1630 src_rect->left, src_height - src_rect->bottom,
1631 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1633 else
1635 LONG row;
1636 UINT yoffset = src_height - src_rect->top + dst_rect.top - 1;
1637 /* I have to process this row by row to swap the image,
1638 * otherwise it would be upside down, so stretching in y direction
1639 * doesn't cost extra time
1641 * However, stretching in x direction can be avoided if not necessary
1643 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
1644 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1646 /* Well, that stuff works, but it's very slow.
1647 * find a better way instead
1649 LONG col;
1651 for (col = dst_rect.left; col < dst_rect.right; ++col)
1653 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1654 dst_rect.left + col /* x offset */, row /* y offset */,
1655 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
1658 else
1660 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1661 dst_rect.left /* x offset */, row /* y offset */,
1662 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
1666 checkGLcall("glCopyTexSubImage2D");
1668 context_release(context);
1670 /* The texture is now most up to date - If the surface is a render target
1671 * and has a drawable, this path is never entered. */
1672 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1673 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1676 /* Uses the hardware to stretch and flip the image */
1677 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
1678 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
1680 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
1681 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1682 unsigned int src_width, src_height, src_pow2_width, src_pow2_height;
1683 struct wined3d_texture *src_texture = src_surface->container;
1684 struct wined3d_texture *dst_texture = dst_surface->container;
1685 struct wined3d_device *device = dst_texture->resource.device;
1686 GLuint src, backup = 0;
1687 float left, right, top, bottom; /* Texture coordinates */
1688 const struct wined3d_gl_info *gl_info;
1689 struct wined3d_context *context;
1690 GLenum drawBuffer = GL_BACK;
1691 GLenum offscreen_buffer;
1692 GLenum texture_target;
1693 BOOL noBackBufferBackup;
1694 BOOL src_offscreen;
1695 BOOL upsidedown = FALSE;
1696 RECT dst_rect = *dst_rect_in;
1698 TRACE("Using hwstretch blit\n");
1699 /* Activate the Proper context for reading from the source surface, set it up for blitting */
1700 context = context_acquire(device, src_texture, src_sub_resource_idx);
1701 gl_info = context->gl_info;
1702 context_apply_blit_state(context, device);
1703 wined3d_texture_load(dst_texture, context, FALSE);
1705 offscreen_buffer = context_get_offscreen_gl_buffer(context);
1706 src_width = wined3d_texture_get_level_width(src_texture, src_surface->texture_level);
1707 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
1708 src_pow2_width = wined3d_texture_get_level_pow2_width(src_texture, src_surface->texture_level);
1709 src_pow2_height = wined3d_texture_get_level_pow2_height(src_texture, src_surface->texture_level);
1711 src_offscreen = wined3d_resource_is_offscreen(&src_texture->resource);
1712 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
1713 if (!noBackBufferBackup && !src_texture->texture_rgb.name)
1715 /* Get it a description */
1716 wined3d_texture_load(src_texture, context, FALSE);
1719 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
1720 * This way we don't have to wait for the 2nd readback to finish to leave this function.
1722 if (context->aux_buffers >= 2)
1724 /* Got more than one aux buffer? Use the 2nd aux buffer */
1725 drawBuffer = GL_AUX1;
1727 else if ((!src_offscreen || offscreen_buffer == GL_BACK) && context->aux_buffers >= 1)
1729 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
1730 drawBuffer = GL_AUX0;
1733 if (noBackBufferBackup)
1735 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
1736 checkGLcall("glGenTextures");
1737 context_bind_texture(context, GL_TEXTURE_2D, backup);
1738 texture_target = GL_TEXTURE_2D;
1740 else
1742 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
1743 * we are reading from the back buffer, the backup can be used as source texture
1745 texture_target = src_surface->texture_target;
1746 context_bind_texture(context, texture_target, src_texture->texture_rgb.name);
1747 gl_info->gl_ops.gl.p_glEnable(texture_target);
1748 checkGLcall("glEnable(texture_target)");
1750 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
1751 surface_get_sub_resource(src_surface)->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
1754 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1755 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1757 if(dst_rect.top > dst_rect.bottom) {
1758 UINT tmp = dst_rect.bottom;
1759 dst_rect.bottom = dst_rect.top;
1760 dst_rect.top = tmp;
1761 upsidedown = TRUE;
1764 if (src_offscreen)
1766 TRACE("Reading from an offscreen target\n");
1767 upsidedown = !upsidedown;
1768 gl_info->gl_ops.gl.p_glReadBuffer(offscreen_buffer);
1770 else
1772 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
1775 /* TODO: Only back up the part that will be overwritten */
1776 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, src_width, src_height);
1778 checkGLcall("glCopyTexSubImage2D");
1780 /* No issue with overriding these - the sampler is dirty due to blit usage */
1781 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
1782 checkGLcall("glTexParameteri");
1783 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
1784 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
1785 checkGLcall("glTexParameteri");
1787 if (!src_texture->swapchain || src_texture == src_texture->swapchain->back_buffers[0])
1789 src = backup ? backup : src_texture->texture_rgb.name;
1791 else
1793 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
1794 checkGLcall("glReadBuffer(GL_FRONT)");
1796 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
1797 checkGLcall("glGenTextures(1, &src)");
1798 context_bind_texture(context, GL_TEXTURE_2D, src);
1800 /* TODO: Only copy the part that will be read. Use src_rect->left,
1801 * src_rect->bottom as origin, but with the width watch out for power
1802 * of 2 sizes. */
1803 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_pow2_width,
1804 src_pow2_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
1805 checkGLcall("glTexImage2D");
1806 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, src_width, src_height);
1808 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1809 checkGLcall("glTexParameteri");
1810 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1811 checkGLcall("glTexParameteri");
1813 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
1814 checkGLcall("glReadBuffer(GL_BACK)");
1816 if (texture_target != GL_TEXTURE_2D)
1818 gl_info->gl_ops.gl.p_glDisable(texture_target);
1819 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
1820 texture_target = GL_TEXTURE_2D;
1823 checkGLcall("glEnd and previous");
1825 left = src_rect->left;
1826 right = src_rect->right;
1828 if (!upsidedown)
1830 top = src_height - src_rect->top;
1831 bottom = src_height - src_rect->bottom;
1833 else
1835 top = src_height - src_rect->bottom;
1836 bottom = src_height - src_rect->top;
1839 if (src_texture->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
1841 left /= src_pow2_width;
1842 right /= src_pow2_width;
1843 top /= src_pow2_height;
1844 bottom /= src_pow2_height;
1847 /* draw the source texture stretched and upside down. The correct surface is bound already */
1848 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1849 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1851 context_set_draw_buffer(context, drawBuffer);
1852 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
1854 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
1855 /* bottom left */
1856 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
1857 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
1859 /* top left */
1860 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
1861 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
1863 /* top right */
1864 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
1865 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1867 /* bottom right */
1868 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
1869 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
1870 gl_info->gl_ops.gl.p_glEnd();
1871 checkGLcall("glEnd and previous");
1873 if (texture_target != dst_surface->texture_target)
1875 gl_info->gl_ops.gl.p_glDisable(texture_target);
1876 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
1877 texture_target = dst_surface->texture_target;
1880 /* Now read the stretched and upside down image into the destination texture */
1881 context_bind_texture(context, texture_target, dst_texture->texture_rgb.name);
1882 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
1884 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
1885 0, 0, /* We blitted the image to the origin */
1886 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1887 checkGLcall("glCopyTexSubImage2D");
1889 if (drawBuffer == GL_BACK)
1891 /* Write the back buffer backup back. */
1892 if (backup)
1894 if (texture_target != GL_TEXTURE_2D)
1896 gl_info->gl_ops.gl.p_glDisable(texture_target);
1897 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
1898 texture_target = GL_TEXTURE_2D;
1900 context_bind_texture(context, GL_TEXTURE_2D, backup);
1902 else
1904 if (texture_target != src_surface->texture_target)
1906 gl_info->gl_ops.gl.p_glDisable(texture_target);
1907 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
1908 texture_target = src_surface->texture_target;
1910 context_bind_texture(context, src_surface->texture_target, src_texture->texture_rgb.name);
1913 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
1914 /* top left */
1915 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
1916 gl_info->gl_ops.gl.p_glVertex2i(0, src_height);
1918 /* bottom left */
1919 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)src_height / (float)src_pow2_height);
1920 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
1922 /* bottom right */
1923 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width,
1924 (float)src_height / (float)src_pow2_height);
1925 gl_info->gl_ops.gl.p_glVertex2i(src_width, 0);
1927 /* top right */
1928 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width, 0.0f);
1929 gl_info->gl_ops.gl.p_glVertex2i(src_width, src_height);
1930 gl_info->gl_ops.gl.p_glEnd();
1932 gl_info->gl_ops.gl.p_glDisable(texture_target);
1933 checkGLcall("glDisable(texture_target)");
1935 /* Cleanup */
1936 if (src != src_texture->texture_rgb.name && src != backup)
1938 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
1939 checkGLcall("glDeleteTextures(1, &src)");
1941 if (backup)
1943 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
1944 checkGLcall("glDeleteTextures(1, &backup)");
1947 if (wined3d_settings.strict_draw_ordering)
1948 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
1950 context_release(context);
1952 /* The texture is now most up to date - If the surface is a render target
1953 * and has a drawable, this path is never entered. */
1954 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1955 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1958 /* Front buffer coordinates are always full screen coordinates, but our GL
1959 * drawable is limited to the window's client area. The sysmem and texture
1960 * copies do have the full screen size. Note that GL has a bottom-left
1961 * origin, while D3D has a top-left origin. */
1962 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
1964 struct wined3d_texture *texture = surface->container;
1965 UINT drawable_height;
1967 if (texture->swapchain)
1969 POINT offset = {0, 0};
1970 RECT windowsize;
1972 if (texture == texture->swapchain->front_buffer)
1974 ScreenToClient(window, &offset);
1975 OffsetRect(rect, offset.x, offset.y);
1978 GetClientRect(window, &windowsize);
1979 drawable_height = windowsize.bottom - windowsize.top;
1981 else
1983 drawable_height = wined3d_texture_get_level_height(texture, surface->texture_level);
1986 rect->top = drawable_height - rect->top;
1987 rect->bottom = drawable_height - rect->bottom;
1990 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
1991 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
1992 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
1994 struct wined3d_texture *dst_texture = dst_surface->container;
1995 struct wined3d_device *device = dst_texture->resource.device;
1996 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
1997 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1998 struct wined3d_texture *src_texture;
2000 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
2001 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
2002 flags, fx, debug_d3dtexturefiltertype(filter));
2004 /* Get the swapchain. One of the surfaces has to be a primary surface */
2005 if (dst_texture->resource.pool == WINED3D_POOL_SYSTEM_MEM)
2007 WARN("Destination is in sysmem, rejecting gl blt\n");
2008 return WINED3DERR_INVALIDCALL;
2011 dst_swapchain = dst_texture->swapchain;
2013 if (src_surface)
2015 src_texture = src_surface->container;
2016 if (src_texture->resource.pool == WINED3D_POOL_SYSTEM_MEM)
2018 WARN("Src is in sysmem, rejecting gl blt\n");
2019 return WINED3DERR_INVALIDCALL;
2022 src_swapchain = src_texture->swapchain;
2024 else
2026 src_texture = NULL;
2027 src_swapchain = NULL;
2030 /* Early sort out of cases where no render target is used */
2031 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
2033 TRACE("No surface is render target, not using hardware blit.\n");
2034 return WINED3DERR_INVALIDCALL;
2037 /* No destination color keying supported */
2038 if (flags & (WINED3D_BLT_DST_CKEY | WINED3D_BLT_DST_CKEY_OVERRIDE))
2040 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2041 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2042 return WINED3DERR_INVALIDCALL;
2045 if (dst_swapchain && dst_swapchain == src_swapchain)
2047 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
2048 return WINED3DERR_INVALIDCALL;
2051 if (dst_swapchain && src_swapchain)
2053 FIXME("Implement hardware blit between two different swapchains\n");
2054 return WINED3DERR_INVALIDCALL;
2057 if (dst_swapchain)
2059 /* Handled with regular texture -> swapchain blit */
2060 if (src_surface == rt)
2061 TRACE("Blit from active render target to a swapchain\n");
2063 else if (src_swapchain && dst_surface == rt)
2065 FIXME("Implement blit from a swapchain to the active render target\n");
2066 return WINED3DERR_INVALIDCALL;
2069 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
2071 unsigned int src_width, src_height;
2072 /* Blit from render target to texture */
2073 BOOL stretchx;
2075 /* P8 read back is not implemented */
2076 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT
2077 || dst_texture->resource.format->id == WINED3DFMT_P8_UINT)
2079 TRACE("P8 read back not supported by frame buffer to texture blit\n");
2080 return WINED3DERR_INVALIDCALL;
2083 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_SRC_CKEY_OVERRIDE))
2085 TRACE("Color keying not supported by frame buffer to texture blit\n");
2086 return WINED3DERR_INVALIDCALL;
2087 /* Destination color key is checked above */
2090 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
2091 stretchx = TRUE;
2092 else
2093 stretchx = FALSE;
2095 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2096 * flip the image nor scale it.
2098 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2099 * -> If the app wants an image width an unscaled width, copy it line per line
2100 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
2101 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2102 * back buffer. This is slower than reading line per line, thus not used for flipping
2103 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2104 * pixel by pixel. */
2105 src_width = wined3d_texture_get_level_width(src_texture, src_surface->texture_level);
2106 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
2107 if (!stretchx || dst_rect->right - dst_rect->left > src_width
2108 || dst_rect->bottom - dst_rect->top > src_height)
2110 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
2111 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
2113 else
2115 TRACE("Using hardware stretching to flip / stretch the texture.\n");
2116 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
2119 return WINED3D_OK;
2122 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
2123 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
2124 return WINED3DERR_INVALIDCALL;
2127 /* Context activation is done by the caller. */
2128 static BOOL surface_load_sysmem(struct wined3d_surface *surface,
2129 struct wined3d_context *context, DWORD dst_location)
2131 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2132 const struct wined3d_gl_info *gl_info = context->gl_info;
2133 struct wined3d_texture *texture = surface->container;
2134 struct wined3d_texture_sub_resource *sub_resource;
2136 sub_resource = &texture->sub_resources[sub_resource_idx];
2137 wined3d_texture_prepare_location(texture, sub_resource_idx, context, dst_location);
2139 if (sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
2140 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
2142 /* Download the surface to system memory. */
2143 if (sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
2145 wined3d_texture_bind_and_dirtify(texture, context,
2146 !(sub_resource->locations & WINED3D_LOCATION_TEXTURE_RGB));
2147 surface_download_data(surface, gl_info, dst_location);
2148 ++texture->download_count;
2150 return TRUE;
2153 if (!(texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2154 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
2156 read_from_framebuffer(surface, context, dst_location);
2157 return TRUE;
2160 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
2161 surface, wined3d_debug_location(sub_resource->locations));
2162 return FALSE;
2165 /* Context activation is done by the caller. */
2166 static BOOL surface_load_drawable(struct wined3d_surface *surface,
2167 struct wined3d_context *context)
2169 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2170 struct wined3d_texture *texture = surface->container;
2171 struct wined3d_surface *restore_rt = NULL;
2172 struct wined3d_device *device;
2173 RECT r;
2175 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2177 DWORD current = texture->sub_resources[sub_resource_idx].locations;
2178 FIXME("Unimplemented copy from %s for depth/stencil buffers.\n",
2179 wined3d_debug_location(current));
2180 return FALSE;
2183 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
2184 && wined3d_resource_is_offscreen(&texture->resource))
2186 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
2187 return FALSE;
2190 device = texture->resource.device;
2191 restore_rt = context_get_rt_surface(context);
2192 if (restore_rt != surface)
2193 context = context_acquire(device, texture, sub_resource_idx);
2194 else
2195 restore_rt = NULL;
2197 surface_get_rect(surface, NULL, &r);
2198 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
2199 device->blitter->blit_surface(device, WINED3D_BLIT_OP_COLOR_BLIT, context,
2200 surface, WINED3D_LOCATION_TEXTURE_RGB, &r,
2201 surface, WINED3D_LOCATION_DRAWABLE, &r,
2202 NULL, WINED3D_TEXF_POINT);
2204 if (restore_rt)
2205 context_restore(context, restore_rt);
2207 return TRUE;
2210 static BOOL surface_load_texture(struct wined3d_surface *surface,
2211 struct wined3d_context *context, BOOL srgb)
2213 unsigned int width, height, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch;
2214 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2215 const struct wined3d_gl_info *gl_info = context->gl_info;
2216 struct wined3d_texture *texture = surface->container;
2217 struct wined3d_device *device = texture->resource.device;
2218 const struct wined3d_color_key_conversion *conversion;
2219 struct wined3d_texture_sub_resource *sub_resource;
2220 struct wined3d_bo_address data;
2221 BYTE *src_mem, *dst_mem = NULL;
2222 struct wined3d_format format;
2223 POINT dst_point = {0, 0};
2224 RECT src_rect;
2225 BOOL depth;
2227 depth = texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL;
2228 sub_resource = surface_get_sub_resource(surface);
2230 if (!depth && wined3d_settings.offscreen_rendering_mode != ORM_FBO
2231 && wined3d_resource_is_offscreen(&texture->resource)
2232 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
2234 surface_load_fb_texture(surface, srgb, context);
2236 return TRUE;
2239 width = wined3d_texture_get_level_width(texture, surface->texture_level);
2240 height = wined3d_texture_get_level_height(texture, surface->texture_level);
2241 SetRect(&src_rect, 0, 0, width, height);
2243 if (!depth && sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
2244 && (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
2245 && fbo_blitter_supported(gl_info, &device->adapter->d3d_info, WINED3D_BLIT_OP_COLOR_BLIT,
2246 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format,
2247 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format))
2249 if (srgb)
2250 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
2251 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
2252 else
2253 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
2254 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
2256 return TRUE;
2259 if (!depth && sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
2260 && (!srgb || (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
2261 && fbo_blitter_supported(gl_info, &device->adapter->d3d_info, WINED3D_BLIT_OP_COLOR_BLIT,
2262 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format,
2263 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format))
2265 DWORD src_location = sub_resource->locations & WINED3D_LOCATION_RB_RESOLVED ?
2266 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
2267 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2269 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, src_location,
2270 &src_rect, surface, dst_location, &src_rect);
2272 return TRUE;
2275 /* Upload from system memory */
2277 if (srgb)
2279 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | texture->resource.map_binding))
2280 == WINED3D_LOCATION_TEXTURE_RGB)
2282 FIXME_(d3d_perf)("Downloading RGB surface %p to reload it as sRGB.\n", surface);
2283 wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding);
2286 else
2288 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | texture->resource.map_binding))
2289 == WINED3D_LOCATION_TEXTURE_SRGB)
2291 FIXME_(d3d_perf)("Downloading sRGB surface %p to reload it as RGB.\n", surface);
2292 wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding);
2296 if (!(sub_resource->locations & surface_simple_locations))
2298 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
2299 /* Lets hope we get it from somewhere... */
2300 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM);
2303 wined3d_texture_prepare_texture(texture, context, srgb);
2304 wined3d_texture_bind_and_dirtify(texture, context, srgb);
2305 wined3d_texture_get_pitch(texture, surface->texture_level, &src_row_pitch, &src_slice_pitch);
2307 format = *texture->resource.format;
2308 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
2309 format = *wined3d_get_format(gl_info, conversion->dst_format, texture->resource.usage);
2311 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
2312 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
2313 * getting called. */
2314 if ((format.convert || conversion) && texture->sub_resources[sub_resource_idx].buffer_object)
2316 TRACE("Removing the pbo attached to surface %p.\n", surface);
2318 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM);
2319 wined3d_texture_set_map_binding(texture, WINED3D_LOCATION_SYSMEM);
2322 wined3d_texture_get_memory(texture, sub_resource_idx, &data, sub_resource->locations);
2323 if (format.convert)
2325 /* This code is entered for texture formats which need a fixup. */
2326 format.byte_count = format.conv_byte_count;
2327 wined3d_format_calculate_pitch(&format, 1, width, height, &dst_row_pitch, &dst_slice_pitch);
2329 src_mem = wined3d_texture_map_bo_address(&data, src_slice_pitch,
2330 gl_info, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READONLY);
2331 if (!(dst_mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
2333 ERR("Out of memory (%u).\n", dst_slice_pitch);
2334 context_release(context);
2335 return FALSE;
2337 format.convert(src_mem, dst_mem, src_row_pitch, src_slice_pitch,
2338 dst_row_pitch, dst_slice_pitch, width, height, 1);
2339 src_row_pitch = dst_row_pitch;
2340 wined3d_texture_unmap_bo_address(&data, gl_info, GL_PIXEL_UNPACK_BUFFER);
2342 data.buffer_object = 0;
2343 data.addr = dst_mem;
2345 else if (conversion)
2347 /* This code is only entered for color keying fixups */
2348 struct wined3d_palette *palette = NULL;
2350 wined3d_format_calculate_pitch(&format, device->surface_alignment,
2351 width, height, &dst_row_pitch, &dst_slice_pitch);
2353 src_mem = wined3d_texture_map_bo_address(&data, src_slice_pitch,
2354 gl_info, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READONLY);
2355 if (!(dst_mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
2357 ERR("Out of memory (%u).\n", dst_slice_pitch);
2358 context_release(context);
2359 return FALSE;
2361 if (texture->swapchain && texture->swapchain->palette)
2362 palette = texture->swapchain->palette;
2363 conversion->convert(src_mem, src_row_pitch, dst_mem, dst_row_pitch,
2364 width, height, palette, &texture->async.gl_color_key);
2365 src_row_pitch = dst_row_pitch;
2366 wined3d_texture_unmap_bo_address(&data, gl_info, GL_PIXEL_UNPACK_BUFFER);
2368 data.buffer_object = 0;
2369 data.addr = dst_mem;
2372 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
2373 src_row_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
2375 HeapFree(GetProcessHeap(), 0, dst_mem);
2377 return TRUE;
2380 /* Context activation is done by the caller. */
2381 static BOOL surface_load_renderbuffer(struct wined3d_surface *surface, struct wined3d_context *context,
2382 DWORD dst_location)
2384 struct wined3d_texture *texture = surface->container;
2385 const RECT rect = {0, 0,
2386 wined3d_texture_get_level_width(texture, surface->texture_level),
2387 wined3d_texture_get_level_height(texture, surface->texture_level)};
2388 DWORD locations = surface_get_sub_resource(surface)->locations;
2389 DWORD src_location;
2391 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2393 FIXME("Unimplemented copy from %s for depth/stencil buffers.\n",
2394 wined3d_debug_location(locations));
2395 return FALSE;
2398 if (locations & WINED3D_LOCATION_RB_MULTISAMPLE)
2399 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
2400 else if (locations & WINED3D_LOCATION_RB_RESOLVED)
2401 src_location = WINED3D_LOCATION_RB_RESOLVED;
2402 else if (locations & WINED3D_LOCATION_TEXTURE_SRGB)
2403 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
2404 else /* surface_blt_fbo will load the source location if necessary. */
2405 src_location = WINED3D_LOCATION_TEXTURE_RGB;
2407 surface_blt_fbo(texture->resource.device, context, WINED3D_TEXF_POINT,
2408 surface, src_location, &rect, surface, dst_location, &rect);
2410 return TRUE;
2413 /* Context activation is done by the caller. Context may be NULL in ddraw-only mode. */
2414 BOOL surface_load_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
2416 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
2418 switch (location)
2420 case WINED3D_LOCATION_USER_MEMORY:
2421 case WINED3D_LOCATION_SYSMEM:
2422 case WINED3D_LOCATION_BUFFER:
2423 return surface_load_sysmem(surface, context, location);
2425 case WINED3D_LOCATION_DRAWABLE:
2426 return surface_load_drawable(surface, context);
2428 case WINED3D_LOCATION_RB_RESOLVED:
2429 case WINED3D_LOCATION_RB_MULTISAMPLE:
2430 return surface_load_renderbuffer(surface, context, location);
2432 case WINED3D_LOCATION_TEXTURE_RGB:
2433 case WINED3D_LOCATION_TEXTURE_SRGB:
2434 return surface_load_texture(surface, context,
2435 location == WINED3D_LOCATION_TEXTURE_SRGB);
2437 default:
2438 ERR("Don't know how to handle location %#x.\n", location);
2439 return FALSE;
2443 static HRESULT fbo_blitter_alloc(struct wined3d_device *device)
2445 return WINED3D_OK;
2448 /* Context activation is done by the caller. */
2449 static void fbo_blitter_free(struct wined3d_device *device)
2453 static void fbo_blitter_clear(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
2454 const RECT *rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
2456 ERR("This blitter does not implement clears.\n");
2459 static void fbo_blitter_blit(struct wined3d_device *device, enum wined3d_blit_op op,
2460 struct wined3d_context *context, struct wined3d_surface *src_surface, DWORD src_location,
2461 const RECT *src_rect, struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect,
2462 const struct wined3d_color_key *colour_key, enum wined3d_texture_filter_type filter)
2464 if (op == WINED3D_BLIT_OP_COLOR_BLIT)
2466 TRACE("Colour blit.\n");
2467 surface_blt_fbo(device, context, filter, src_surface, src_location,
2468 src_rect, dst_surface, dst_location, dst_rect);
2469 return;
2472 if (op == WINED3D_BLIT_OP_DEPTH_BLIT)
2474 TRACE("Depth/stencil blit.\n");
2475 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
2476 return;
2479 ERR("This blitter does not implement blit op %#x.\n", op);
2482 const struct wined3d_blitter_ops fbo_blitter_ops =
2484 fbo_blitter_alloc,
2485 fbo_blitter_free,
2486 fbo_blitter_supported,
2487 fbo_blitter_clear,
2488 fbo_blitter_blit,
2491 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
2492 /* Context activation is done by the caller. */
2493 static void ffp_blit_free(struct wined3d_device *device) { }
2495 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info,
2496 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
2497 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
2498 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
2500 BOOL decompress;
2502 decompress = src_format && (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
2503 && !(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED);
2504 if (!decompress && (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM))
2506 TRACE("Source or destination is in system memory.\n");
2507 return FALSE;
2510 switch (blit_op)
2512 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
2513 if (d3d_info->shader_color_key)
2515 TRACE("Color keying requires converted textures.\n");
2516 return FALSE;
2518 case WINED3D_BLIT_OP_COLOR_BLIT:
2519 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
2520 if (TRACE_ON(d3d))
2522 TRACE("Checking support for fixup:\n");
2523 dump_color_fixup_desc(src_format->color_fixup);
2526 /* We only support identity conversions. */
2527 if (!is_identity_fixup(src_format->color_fixup)
2528 || !is_identity_fixup(dst_format->color_fixup))
2530 TRACE("Fixups are not supported.\n");
2531 return FALSE;
2534 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
2536 TRACE("Can only blit to render targets.\n");
2537 return FALSE;
2539 return TRUE;
2541 case WINED3D_BLIT_OP_COLOR_FILL:
2542 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
2544 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
2545 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
2546 return FALSE;
2548 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
2550 TRACE("Color fill not supported\n");
2551 return FALSE;
2554 /* FIXME: We should reject color fills on formats with fixups,
2555 * but this would break P8 color fills for example. */
2557 return TRUE;
2559 case WINED3D_BLIT_OP_DEPTH_FILL:
2560 return TRUE;
2562 default:
2563 TRACE("Unsupported blit_op=%d\n", blit_op);
2564 return FALSE;
2568 static void ffp_blit_clear(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
2569 const RECT *rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
2571 const RECT draw_rect = {0, 0, view->width, view->height};
2572 struct wined3d_fb_state fb = {&view, NULL};
2574 if (flags != WINED3DCLEAR_TARGET)
2576 struct wined3d_fb_state fb = {NULL, view};
2578 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, flags, NULL, depth, stencil);
2579 return;
2582 device_clear_render_targets(device, 1, &fb, 1, rect, &draw_rect, flags, colour, 0.0f, 0);
2585 static void ffp_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op,
2586 struct wined3d_context *context, struct wined3d_surface *src_surface, DWORD src_location,
2587 const RECT *src_rect, struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect,
2588 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter)
2590 struct wined3d_texture *src_texture = src_surface->container;
2591 struct wined3d_texture *dst_texture = dst_surface->container;
2592 const struct wined3d_gl_info *gl_info = context->gl_info;
2593 struct wined3d_color_key old_blt_key;
2594 DWORD old_color_key_flags;
2595 RECT r;
2597 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
2599 old_blt_key = src_texture->async.src_blt_color_key;
2600 old_color_key_flags = src_texture->async.color_key_flags;
2601 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT, color_key);
2603 /* Make sure the surface is up-to-date. This should probably use
2604 * surface_load_location() and worry about the destination surface too,
2605 * unless we're overwriting it completely. */
2606 wined3d_texture_load(src_texture, context, FALSE);
2608 /* Activate the destination context, set it up for blitting. */
2609 context_apply_blit_state(context, device);
2611 if (dst_location == WINED3D_LOCATION_DRAWABLE)
2613 r = *dst_rect;
2614 surface_translate_drawable_coords(dst_surface, context->win_handle, &r);
2615 dst_rect = &r;
2618 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
2620 GLenum buffer;
2622 if (dst_location == WINED3D_LOCATION_DRAWABLE)
2624 TRACE("Destination surface %p is onscreen.\n", dst_surface);
2625 buffer = wined3d_texture_get_gl_buffer(dst_texture);
2627 else
2629 TRACE("Destination surface %p is offscreen.\n", dst_surface);
2630 buffer = GL_COLOR_ATTACHMENT0;
2632 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
2633 context_set_draw_buffer(context, buffer);
2634 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
2635 context_invalidate_state(context, STATE_FRAMEBUFFER);
2638 gl_info->gl_ops.gl.p_glEnable(src_texture->target);
2639 checkGLcall("glEnable(target)");
2641 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST || color_key)
2643 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
2644 checkGLcall("glEnable(GL_ALPHA_TEST)");
2647 if (color_key)
2649 /* For P8 surfaces, the alpha component contains the palette index.
2650 * Which means that the colorkey is one of the palette entries. In
2651 * other cases pixels that should be masked away have alpha set to 0. */
2652 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT)
2653 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
2654 (float)src_texture->async.src_blt_color_key.color_space_low_value / 255.0f);
2655 else
2656 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
2657 checkGLcall("glAlphaFunc");
2660 draw_textured_quad(src_surface, context, src_rect, dst_rect, filter);
2662 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST || color_key)
2664 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2665 checkGLcall("glDisable(GL_ALPHA_TEST)");
2668 /* Leave the OpenGL state valid for blitting. */
2669 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
2670 checkGLcall("glDisable(GL_TEXTURE_2D)");
2671 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
2673 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
2674 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
2676 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
2678 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
2679 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
2682 if (wined3d_settings.strict_draw_ordering
2683 || (dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture))
2684 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2686 /* Restore the color key parameters */
2687 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT,
2688 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
2691 const struct wined3d_blitter_ops ffp_blit =
2693 ffp_blit_alloc,
2694 ffp_blit_free,
2695 ffp_blit_supported,
2696 ffp_blit_clear,
2697 ffp_blit_blit_surface,
2700 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
2702 return WINED3D_OK;
2705 /* Context activation is done by the caller. */
2706 static void cpu_blit_free(struct wined3d_device *device)
2710 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info,
2711 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
2712 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
2713 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
2715 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL || blit_op == WINED3D_BLIT_OP_DEPTH_FILL)
2716 return TRUE;
2718 return FALSE;
2721 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
2722 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
2723 const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx)
2725 UINT row_block_count;
2726 const BYTE *src_row;
2727 BYTE *dst_row;
2728 UINT x, y;
2730 src_row = src_data;
2731 dst_row = dst_data;
2733 row_block_count = (update_w + format->block_width - 1) / format->block_width;
2735 if (!flags)
2737 for (y = 0; y < update_h; y += format->block_height)
2739 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
2740 src_row += src_pitch;
2741 dst_row += dst_pitch;
2744 return WINED3D_OK;
2747 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
2749 src_row += (((update_h / format->block_height) - 1) * src_pitch);
2751 switch (format->id)
2753 case WINED3DFMT_DXT1:
2754 for (y = 0; y < update_h; y += format->block_height)
2756 struct block
2758 WORD color[2];
2759 BYTE control_row[4];
2762 const struct block *s = (const struct block *)src_row;
2763 struct block *d = (struct block *)dst_row;
2765 for (x = 0; x < row_block_count; ++x)
2767 d[x].color[0] = s[x].color[0];
2768 d[x].color[1] = s[x].color[1];
2769 d[x].control_row[0] = s[x].control_row[3];
2770 d[x].control_row[1] = s[x].control_row[2];
2771 d[x].control_row[2] = s[x].control_row[1];
2772 d[x].control_row[3] = s[x].control_row[0];
2774 src_row -= src_pitch;
2775 dst_row += dst_pitch;
2777 return WINED3D_OK;
2779 case WINED3DFMT_DXT2:
2780 case WINED3DFMT_DXT3:
2781 for (y = 0; y < update_h; y += format->block_height)
2783 struct block
2785 WORD alpha_row[4];
2786 WORD color[2];
2787 BYTE control_row[4];
2790 const struct block *s = (const struct block *)src_row;
2791 struct block *d = (struct block *)dst_row;
2793 for (x = 0; x < row_block_count; ++x)
2795 d[x].alpha_row[0] = s[x].alpha_row[3];
2796 d[x].alpha_row[1] = s[x].alpha_row[2];
2797 d[x].alpha_row[2] = s[x].alpha_row[1];
2798 d[x].alpha_row[3] = s[x].alpha_row[0];
2799 d[x].color[0] = s[x].color[0];
2800 d[x].color[1] = s[x].color[1];
2801 d[x].control_row[0] = s[x].control_row[3];
2802 d[x].control_row[1] = s[x].control_row[2];
2803 d[x].control_row[2] = s[x].control_row[1];
2804 d[x].control_row[3] = s[x].control_row[0];
2806 src_row -= src_pitch;
2807 dst_row += dst_pitch;
2809 return WINED3D_OK;
2811 default:
2812 FIXME("Compressed flip not implemented for format %s.\n",
2813 debug_d3dformat(format->id));
2814 return E_NOTIMPL;
2818 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
2819 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
2821 return E_NOTIMPL;
2824 static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
2825 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
2826 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
2827 enum wined3d_texture_filter_type filter)
2829 unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count;
2830 const struct wined3d_format *src_format, *dst_format;
2831 struct wined3d_texture *converted_texture = NULL;
2832 unsigned int src_fmt_flags, dst_fmt_flags;
2833 struct wined3d_map_desc dst_map, src_map;
2834 const BYTE *sbase = NULL;
2835 HRESULT hr = WINED3D_OK;
2836 BOOL same_sub_resource;
2837 const BYTE *sbuf;
2838 BYTE *dbuf;
2839 int x, y;
2841 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
2842 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
2843 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture,
2844 src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
2846 if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx)
2848 same_sub_resource = TRUE;
2849 wined3d_resource_map(&dst_texture->resource, dst_sub_resource_idx, &dst_map, NULL, 0);
2850 src_map = dst_map;
2851 src_format = dst_texture->resource.format;
2852 dst_format = src_format;
2853 dst_fmt_flags = dst_texture->resource.format_flags;
2854 src_fmt_flags = dst_fmt_flags;
2856 else
2858 same_sub_resource = FALSE;
2859 dst_format = dst_texture->resource.format;
2860 dst_fmt_flags = dst_texture->resource.format_flags;
2861 if (src_texture)
2863 if (dst_texture->resource.format->id != src_texture->resource.format->id)
2865 if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format)))
2867 FIXME("Cannot convert %s to %s.\n", debug_d3dformat(src_texture->resource.format->id),
2868 debug_d3dformat(dst_texture->resource.format->id));
2869 return WINED3DERR_NOTAVAILABLE;
2871 src_texture = converted_texture;
2872 src_sub_resource_idx = 0;
2874 wined3d_resource_map(&src_texture->resource, src_sub_resource_idx, &src_map, NULL, WINED3D_MAP_READONLY);
2875 src_format = src_texture->resource.format;
2876 src_fmt_flags = src_texture->resource.format_flags;
2878 else
2880 src_format = dst_format;
2881 src_fmt_flags = dst_fmt_flags;
2884 wined3d_resource_map(&dst_texture->resource, dst_sub_resource_idx, &dst_map, dst_box, 0);
2887 bpp = dst_format->byte_count;
2888 src_height = src_box->bottom - src_box->top;
2889 src_width = src_box->right - src_box->left;
2890 dst_height = dst_box->bottom - dst_box->top;
2891 dst_width = dst_box->right - dst_box->left;
2892 row_byte_count = dst_width * bpp;
2894 if (src_texture)
2895 sbase = (BYTE *)src_map.data
2896 + ((src_box->top / src_format->block_height) * src_map.row_pitch)
2897 + ((src_box->left / src_format->block_width) * src_format->block_byte_count);
2898 if (same_sub_resource)
2899 dbuf = (BYTE *)dst_map.data
2900 + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch)
2901 + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count);
2902 else
2903 dbuf = dst_map.data;
2905 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
2907 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
2909 if (same_sub_resource)
2911 FIXME("Only plain blits supported on compressed surfaces.\n");
2912 hr = E_NOTIMPL;
2913 goto release;
2916 if (src_height != dst_height || src_width != dst_width)
2918 WARN("Stretching not supported on compressed surfaces.\n");
2919 hr = WINED3DERR_INVALIDCALL;
2920 goto release;
2923 hr = surface_cpu_blt_compressed(sbase, dbuf,
2924 src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height,
2925 src_format, flags, fx);
2926 goto release;
2929 /* Now the 'with source' blits. */
2930 if (src_texture)
2932 int sx, xinc, sy, yinc;
2934 if (!dst_width || !dst_height) /* Hmm... stupid program? */
2935 goto release;
2937 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
2938 && (src_width != dst_width || src_height != dst_height))
2940 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
2941 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
2944 xinc = (src_width << 16) / dst_width;
2945 yinc = (src_height << 16) / dst_height;
2947 if (!flags)
2949 /* No effects, we can cheat here. */
2950 if (dst_width == src_width)
2952 if (dst_height == src_height)
2954 /* No stretching in either direction. This needs to be as
2955 * fast as possible. */
2956 sbuf = sbase;
2958 /* Check for overlapping surfaces. */
2959 if (!same_sub_resource || dst_box->top < src_box->top
2960 || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
2962 /* No overlap, or dst above src, so copy from top downwards. */
2963 for (y = 0; y < dst_height; ++y)
2965 memcpy(dbuf, sbuf, row_byte_count);
2966 sbuf += src_map.row_pitch;
2967 dbuf += dst_map.row_pitch;
2970 else if (dst_box->top > src_box->top)
2972 /* Copy from bottom upwards. */
2973 sbuf += src_map.row_pitch * dst_height;
2974 dbuf += dst_map.row_pitch * dst_height;
2975 for (y = 0; y < dst_height; ++y)
2977 sbuf -= src_map.row_pitch;
2978 dbuf -= dst_map.row_pitch;
2979 memcpy(dbuf, sbuf, row_byte_count);
2982 else
2984 /* Src and dst overlapping on the same line, use memmove. */
2985 for (y = 0; y < dst_height; ++y)
2987 memmove(dbuf, sbuf, row_byte_count);
2988 sbuf += src_map.row_pitch;
2989 dbuf += dst_map.row_pitch;
2993 else
2995 /* Stretching in y direction only. */
2996 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
2998 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
2999 memcpy(dbuf, sbuf, row_byte_count);
3000 dbuf += dst_map.row_pitch;
3004 else
3006 /* Stretching in X direction. */
3007 int last_sy = -1;
3008 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3010 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3012 if ((sy >> 16) == (last_sy >> 16))
3014 /* This source row is the same as last source row -
3015 * Copy the already stretched row. */
3016 memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count);
3018 else
3020 #define STRETCH_ROW(type) \
3021 do { \
3022 const type *s = (const type *)sbuf; \
3023 type *d = (type *)dbuf; \
3024 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3025 d[x] = s[sx >> 16]; \
3026 } while(0)
3028 switch(bpp)
3030 case 1:
3031 STRETCH_ROW(BYTE);
3032 break;
3033 case 2:
3034 STRETCH_ROW(WORD);
3035 break;
3036 case 4:
3037 STRETCH_ROW(DWORD);
3038 break;
3039 case 3:
3041 const BYTE *s;
3042 BYTE *d = dbuf;
3043 for (x = sx = 0; x < dst_width; x++, sx+= xinc)
3045 DWORD pixel;
3047 s = sbuf + 3 * (sx >> 16);
3048 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3049 d[0] = (pixel ) & 0xff;
3050 d[1] = (pixel >> 8) & 0xff;
3051 d[2] = (pixel >> 16) & 0xff;
3052 d += 3;
3054 break;
3056 default:
3057 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
3058 hr = WINED3DERR_NOTAVAILABLE;
3059 goto error;
3061 #undef STRETCH_ROW
3063 dbuf += dst_map.row_pitch;
3064 last_sy = sy;
3068 else
3070 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
3071 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
3072 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
3073 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3074 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
3076 /* The color keying flags are checked for correctness in ddraw */
3077 if (flags & WINED3D_BLT_SRC_CKEY)
3079 keylow = src_texture->async.src_blt_color_key.color_space_low_value;
3080 keyhigh = src_texture->async.src_blt_color_key.color_space_high_value;
3082 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3084 keylow = fx->src_color_key.color_space_low_value;
3085 keyhigh = fx->src_color_key.color_space_high_value;
3088 if (flags & WINED3D_BLT_DST_CKEY)
3090 /* Destination color keys are taken from the source surface! */
3091 destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value;
3092 destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value;
3094 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
3096 destkeylow = fx->dst_color_key.color_space_low_value;
3097 destkeyhigh = fx->dst_color_key.color_space_high_value;
3100 if (bpp == 1)
3102 keymask = 0xff;
3104 else
3106 DWORD masks[3];
3107 get_color_masks(src_format, masks);
3108 keymask = masks[0]
3109 | masks[1]
3110 | masks[2];
3112 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3113 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
3116 if (flags & WINED3D_BLT_FX)
3118 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
3119 LONG tmpxy;
3120 dTopLeft = dbuf;
3121 dTopRight = dbuf + ((dst_width - 1) * bpp);
3122 dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
3123 dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
3125 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
3127 /* I don't think we need to do anything about this flag */
3128 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
3130 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
3132 tmp = dTopRight;
3133 dTopRight = dTopLeft;
3134 dTopLeft = tmp;
3135 tmp = dBottomRight;
3136 dBottomRight = dBottomLeft;
3137 dBottomLeft = tmp;
3138 dstxinc = dstxinc * -1;
3140 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
3142 tmp = dTopLeft;
3143 dTopLeft = dBottomLeft;
3144 dBottomLeft = tmp;
3145 tmp = dTopRight;
3146 dTopRight = dBottomRight;
3147 dBottomRight = tmp;
3148 dstyinc = dstyinc * -1;
3150 if (fx->fx & WINEDDBLTFX_NOTEARING)
3152 /* I don't think we need to do anything about this flag */
3153 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
3155 if (fx->fx & WINEDDBLTFX_ROTATE180)
3157 tmp = dBottomRight;
3158 dBottomRight = dTopLeft;
3159 dTopLeft = tmp;
3160 tmp = dBottomLeft;
3161 dBottomLeft = dTopRight;
3162 dTopRight = tmp;
3163 dstxinc = dstxinc * -1;
3164 dstyinc = dstyinc * -1;
3166 if (fx->fx & WINEDDBLTFX_ROTATE270)
3168 tmp = dTopLeft;
3169 dTopLeft = dBottomLeft;
3170 dBottomLeft = dBottomRight;
3171 dBottomRight = dTopRight;
3172 dTopRight = tmp;
3173 tmpxy = dstxinc;
3174 dstxinc = dstyinc;
3175 dstyinc = tmpxy;
3176 dstxinc = dstxinc * -1;
3178 if (fx->fx & WINEDDBLTFX_ROTATE90)
3180 tmp = dTopLeft;
3181 dTopLeft = dTopRight;
3182 dTopRight = dBottomRight;
3183 dBottomRight = dBottomLeft;
3184 dBottomLeft = tmp;
3185 tmpxy = dstxinc;
3186 dstxinc = dstyinc;
3187 dstyinc = tmpxy;
3188 dstyinc = dstyinc * -1;
3190 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
3192 /* I don't think we need to do anything about this flag */
3193 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
3195 dbuf = dTopLeft;
3196 flags &= ~(WINED3D_BLT_FX);
3199 #define COPY_COLORKEY_FX(type) \
3200 do { \
3201 const type *s; \
3202 type *d = (type *)dbuf, *dx, tmp; \
3203 for (y = sy = 0; y < dst_height; ++y, sy += yinc) \
3205 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
3206 dx = d; \
3207 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3209 tmp = s[sx >> 16]; \
3210 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
3211 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
3213 dx[0] = tmp; \
3215 dx = (type *)(((BYTE *)dx) + dstxinc); \
3217 d = (type *)(((BYTE *)d) + dstyinc); \
3219 } while(0)
3221 switch (bpp)
3223 case 1:
3224 COPY_COLORKEY_FX(BYTE);
3225 break;
3226 case 2:
3227 COPY_COLORKEY_FX(WORD);
3228 break;
3229 case 4:
3230 COPY_COLORKEY_FX(DWORD);
3231 break;
3232 case 3:
3234 const BYTE *s;
3235 BYTE *d = dbuf, *dx;
3236 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3238 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3239 dx = d;
3240 for (x = sx = 0; x < dst_width; ++x, sx+= xinc)
3242 DWORD pixel, dpixel = 0;
3243 s = sbuf + 3 * (sx>>16);
3244 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3245 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
3246 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
3247 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
3249 dx[0] = (pixel ) & 0xff;
3250 dx[1] = (pixel >> 8) & 0xff;
3251 dx[2] = (pixel >> 16) & 0xff;
3253 dx += dstxinc;
3255 d += dstyinc;
3257 break;
3259 default:
3260 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
3261 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
3262 hr = WINED3DERR_NOTAVAILABLE;
3263 goto error;
3264 #undef COPY_COLORKEY_FX
3269 error:
3270 if (flags)
3271 FIXME(" Unsupported flags %#x.\n", flags);
3273 release:
3274 wined3d_resource_unmap(&dst_texture->resource, dst_sub_resource_idx);
3275 if (src_texture && !same_sub_resource)
3276 wined3d_resource_unmap(&src_texture->resource, src_sub_resource_idx);
3277 if (converted_texture)
3278 wined3d_texture_decref(converted_texture);
3280 return hr;
3283 static void surface_cpu_blt_colour_fill(struct wined3d_rendertarget_view *view,
3284 const struct wined3d_box *box, const struct wined3d_color *colour)
3286 unsigned int x, y, w, h, bpp;
3287 struct wined3d_map_desc map;
3288 BYTE *row;
3289 DWORD c;
3291 TRACE("view %p, box %s, colour %s.\n", view, debug_box(box), debug_color(colour));
3293 if (view->format_flags & WINED3DFMT_FLAG_BLOCKS)
3295 FIXME("Not implemented for format %s.\n", debug_d3dformat(view->format->id));
3296 return;
3299 c = wined3d_format_convert_from_float(view->format, colour);
3300 bpp = view->format->byte_count;
3301 w = box->right - box->left;
3302 h = box->bottom - box->top;
3304 wined3d_resource_map(view->resource, view->sub_resource_idx, &map, box, 0);
3306 switch (bpp)
3308 case 1:
3309 for (x = 0; x < w; ++x)
3311 ((BYTE *)map.data)[x] = c;
3313 break;
3315 case 2:
3316 for (x = 0; x < w; ++x)
3318 ((WORD *)map.data)[x] = c;
3320 break;
3322 case 3:
3324 row = map.data;
3325 for (x = 0; x < w; ++x, row += 3)
3327 row[0] = (c ) & 0xff;
3328 row[1] = (c >> 8) & 0xff;
3329 row[2] = (c >> 16) & 0xff;
3331 break;
3333 case 4:
3334 for (x = 0; x < w; ++x)
3336 ((DWORD *)map.data)[x] = c;
3338 break;
3340 default:
3341 FIXME("Not implemented for bpp %u.\n", bpp);
3342 wined3d_resource_unmap(view->resource, view->sub_resource_idx);
3343 return;
3346 row = map.data;
3347 for (y = 1; y < h; ++y)
3349 row += map.row_pitch;
3350 memcpy(row, map.data, w * bpp);
3352 wined3d_resource_unmap(view->resource, view->sub_resource_idx);
3355 static void cpu_blit_clear(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
3356 const RECT *rect, DWORD flags, const struct wined3d_color *colour, float depth, DWORD stencil)
3358 const struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
3359 struct wined3d_color c = {depth, 0.0f, 0.0f, 0.0f};
3361 if (flags == WINED3DCLEAR_TARGET)
3363 surface_cpu_blt_colour_fill(view, &box, colour);
3364 return;
3367 if (flags == WINED3DCLEAR_ZBUFFER)
3369 surface_cpu_blt_colour_fill(view, &box, &c);
3370 return;
3373 FIXME("flags %#x not implemented.\n", flags);
3376 static void cpu_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op,
3377 struct wined3d_context *context, struct wined3d_surface *src_surface, DWORD src_location,
3378 const RECT *src_rect, struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect,
3379 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter)
3381 /* FIXME: Remove error returns from surface_blt_cpu. */
3382 ERR("Blit method not implemented by cpu_blit.\n");
3385 const struct wined3d_blitter_ops cpu_blit =
3387 cpu_blit_alloc,
3388 cpu_blit_free,
3389 cpu_blit_supported,
3390 cpu_blit_clear,
3391 cpu_blit_blit_surface,
3394 HRESULT wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3395 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
3396 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
3398 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
3399 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
3400 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
3401 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
3402 struct wined3d_texture *dst_texture = dst_surface->container;
3403 struct wined3d_texture *src_texture = src_surface->container;
3404 struct wined3d_device *device = dst_texture->resource.device;
3405 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3406 DWORD src_ds_flags, dst_ds_flags;
3407 BOOL scale, convert;
3409 static const DWORD simple_blit = WINED3D_BLT_ASYNC
3410 | WINED3D_BLT_SRC_CKEY
3411 | WINED3D_BLT_SRC_CKEY_OVERRIDE
3412 | WINED3D_BLT_WAIT
3413 | WINED3D_BLT_DO_NOT_WAIT
3414 | WINED3D_BLT_ALPHA_TEST;
3416 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
3417 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3418 flags, fx, debug_d3dtexturefiltertype(filter));
3419 TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage));
3421 if (fx)
3423 TRACE("fx %#x.\n", fx->fx);
3424 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
3425 fx->dst_color_key.color_space_low_value,
3426 fx->dst_color_key.color_space_high_value);
3427 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
3428 fx->src_color_key.color_space_low_value,
3429 fx->src_color_key.color_space_high_value);
3432 if (!fx || !(fx->fx))
3433 flags &= ~WINED3D_BLT_FX;
3435 if (flags & WINED3D_BLT_WAIT)
3436 flags &= ~WINED3D_BLT_WAIT;
3438 if (flags & WINED3D_BLT_ASYNC)
3440 static unsigned int once;
3442 if (!once++)
3443 FIXME("Can't handle WINED3D_BLT_ASYNC flag.\n");
3444 flags &= ~WINED3D_BLT_ASYNC;
3447 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
3448 if (flags & WINED3D_BLT_DO_NOT_WAIT)
3450 static unsigned int once;
3452 if (!once++)
3453 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
3454 flags &= ~WINED3D_BLT_DO_NOT_WAIT;
3457 if (!device->d3d_initialized)
3459 WARN("D3D not initialized, using fallback.\n");
3460 goto cpu;
3463 /* We want to avoid invalidating the sysmem location for converted
3464 * surfaces, since otherwise we'd have to convert the data back when
3465 * locking them. */
3466 if (dst_texture->flags & WINED3D_TEXTURE_CONVERTED || dst_texture->resource.format->convert
3467 || wined3d_format_get_color_key_conversion(dst_texture, TRUE))
3469 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
3470 goto cpu;
3473 if (flags & ~simple_blit)
3475 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
3476 goto fallback;
3479 src_swapchain = src_texture->swapchain;
3480 dst_swapchain = dst_texture->swapchain;
3482 /* This isn't strictly needed. FBO blits for example could deal with
3483 * cross-swapchain blits by first downloading the source to a texture
3484 * before switching to the destination context. We just have this here to
3485 * not have to deal with the issue, since cross-swapchain blits should be
3486 * rare. */
3487 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
3489 FIXME("Using fallback for cross-swapchain blit.\n");
3490 goto fallback;
3493 scale = src_rect->right - src_rect->left != dst_rect->right - dst_rect->left
3494 || src_rect->bottom - src_rect->top != dst_rect->bottom - dst_rect->top;
3495 convert = src_texture->resource.format->id != dst_texture->resource.format->id;
3497 dst_ds_flags = dst_texture->resource.format_flags
3498 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
3499 src_ds_flags = src_texture->resource.format_flags
3500 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
3502 if (src_ds_flags || dst_ds_flags)
3504 const struct wined3d_blitter_ops *blitter;
3505 struct wined3d_context *context;
3507 TRACE("Depth/stencil blit.\n");
3509 if ((blitter = wined3d_select_blitter(&device->adapter->gl_info,
3510 &device->adapter->d3d_info, WINED3D_BLIT_OP_DEPTH_BLIT,
3511 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
3512 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format)))
3514 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3515 blitter->blit_surface(device, WINED3D_BLIT_OP_DEPTH_BLIT, context,
3516 src_surface, src_texture->resource.draw_binding, src_rect,
3517 dst_surface, dst_texture->resource.draw_binding, dst_rect,
3518 NULL, filter);
3519 context_release(context);
3521 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx,
3522 dst_texture->resource.draw_binding);
3523 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx,
3524 ~dst_texture->resource.draw_binding);
3526 return WINED3D_OK;
3529 else
3531 struct wined3d_texture_sub_resource *src_sub_resource, *dst_sub_resource;
3532 enum wined3d_blit_op blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
3533 const struct wined3d_color_key *colour_key = NULL;
3534 const struct wined3d_blitter_ops *blitter;
3536 TRACE("Colour blit.\n");
3538 dst_sub_resource = surface_get_sub_resource(dst_surface);
3539 src_sub_resource = &src_texture->sub_resources[src_sub_resource_idx];
3541 /* In principle this would apply to depth blits as well, but we don't
3542 * implement those in the CPU blitter at the moment. */
3543 if ((dst_sub_resource->locations & dst_texture->resource.map_binding)
3544 && (src_sub_resource->locations & src_texture->resource.map_binding))
3546 if (scale)
3547 TRACE("Not doing sysmem blit because of scaling.\n");
3548 else if (convert)
3549 TRACE("Not doing sysmem blit because of format conversion.\n");
3550 else
3551 goto cpu;
3554 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3556 colour_key = &fx->src_color_key;
3557 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
3559 else if (flags & WINED3D_BLT_SRC_CKEY)
3561 colour_key = &src_texture->async.src_blt_color_key;
3562 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
3564 else if (flags & WINED3D_BLT_ALPHA_TEST)
3566 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
3568 else if ((src_sub_resource->locations & WINED3D_LOCATION_SYSMEM)
3569 && !(dst_sub_resource->locations & WINED3D_LOCATION_SYSMEM))
3571 /* Upload */
3572 if (scale)
3573 TRACE("Not doing upload because of scaling.\n");
3574 else if (convert)
3575 TRACE("Not doing upload because of format conversion.\n");
3576 else
3578 POINT dst_point = {dst_rect->left, dst_rect->top};
3580 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, src_rect)))
3582 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
3584 struct wined3d_context *context = context_acquire(device,
3585 dst_texture, dst_sub_resource_idx);
3586 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx,
3587 context, dst_texture->resource.draw_binding);
3588 context_release(context);
3590 return WINED3D_OK;
3594 else if (dst_swapchain && dst_swapchain->back_buffers
3595 && dst_texture == dst_swapchain->front_buffer
3596 && src_texture == dst_swapchain->back_buffers[0])
3598 /* Use present for back -> front blits. The idea behind this is
3599 * that present is potentially faster than a blit, in particular
3600 * when FBO blits aren't available. Some ddraw applications like
3601 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
3602 * to the frontbuffer instead of doing a Flip(). D3d8 and d3d9
3603 * applications can't blit directly to the frontbuffer. */
3604 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
3606 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
3608 /* Set the swap effect to COPY, we don't want the backbuffer to
3609 * become undefined. */
3610 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
3611 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, 0);
3612 dst_swapchain->desc.swap_effect = swap_effect;
3614 return WINED3D_OK;
3617 if ((blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, blit_op,
3618 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
3619 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format)))
3621 struct wined3d_context *context;
3623 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3624 blitter->blit_surface(device, blit_op, context,
3625 src_surface, src_texture->resource.draw_binding, src_rect,
3626 dst_surface, dst_texture->resource.draw_binding, dst_rect,
3627 colour_key, filter);
3628 context_release(context);
3630 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx,
3631 dst_texture->resource.draw_binding);
3632 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx,
3633 ~dst_texture->resource.draw_binding);
3635 return WINED3D_OK;
3639 fallback:
3640 /* Special cases for render targets. */
3641 if (SUCCEEDED(surface_blt_special(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter)))
3642 return WINED3D_OK;
3644 cpu:
3645 return surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
3646 src_texture, src_sub_resource_idx, &src_box, flags, fx, filter);