wined3d: Get rid of wined3d_select_blitter().
[wine.git] / dlls / wined3d / surface.c
blob35d540e77b4d32846c13eb22bb2009bd911dba73
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, enum wined3d_blit_op blit_op,
501 DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
502 DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
504 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
505 return FALSE;
507 /* Source and/or destination need to be on the GL side */
508 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
509 return FALSE;
511 switch (blit_op)
513 case WINED3D_BLIT_OP_COLOR_BLIT:
514 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
515 || (src_usage & WINED3DUSAGE_RENDERTARGET)))
516 return FALSE;
517 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
518 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
519 return FALSE;
520 if (!(src_format->id == dst_format->id
521 || (is_identity_fixup(src_format->color_fixup)
522 && is_identity_fixup(dst_format->color_fixup))))
523 return FALSE;
524 break;
526 case WINED3D_BLIT_OP_DEPTH_BLIT:
527 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
528 return FALSE;
529 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
530 return FALSE;
531 /* Accept pure swizzle fixups for depth formats. In general we
532 * ignore the stencil component (if present) at the moment and the
533 * swizzle is not relevant with just the depth component. */
534 if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup)
535 || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup))
536 return FALSE;
537 break;
539 default:
540 return FALSE;
543 return TRUE;
546 /* This call just downloads data, the caller is responsible for binding the
547 * correct texture. */
548 /* Context activation is done by the caller. */
549 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
550 DWORD dst_location)
552 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
553 struct wined3d_texture *texture = surface->container;
554 const struct wined3d_format *format = texture->resource.format;
555 struct wined3d_texture_sub_resource *sub_resource;
556 unsigned int dst_row_pitch, dst_slice_pitch;
557 unsigned int src_row_pitch, src_slice_pitch;
558 struct wined3d_bo_address data;
559 BYTE *temporary_mem = NULL;
560 void *mem;
562 /* Only support read back of converted P8 surfaces. */
563 if (texture->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
565 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
566 return;
569 sub_resource = &texture->sub_resources[sub_resource_idx];
571 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
573 /* NP2 emulation is not allowed on array textures. */
574 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
575 ERR("Array texture %p uses NP2 emulation.\n", texture);
577 WARN_(d3d_perf)("Downloading all miplevel layers to get the surface data for a single sub-resource.\n");
579 if (!(temporary_mem = wined3d_calloc(texture->layer_count, sub_resource->size)))
581 ERR("Out of memory.\n");
582 return;
586 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
588 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
590 wined3d_texture_get_pitch(texture, surface->texture_level, &dst_row_pitch, &dst_slice_pitch);
591 wined3d_format_calculate_pitch(format, texture->resource.device->surface_alignment,
592 wined3d_texture_get_level_pow2_width(texture, surface->texture_level),
593 wined3d_texture_get_level_pow2_height(texture, surface->texture_level),
594 &src_row_pitch, &src_slice_pitch);
595 if (!(temporary_mem = HeapAlloc(GetProcessHeap(), 0, src_slice_pitch)))
597 ERR("Out of memory.\n");
598 return;
601 if (data.buffer_object)
602 ERR("NP2 emulated texture uses PBO unexpectedly.\n");
603 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
604 ERR("Unexpected compressed format for NP2 emulated texture.\n");
607 if (temporary_mem)
609 mem = temporary_mem;
611 else if (data.buffer_object)
613 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
614 checkGLcall("glBindBuffer");
615 mem = data.addr;
617 else
619 mem = data.addr;
622 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
624 TRACE("Downloading compressed surface %p, level %u, format %#x, type %#x, data %p.\n",
625 surface, surface->texture_level, format->glFormat, format->glType, mem);
627 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target, surface->texture_level, mem));
628 checkGLcall("glGetCompressedTexImage");
630 else
632 TRACE("Downloading surface %p, level %u, format %#x, type %#x, data %p.\n",
633 surface, surface->texture_level, format->glFormat, format->glType, mem);
635 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
636 format->glFormat, format->glType, mem);
637 checkGLcall("glGetTexImage");
640 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
642 const BYTE *src_data;
643 unsigned int h, y;
644 BYTE *dst_data;
646 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
647 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
648 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
650 * We're doing this...
652 * instead of boxing the texture :
653 * |<-texture width ->| -->pow2width| /\
654 * |111111111111111111| | |
655 * |222 Texture 222222| boxed empty | texture height
656 * |3333 Data 33333333| | |
657 * |444444444444444444| | \/
658 * ----------------------------------- |
659 * | boxed empty | boxed empty | pow2height
660 * | | | \/
661 * -----------------------------------
664 * we're repacking the data to the expected texture width
666 * |<-texture width ->| -->pow2width| /\
667 * |111111111111111111222222222222222| |
668 * |222333333333333333333444444444444| texture height
669 * |444444 | |
670 * | | \/
671 * | | |
672 * | empty | pow2height
673 * | | \/
674 * -----------------------------------
676 * == is the same as
678 * |<-texture width ->| /\
679 * |111111111111111111|
680 * |222222222222222222|texture height
681 * |333333333333333333|
682 * |444444444444444444| \/
683 * --------------------
685 * This also means that any references to surface memory should work with the data as if it were a
686 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
688 * internally the texture is still stored in a boxed format so any references to textureName will
689 * get a boxed texture with width pow2width and not a texture of width resource.width. */
690 src_data = mem;
691 dst_data = data.addr;
692 TRACE("Repacking the surface data from pitch %u to pitch %u.\n", src_row_pitch, dst_row_pitch);
693 h = wined3d_texture_get_level_height(texture, surface->texture_level);
694 for (y = 0; y < h; ++y)
696 memcpy(dst_data, src_data, dst_row_pitch);
697 src_data += src_row_pitch;
698 dst_data += dst_row_pitch;
701 else if (temporary_mem)
703 void *src_data = temporary_mem + surface->texture_layer * sub_resource->size;
704 if (data.buffer_object)
706 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
707 checkGLcall("glBindBuffer");
708 GL_EXTCALL(glBufferSubData(GL_PIXEL_PACK_BUFFER, 0, sub_resource->size, src_data));
709 checkGLcall("glBufferSubData");
711 else
713 memcpy(data.addr, src_data, sub_resource->size);
717 if (data.buffer_object)
719 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
720 checkGLcall("glBindBuffer");
723 HeapFree(GetProcessHeap(), 0, temporary_mem);
726 /* This call just uploads data, the caller is responsible for binding the
727 * correct texture. */
728 /* Context activation is done by the caller. */
729 void wined3d_surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
730 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
731 BOOL srgb, const struct wined3d_const_bo_address *data)
733 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
734 struct wined3d_texture *texture = surface->container;
735 UINT update_w = src_rect->right - src_rect->left;
736 UINT update_h = src_rect->bottom - src_rect->top;
738 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
739 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
740 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
742 if (texture->sub_resources[sub_resource_idx].map_count)
744 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
745 texture->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
748 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE)
750 update_h *= format->height_scale.numerator;
751 update_h /= format->height_scale.denominator;
754 if (data->buffer_object)
756 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
757 checkGLcall("glBindBuffer");
760 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
762 unsigned int dst_row_pitch, dst_slice_pitch;
763 const BYTE *addr = data->addr;
764 GLenum internal;
766 addr += (src_rect->top / format->block_height) * src_pitch;
767 addr += (src_rect->left / format->block_width) * format->block_byte_count;
769 if (srgb)
770 internal = format->glGammaInternal;
771 else if (texture->resource.usage & WINED3DUSAGE_RENDERTARGET
772 && wined3d_resource_is_offscreen(&texture->resource))
773 internal = format->rtInternal;
774 else
775 internal = format->glInternal;
777 wined3d_format_calculate_pitch(format, 1, update_w, update_h, &dst_row_pitch, &dst_slice_pitch);
779 TRACE("Uploading compressed data, target %#x, level %u, layer %u, x %d, y %d, w %u, h %u, "
780 "format %#x, image_size %#x, addr %p.\n",
781 surface->texture_target, surface->texture_level, surface->texture_layer,
782 dst_point->x, dst_point->y, update_w, update_h, internal, dst_slice_pitch, addr);
784 if (dst_row_pitch == src_pitch)
786 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
788 GL_EXTCALL(glCompressedTexSubImage3D(surface->texture_target, surface->texture_level,
789 dst_point->x, dst_point->y, surface->texture_layer, update_w, update_h, 1,
790 internal, dst_slice_pitch, addr));
792 else
794 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
795 dst_point->x, dst_point->y, update_w, update_h,
796 internal, dst_slice_pitch, addr));
799 else
801 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
802 UINT row, y;
804 /* glCompressedTexSubImage2D() ignores pixel store state, so we
805 * can't use the unpack row length like for glTexSubImage2D. */
806 for (row = 0, y = dst_point->y; row < row_count; ++row)
808 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
810 GL_EXTCALL(glCompressedTexSubImage3D(surface->texture_target, surface->texture_level,
811 dst_point->x, y, surface->texture_layer, update_w, format->block_height, 1,
812 internal, dst_row_pitch, addr));
814 else
816 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
817 dst_point->x, y, update_w, format->block_height, internal, dst_row_pitch, addr));
820 y += format->block_height;
821 addr += src_pitch;
824 checkGLcall("Upload compressed surface data");
826 else
828 const BYTE *addr = data->addr;
830 addr += src_rect->top * src_pitch;
831 addr += src_rect->left * format->byte_count;
833 TRACE("Uploading data, target %#x, level %u, layer %u, x %d, y %d, w %u, h %u, "
834 "format %#x, type %#x, addr %p.\n",
835 surface->texture_target, surface->texture_level, surface->texture_layer,
836 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
838 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
839 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
841 GL_EXTCALL(glTexSubImage3D(surface->texture_target, surface->texture_level,
842 dst_point->x, dst_point->y, surface->texture_layer, update_w, update_h, 1,
843 format->glFormat, format->glType, addr));
845 else
847 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
848 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
850 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
851 checkGLcall("Upload surface data");
854 if (data->buffer_object)
856 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
857 checkGLcall("glBindBuffer");
860 if (wined3d_settings.strict_draw_ordering)
861 gl_info->gl_ops.gl.p_glFlush();
863 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
865 struct wined3d_device *device = texture->resource.device;
866 unsigned int i;
868 for (i = 0; i < device->context_count; ++i)
870 context_surface_update(device->contexts[i], surface);
875 static BOOL wined3d_surface_check_rect_dimensions(struct wined3d_surface *surface, const RECT *rect)
877 struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
879 return SUCCEEDED(wined3d_texture_check_box_dimensions(surface->container, surface->texture_level, &box));
882 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
883 struct wined3d_surface *src_surface, const RECT *src_rect)
885 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
886 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
887 struct wined3d_texture *src_texture = src_surface->container;
888 struct wined3d_texture *dst_texture = dst_surface->container;
889 unsigned int src_row_pitch, src_slice_pitch;
890 const struct wined3d_format *src_format;
891 const struct wined3d_format *dst_format;
892 const struct wined3d_gl_info *gl_info;
893 struct wined3d_context *context;
894 struct wined3d_bo_address data;
895 UINT update_w, update_h;
896 RECT r, dst_rect;
897 POINT p;
899 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
900 dst_surface, wine_dbgstr_point(dst_point),
901 src_surface, wine_dbgstr_rect(src_rect));
903 src_format = src_texture->resource.format;
904 dst_format = dst_texture->resource.format;
906 if (src_format->id != dst_format->id)
908 WARN("Source and destination surfaces should have the same format.\n");
909 return WINED3DERR_INVALIDCALL;
912 if (!dst_point)
914 p.x = 0;
915 p.y = 0;
916 dst_point = &p;
918 else if (dst_point->x < 0 || dst_point->y < 0)
920 WARN("Invalid destination point.\n");
921 return WINED3DERR_INVALIDCALL;
924 if (!src_rect)
926 SetRect(&r, 0, 0, wined3d_texture_get_level_width(src_texture, src_surface->texture_level),
927 wined3d_texture_get_level_height(src_texture, src_surface->texture_level));
928 src_rect = &r;
931 if (!wined3d_surface_check_rect_dimensions(src_surface, src_rect))
933 WARN("Source rectangle not block-aligned.\n");
934 return WINED3DERR_INVALIDCALL;
937 update_w = src_rect->right - src_rect->left;
938 update_h = src_rect->bottom - src_rect->top;
939 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
940 if (!wined3d_surface_check_rect_dimensions(dst_surface, &dst_rect))
942 WARN("Destination rectangle not block-aligned.\n");
943 return WINED3DERR_INVALIDCALL;
946 /* Use wined3d_texture_blt() instead of uploading directly if we need conversion. */
947 if (dst_format->convert || wined3d_format_get_color_key_conversion(dst_texture, FALSE))
948 return wined3d_texture_blt(dst_texture, dst_sub_resource_idx, &dst_rect,
949 src_texture, src_sub_resource_idx, src_rect, 0, NULL, WINED3D_TEXF_POINT);
951 context = context_acquire(dst_texture->resource.device, NULL, 0);
952 gl_info = context->gl_info;
954 /* Only load the surface for partial updates. For newly allocated texture
955 * the texture wouldn't be the current location, and we'd upload zeroes
956 * just to overwrite them again. */
957 if (update_w == wined3d_texture_get_level_width(dst_texture, dst_surface->texture_level)
958 && update_h == wined3d_texture_get_level_height(dst_texture, dst_surface->texture_level))
959 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
960 else
961 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
962 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
964 wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &data,
965 src_texture->sub_resources[src_sub_resource_idx].locations);
966 wined3d_texture_get_pitch(src_texture, src_surface->texture_level, &src_row_pitch, &src_slice_pitch);
968 wined3d_surface_upload_data(dst_surface, gl_info, src_format, src_rect,
969 src_row_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
971 context_release(context);
973 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
974 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
976 return WINED3D_OK;
979 /* In D3D the depth stencil dimensions have to be greater than or equal to the
980 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
981 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
982 /* Context activation is done by the caller. */
983 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
985 const struct wined3d_gl_info *gl_info = &surface->container->resource.device->adapter->gl_info;
986 struct wined3d_renderbuffer_entry *entry;
987 GLuint renderbuffer = 0;
988 unsigned int src_width, src_height;
989 unsigned int width, height;
991 if (rt && rt->container->resource.format->id != WINED3DFMT_NULL)
993 width = wined3d_texture_get_level_pow2_width(rt->container, rt->texture_level);
994 height = wined3d_texture_get_level_pow2_height(rt->container, rt->texture_level);
996 else
998 width = wined3d_texture_get_level_pow2_width(surface->container, surface->texture_level);
999 height = wined3d_texture_get_level_pow2_height(surface->container, surface->texture_level);
1002 src_width = wined3d_texture_get_level_pow2_width(surface->container, surface->texture_level);
1003 src_height = wined3d_texture_get_level_pow2_height(surface->container, surface->texture_level);
1005 /* A depth stencil smaller than the render target is not valid */
1006 if (width > src_width || height > src_height) return;
1008 /* Remove any renderbuffer set if the sizes match */
1009 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1010 || (width == src_width && height == src_height))
1012 surface->current_renderbuffer = NULL;
1013 return;
1016 /* Look if we've already got a renderbuffer of the correct dimensions */
1017 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1019 if (entry->width == width && entry->height == height)
1021 renderbuffer = entry->id;
1022 surface->current_renderbuffer = entry;
1023 break;
1027 if (!renderbuffer)
1029 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1030 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1031 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1032 surface->container->resource.format->glInternal, width, height);
1034 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1035 entry->width = width;
1036 entry->height = height;
1037 entry->id = renderbuffer;
1038 list_add_head(&surface->renderbuffers, &entry->entry);
1040 surface->current_renderbuffer = entry;
1043 checkGLcall("set_compatible_renderbuffer");
1046 /* See also float_16_to_32() in wined3d_private.h */
1047 static inline unsigned short float_32_to_16(const float *in)
1049 int exp = 0;
1050 float tmp = fabsf(*in);
1051 unsigned int mantissa;
1052 unsigned short ret;
1054 /* Deal with special numbers */
1055 if (*in == 0.0f)
1056 return 0x0000;
1057 if (isnan(*in))
1058 return 0x7c01;
1059 if (isinf(*in))
1060 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1062 if (tmp < (float)(1u << 10))
1066 tmp = tmp * 2.0f;
1067 exp--;
1068 } while (tmp < (float)(1u << 10));
1070 else if (tmp >= (float)(1u << 11))
1074 tmp /= 2.0f;
1075 exp++;
1076 } while (tmp >= (float)(1u << 11));
1079 mantissa = (unsigned int)tmp;
1080 if (tmp - mantissa >= 0.5f)
1081 ++mantissa; /* Round to nearest, away from zero. */
1083 exp += 10; /* Normalize the mantissa. */
1084 exp += 15; /* Exponent is encoded with excess 15. */
1086 if (exp > 30) /* too big */
1088 ret = 0x7c00; /* INF */
1090 else if (exp <= 0)
1092 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1093 while (exp <= 0)
1095 mantissa = mantissa >> 1;
1096 ++exp;
1098 ret = mantissa & 0x3ff;
1100 else
1102 ret = (exp << 10) | (mantissa & 0x3ff);
1105 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1106 return ret;
1109 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
1110 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1112 unsigned short *dst_s;
1113 const float *src_f;
1114 unsigned int x, y;
1116 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1118 for (y = 0; y < h; ++y)
1120 src_f = (const float *)(src + y * pitch_in);
1121 dst_s = (unsigned short *) (dst + y * pitch_out);
1122 for (x = 0; x < w; ++x)
1124 dst_s[x] = float_32_to_16(src_f + x);
1129 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
1130 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1132 static const unsigned char convert_5to8[] =
1134 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
1135 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
1136 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
1137 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
1139 static const unsigned char convert_6to8[] =
1141 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
1142 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
1143 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
1144 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
1145 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
1146 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
1147 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
1148 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
1150 unsigned int x, y;
1152 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1154 for (y = 0; y < h; ++y)
1156 const WORD *src_line = (const WORD *)(src + y * pitch_in);
1157 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1158 for (x = 0; x < w; ++x)
1160 WORD pixel = src_line[x];
1161 dst_line[x] = 0xff000000u
1162 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
1163 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
1164 | convert_5to8[(pixel & 0x001fu)];
1169 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
1170 * in both cases we're just setting the X / Alpha channel to 0xff. */
1171 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
1172 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1174 unsigned int x, y;
1176 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1178 for (y = 0; y < h; ++y)
1180 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
1181 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1183 for (x = 0; x < w; ++x)
1185 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
1190 static inline BYTE cliptobyte(int x)
1192 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
1195 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
1196 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1198 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1199 unsigned int x, y;
1201 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1203 for (y = 0; y < h; ++y)
1205 const BYTE *src_line = src + y * pitch_in;
1206 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1207 for (x = 0; x < w; ++x)
1209 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1210 * C = Y - 16; D = U - 128; E = V - 128;
1211 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1212 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1213 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1214 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1215 * U and V are shared between the pixels. */
1216 if (!(x & 1)) /* For every even pixel, read new U and V. */
1218 d = (int) src_line[1] - 128;
1219 e = (int) src_line[3] - 128;
1220 r2 = 409 * e + 128;
1221 g2 = - 100 * d - 208 * e + 128;
1222 b2 = 516 * d + 128;
1224 c2 = 298 * ((int) src_line[0] - 16);
1225 dst_line[x] = 0xff000000
1226 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
1227 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
1228 | cliptobyte((c2 + b2) >> 8); /* blue */
1229 /* Scale RGB values to 0..255 range,
1230 * then clip them if still not in range (may be negative),
1231 * then shift them within DWORD if necessary. */
1232 src_line += 2;
1237 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
1238 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1240 unsigned int x, y;
1241 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1243 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
1245 for (y = 0; y < h; ++y)
1247 const BYTE *src_line = src + y * pitch_in;
1248 WORD *dst_line = (WORD *)(dst + y * pitch_out);
1249 for (x = 0; x < w; ++x)
1251 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1252 * C = Y - 16; D = U - 128; E = V - 128;
1253 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1254 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1255 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1256 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1257 * U and V are shared between the pixels. */
1258 if (!(x & 1)) /* For every even pixel, read new U and V. */
1260 d = (int) src_line[1] - 128;
1261 e = (int) src_line[3] - 128;
1262 r2 = 409 * e + 128;
1263 g2 = - 100 * d - 208 * e + 128;
1264 b2 = 516 * d + 128;
1266 c2 = 298 * ((int) src_line[0] - 16);
1267 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
1268 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
1269 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
1270 /* Scale RGB values to 0..255 range,
1271 * then clip them if still not in range (may be negative),
1272 * then shift them within DWORD if necessary. */
1273 src_line += 2;
1278 struct d3dfmt_converter_desc
1280 enum wined3d_format_id from, to;
1281 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
1284 static const struct d3dfmt_converter_desc converters[] =
1286 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
1287 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
1288 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1289 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1290 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
1291 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
1294 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
1295 enum wined3d_format_id to)
1297 unsigned int i;
1299 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
1301 if (converters[i].from == from && converters[i].to == to)
1302 return &converters[i];
1305 return NULL;
1308 static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture,
1309 unsigned int sub_resource_idx, const struct wined3d_format *dst_format)
1311 unsigned int texture_level = sub_resource_idx % src_texture->level_count;
1312 const struct wined3d_format *src_format = src_texture->resource.format;
1313 struct wined3d_device *device = src_texture->resource.device;
1314 const struct d3dfmt_converter_desc *conv = NULL;
1315 struct wined3d_texture *dst_texture;
1316 struct wined3d_resource_desc desc;
1317 struct wined3d_map_desc src_map;
1319 if (!(conv = find_converter(src_format->id, dst_format->id)) && (!device->d3d_initialized
1320 || !is_identity_fixup(src_format->color_fixup) || src_format->convert
1321 || !is_identity_fixup(dst_format->color_fixup) || dst_format->convert
1322 || (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)))
1324 FIXME("Cannot find a conversion function from format %s to %s.\n",
1325 debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
1326 return NULL;
1329 /* FIXME: Multisampled conversion? */
1330 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
1331 desc.format = dst_format->id;
1332 desc.multisample_type = WINED3D_MULTISAMPLE_NONE;
1333 desc.multisample_quality = 0;
1334 desc.usage = 0;
1335 desc.pool = WINED3D_POOL_SCRATCH;
1336 desc.width = wined3d_texture_get_level_width(src_texture, texture_level);
1337 desc.height = wined3d_texture_get_level_height(src_texture, texture_level);
1338 desc.depth = 1;
1339 desc.size = 0;
1340 if (FAILED(wined3d_texture_create(device, &desc, 1, 1,
1341 WINED3D_TEXTURE_CREATE_MAPPABLE | WINED3D_TEXTURE_CREATE_DISCARD,
1342 NULL, NULL, &wined3d_null_parent_ops, &dst_texture)))
1344 ERR("Failed to create a destination texture for conversion.\n");
1345 return NULL;
1348 memset(&src_map, 0, sizeof(src_map));
1349 if (FAILED(wined3d_resource_map(&src_texture->resource, sub_resource_idx,
1350 &src_map, NULL, WINED3D_MAP_READONLY)))
1352 ERR("Failed to map the source texture.\n");
1353 wined3d_texture_decref(dst_texture);
1354 return NULL;
1356 if (conv)
1358 struct wined3d_map_desc dst_map;
1360 memset(&dst_map, 0, sizeof(dst_map));
1361 if (FAILED(wined3d_resource_map(&dst_texture->resource, 0, &dst_map, NULL, 0)))
1363 ERR("Failed to map the destination texture.\n");
1364 wined3d_resource_unmap(&src_texture->resource, sub_resource_idx);
1365 wined3d_texture_decref(dst_texture);
1366 return NULL;
1369 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch, desc.width, desc.height);
1371 wined3d_resource_unmap(&dst_texture->resource, 0);
1373 else
1375 struct wined3d_bo_address data = {0, src_map.data};
1376 RECT src_rect = {0, 0, desc.width, desc.height};
1377 const struct wined3d_gl_info *gl_info;
1378 struct wined3d_context *context;
1379 POINT dst_point = {0, 0};
1381 TRACE("Using upload conversion.\n");
1382 context = context_acquire(device, NULL, 0);
1383 gl_info = context->gl_info;
1385 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
1386 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
1387 wined3d_surface_upload_data(dst_texture->sub_resources[0].u.surface, gl_info, src_format,
1388 &src_rect, src_map.row_pitch, &dst_point, FALSE, wined3d_const_bo_address(&data));
1390 context_release(context);
1392 wined3d_texture_validate_location(dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB);
1393 wined3d_texture_invalidate_location(dst_texture, 0, ~WINED3D_LOCATION_TEXTURE_RGB);
1395 wined3d_resource_unmap(&src_texture->resource, sub_resource_idx);
1397 return dst_texture;
1400 static void read_from_framebuffer(struct wined3d_surface *surface,
1401 struct wined3d_context *old_ctx, DWORD dst_location)
1403 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
1404 struct wined3d_texture *texture = surface->container;
1405 struct wined3d_device *device = texture->resource.device;
1406 const struct wined3d_gl_info *gl_info;
1407 struct wined3d_context *context = old_ctx;
1408 struct wined3d_surface *restore_rt = NULL;
1409 unsigned int row_pitch, slice_pitch;
1410 unsigned int width, height;
1411 BYTE *mem;
1412 BYTE *row, *top, *bottom;
1413 int i;
1414 BOOL srcIsUpsideDown;
1415 struct wined3d_bo_address data;
1417 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
1419 restore_rt = context_get_rt_surface(old_ctx);
1420 if (restore_rt != surface)
1421 context = context_acquire(device, texture, sub_resource_idx);
1422 else
1423 restore_rt = NULL;
1425 context_apply_blit_state(context, device);
1426 gl_info = context->gl_info;
1428 /* Select the correct read buffer, and give some debug output.
1429 * There is no need to keep track of the current read buffer or reset it, every part of the code
1430 * that reads sets the read buffer as desired.
1432 if (wined3d_resource_is_offscreen(&texture->resource))
1434 /* Mapping the primary render target which is not on a swapchain.
1435 * Read from the back buffer. */
1436 TRACE("Mapping offscreen render target.\n");
1437 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1438 srcIsUpsideDown = TRUE;
1440 else
1442 /* Onscreen surfaces are always part of a swapchain */
1443 GLenum buffer = wined3d_texture_get_gl_buffer(texture);
1444 TRACE("Mapping %#x buffer.\n", buffer);
1445 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
1446 checkGLcall("glReadBuffer");
1447 srcIsUpsideDown = FALSE;
1450 if (data.buffer_object)
1452 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1453 checkGLcall("glBindBuffer");
1456 wined3d_texture_get_pitch(texture, surface->texture_level, &row_pitch, &slice_pitch);
1458 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1459 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / texture->resource.format->byte_count);
1460 checkGLcall("glPixelStorei");
1462 width = wined3d_texture_get_level_width(texture, surface->texture_level);
1463 height = wined3d_texture_get_level_height(texture, surface->texture_level);
1464 gl_info->gl_ops.gl.p_glReadPixels(0, 0, width, height,
1465 texture->resource.format->glFormat,
1466 texture->resource.format->glType, data.addr);
1467 checkGLcall("glReadPixels");
1469 /* Reset previous pixel store pack state */
1470 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
1471 checkGLcall("glPixelStorei");
1473 if (!srcIsUpsideDown)
1475 /* glReadPixels returns the image upside down, and there is no way to
1476 * prevent this. Flip the lines in software. */
1478 if (!(row = HeapAlloc(GetProcessHeap(), 0, row_pitch)))
1479 goto error;
1481 if (data.buffer_object)
1483 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
1484 checkGLcall("glMapBuffer");
1486 else
1487 mem = data.addr;
1489 top = mem;
1490 bottom = mem + row_pitch * (height - 1);
1491 for (i = 0; i < height / 2; i++)
1493 memcpy(row, top, row_pitch);
1494 memcpy(top, bottom, row_pitch);
1495 memcpy(bottom, row, row_pitch);
1496 top += row_pitch;
1497 bottom -= row_pitch;
1499 HeapFree(GetProcessHeap(), 0, row);
1501 if (data.buffer_object)
1502 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
1505 error:
1506 if (data.buffer_object)
1508 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1509 checkGLcall("glBindBuffer");
1512 if (restore_rt)
1513 context_restore(context, restore_rt);
1516 /* Read the framebuffer contents into a texture. Note that this function
1517 * doesn't do any kind of flipping. Using this on an onscreen surface will
1518 * result in a flipped D3D texture.
1520 * Context activation is done by the caller. This function may temporarily
1521 * switch to a different context and restore the original one before return. */
1522 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb, struct wined3d_context *old_ctx)
1524 struct wined3d_texture *texture = surface->container;
1525 struct wined3d_device *device = texture->resource.device;
1526 const struct wined3d_gl_info *gl_info;
1527 struct wined3d_context *context = old_ctx;
1528 struct wined3d_surface *restore_rt = NULL;
1530 restore_rt = context_get_rt_surface(old_ctx);
1531 if (restore_rt != surface)
1532 context = context_acquire(device, texture, surface_get_sub_resource_idx(surface));
1533 else
1534 restore_rt = NULL;
1536 gl_info = context->gl_info;
1537 device_invalidate_state(device, STATE_FRAMEBUFFER);
1539 wined3d_texture_prepare_texture(texture, context, srgb);
1540 wined3d_texture_bind_and_dirtify(texture, context, srgb);
1542 TRACE("Reading back offscreen render target %p.\n", surface);
1544 if (wined3d_resource_is_offscreen(&texture->resource))
1545 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1546 else
1547 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(texture));
1548 checkGLcall("glReadBuffer");
1550 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
1551 0, 0, 0, 0, wined3d_texture_get_level_width(texture, surface->texture_level),
1552 wined3d_texture_get_level_height(texture, surface->texture_level));
1553 checkGLcall("glCopyTexSubImage2D");
1555 if (restore_rt)
1556 context_restore(context, restore_rt);
1559 /* Does a direct frame buffer -> texture copy. Stretching is done with single
1560 * pixel copy calls. */
1561 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
1562 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
1564 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
1565 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1566 struct wined3d_texture *src_texture = src_surface->container;
1567 struct wined3d_texture *dst_texture = dst_surface->container;
1568 struct wined3d_device *device = dst_texture->resource.device;
1569 const struct wined3d_gl_info *gl_info;
1570 float xrel, yrel;
1571 struct wined3d_context *context;
1572 BOOL upsidedown = FALSE;
1573 RECT dst_rect = *dst_rect_in;
1574 unsigned int src_height;
1576 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1577 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1579 if(dst_rect.top > dst_rect.bottom) {
1580 UINT tmp = dst_rect.bottom;
1581 dst_rect.bottom = dst_rect.top;
1582 dst_rect.top = tmp;
1583 upsidedown = TRUE;
1586 context = context_acquire(device, src_texture, src_sub_resource_idx);
1587 gl_info = context->gl_info;
1588 context_apply_blit_state(context, device);
1589 wined3d_texture_load(dst_texture, context, FALSE);
1591 /* Bind the target texture */
1592 context_bind_texture(context, dst_texture->target, dst_texture->texture_rgb.name);
1593 if (wined3d_resource_is_offscreen(&src_texture->resource))
1595 TRACE("Reading from an offscreen target\n");
1596 upsidedown = !upsidedown;
1597 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1599 else
1601 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
1603 checkGLcall("glReadBuffer");
1605 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
1606 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
1608 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1610 FIXME_(d3d_perf)("Doing a pixel by pixel copy from the framebuffer to a texture.\n");
1612 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1613 ERR("Texture filtering not supported in direct blit.\n");
1615 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1616 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1618 ERR("Texture filtering not supported in direct blit\n");
1621 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
1622 if (upsidedown
1623 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1624 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1626 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
1627 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1628 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
1629 src_rect->left, src_height - src_rect->bottom,
1630 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1632 else
1634 LONG row;
1635 UINT yoffset = src_height - src_rect->top + dst_rect.top - 1;
1636 /* I have to process this row by row to swap the image,
1637 * otherwise it would be upside down, so stretching in y direction
1638 * doesn't cost extra time
1640 * However, stretching in x direction can be avoided if not necessary
1642 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
1643 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1645 /* Well, that stuff works, but it's very slow.
1646 * find a better way instead
1648 LONG col;
1650 for (col = dst_rect.left; col < dst_rect.right; ++col)
1652 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1653 dst_rect.left + col /* x offset */, row /* y offset */,
1654 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
1657 else
1659 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1660 dst_rect.left /* x offset */, row /* y offset */,
1661 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
1665 checkGLcall("glCopyTexSubImage2D");
1667 context_release(context);
1669 /* The texture is now most up to date - If the surface is a render target
1670 * and has a drawable, this path is never entered. */
1671 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1672 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1675 /* Uses the hardware to stretch and flip the image */
1676 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
1677 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
1679 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
1680 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1681 unsigned int src_width, src_height, src_pow2_width, src_pow2_height;
1682 struct wined3d_texture *src_texture = src_surface->container;
1683 struct wined3d_texture *dst_texture = dst_surface->container;
1684 struct wined3d_device *device = dst_texture->resource.device;
1685 GLuint src, backup = 0;
1686 float left, right, top, bottom; /* Texture coordinates */
1687 const struct wined3d_gl_info *gl_info;
1688 struct wined3d_context *context;
1689 GLenum drawBuffer = GL_BACK;
1690 GLenum offscreen_buffer;
1691 GLenum texture_target;
1692 BOOL noBackBufferBackup;
1693 BOOL src_offscreen;
1694 BOOL upsidedown = FALSE;
1695 RECT dst_rect = *dst_rect_in;
1697 TRACE("Using hwstretch blit\n");
1698 /* Activate the Proper context for reading from the source surface, set it up for blitting */
1699 context = context_acquire(device, src_texture, src_sub_resource_idx);
1700 gl_info = context->gl_info;
1701 context_apply_blit_state(context, device);
1702 wined3d_texture_load(dst_texture, context, FALSE);
1704 offscreen_buffer = context_get_offscreen_gl_buffer(context);
1705 src_width = wined3d_texture_get_level_width(src_texture, src_surface->texture_level);
1706 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
1707 src_pow2_width = wined3d_texture_get_level_pow2_width(src_texture, src_surface->texture_level);
1708 src_pow2_height = wined3d_texture_get_level_pow2_height(src_texture, src_surface->texture_level);
1710 src_offscreen = wined3d_resource_is_offscreen(&src_texture->resource);
1711 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
1712 if (!noBackBufferBackup && !src_texture->texture_rgb.name)
1714 /* Get it a description */
1715 wined3d_texture_load(src_texture, context, FALSE);
1718 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
1719 * This way we don't have to wait for the 2nd readback to finish to leave this function.
1721 if (context->aux_buffers >= 2)
1723 /* Got more than one aux buffer? Use the 2nd aux buffer */
1724 drawBuffer = GL_AUX1;
1726 else if ((!src_offscreen || offscreen_buffer == GL_BACK) && context->aux_buffers >= 1)
1728 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
1729 drawBuffer = GL_AUX0;
1732 if (noBackBufferBackup)
1734 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
1735 checkGLcall("glGenTextures");
1736 context_bind_texture(context, GL_TEXTURE_2D, backup);
1737 texture_target = GL_TEXTURE_2D;
1739 else
1741 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
1742 * we are reading from the back buffer, the backup can be used as source texture
1744 texture_target = src_surface->texture_target;
1745 context_bind_texture(context, texture_target, src_texture->texture_rgb.name);
1746 gl_info->gl_ops.gl.p_glEnable(texture_target);
1747 checkGLcall("glEnable(texture_target)");
1749 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
1750 surface_get_sub_resource(src_surface)->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
1753 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1754 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1756 if(dst_rect.top > dst_rect.bottom) {
1757 UINT tmp = dst_rect.bottom;
1758 dst_rect.bottom = dst_rect.top;
1759 dst_rect.top = tmp;
1760 upsidedown = TRUE;
1763 if (src_offscreen)
1765 TRACE("Reading from an offscreen target\n");
1766 upsidedown = !upsidedown;
1767 gl_info->gl_ops.gl.p_glReadBuffer(offscreen_buffer);
1769 else
1771 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
1774 /* TODO: Only back up the part that will be overwritten */
1775 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, src_width, src_height);
1777 checkGLcall("glCopyTexSubImage2D");
1779 /* No issue with overriding these - the sampler is dirty due to blit usage */
1780 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
1781 checkGLcall("glTexParameteri");
1782 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
1783 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
1784 checkGLcall("glTexParameteri");
1786 if (!src_texture->swapchain || src_texture == src_texture->swapchain->back_buffers[0])
1788 src = backup ? backup : src_texture->texture_rgb.name;
1790 else
1792 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
1793 checkGLcall("glReadBuffer(GL_FRONT)");
1795 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
1796 checkGLcall("glGenTextures(1, &src)");
1797 context_bind_texture(context, GL_TEXTURE_2D, src);
1799 /* TODO: Only copy the part that will be read. Use src_rect->left,
1800 * src_rect->bottom as origin, but with the width watch out for power
1801 * of 2 sizes. */
1802 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_pow2_width,
1803 src_pow2_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
1804 checkGLcall("glTexImage2D");
1805 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, src_width, src_height);
1807 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1808 checkGLcall("glTexParameteri");
1809 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1810 checkGLcall("glTexParameteri");
1812 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
1813 checkGLcall("glReadBuffer(GL_BACK)");
1815 if (texture_target != GL_TEXTURE_2D)
1817 gl_info->gl_ops.gl.p_glDisable(texture_target);
1818 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
1819 texture_target = GL_TEXTURE_2D;
1822 checkGLcall("glEnd and previous");
1824 left = src_rect->left;
1825 right = src_rect->right;
1827 if (!upsidedown)
1829 top = src_height - src_rect->top;
1830 bottom = src_height - src_rect->bottom;
1832 else
1834 top = src_height - src_rect->bottom;
1835 bottom = src_height - src_rect->top;
1838 if (src_texture->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
1840 left /= src_pow2_width;
1841 right /= src_pow2_width;
1842 top /= src_pow2_height;
1843 bottom /= src_pow2_height;
1846 /* draw the source texture stretched and upside down. The correct surface is bound already */
1847 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1848 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1850 context_set_draw_buffer(context, drawBuffer);
1851 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
1853 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
1854 /* bottom left */
1855 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
1856 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
1858 /* top left */
1859 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
1860 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
1862 /* top right */
1863 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
1864 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1866 /* bottom right */
1867 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
1868 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
1869 gl_info->gl_ops.gl.p_glEnd();
1870 checkGLcall("glEnd and previous");
1872 if (texture_target != dst_surface->texture_target)
1874 gl_info->gl_ops.gl.p_glDisable(texture_target);
1875 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
1876 texture_target = dst_surface->texture_target;
1879 /* Now read the stretched and upside down image into the destination texture */
1880 context_bind_texture(context, texture_target, dst_texture->texture_rgb.name);
1881 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
1883 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
1884 0, 0, /* We blitted the image to the origin */
1885 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1886 checkGLcall("glCopyTexSubImage2D");
1888 if (drawBuffer == GL_BACK)
1890 /* Write the back buffer backup back. */
1891 if (backup)
1893 if (texture_target != GL_TEXTURE_2D)
1895 gl_info->gl_ops.gl.p_glDisable(texture_target);
1896 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
1897 texture_target = GL_TEXTURE_2D;
1899 context_bind_texture(context, GL_TEXTURE_2D, backup);
1901 else
1903 if (texture_target != src_surface->texture_target)
1905 gl_info->gl_ops.gl.p_glDisable(texture_target);
1906 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
1907 texture_target = src_surface->texture_target;
1909 context_bind_texture(context, src_surface->texture_target, src_texture->texture_rgb.name);
1912 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
1913 /* top left */
1914 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
1915 gl_info->gl_ops.gl.p_glVertex2i(0, src_height);
1917 /* bottom left */
1918 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)src_height / (float)src_pow2_height);
1919 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
1921 /* bottom right */
1922 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width,
1923 (float)src_height / (float)src_pow2_height);
1924 gl_info->gl_ops.gl.p_glVertex2i(src_width, 0);
1926 /* top right */
1927 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width, 0.0f);
1928 gl_info->gl_ops.gl.p_glVertex2i(src_width, src_height);
1929 gl_info->gl_ops.gl.p_glEnd();
1931 gl_info->gl_ops.gl.p_glDisable(texture_target);
1932 checkGLcall("glDisable(texture_target)");
1934 /* Cleanup */
1935 if (src != src_texture->texture_rgb.name && src != backup)
1937 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
1938 checkGLcall("glDeleteTextures(1, &src)");
1940 if (backup)
1942 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
1943 checkGLcall("glDeleteTextures(1, &backup)");
1946 if (wined3d_settings.strict_draw_ordering)
1947 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
1949 context_release(context);
1951 /* The texture is now most up to date - If the surface is a render target
1952 * and has a drawable, this path is never entered. */
1953 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1954 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1957 /* Front buffer coordinates are always full screen coordinates, but our GL
1958 * drawable is limited to the window's client area. The sysmem and texture
1959 * copies do have the full screen size. Note that GL has a bottom-left
1960 * origin, while D3D has a top-left origin. */
1961 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
1963 struct wined3d_texture *texture = surface->container;
1964 UINT drawable_height;
1966 if (texture->swapchain)
1968 POINT offset = {0, 0};
1969 RECT windowsize;
1971 if (texture == texture->swapchain->front_buffer)
1973 ScreenToClient(window, &offset);
1974 OffsetRect(rect, offset.x, offset.y);
1977 GetClientRect(window, &windowsize);
1978 drawable_height = windowsize.bottom - windowsize.top;
1980 else
1982 drawable_height = wined3d_texture_get_level_height(texture, surface->texture_level);
1985 rect->top = drawable_height - rect->top;
1986 rect->bottom = drawable_height - rect->bottom;
1989 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
1990 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
1991 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
1993 struct wined3d_texture *dst_texture = dst_surface->container;
1994 struct wined3d_device *device = dst_texture->resource.device;
1995 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
1996 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
1997 struct wined3d_texture *src_texture;
1999 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
2000 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
2001 flags, fx, debug_d3dtexturefiltertype(filter));
2003 /* Get the swapchain. One of the surfaces has to be a primary surface */
2004 if (dst_texture->resource.pool == WINED3D_POOL_SYSTEM_MEM)
2006 WARN("Destination is in sysmem, rejecting gl blt\n");
2007 return WINED3DERR_INVALIDCALL;
2010 dst_swapchain = dst_texture->swapchain;
2012 if (src_surface)
2014 src_texture = src_surface->container;
2015 if (src_texture->resource.pool == WINED3D_POOL_SYSTEM_MEM)
2017 WARN("Src is in sysmem, rejecting gl blt\n");
2018 return WINED3DERR_INVALIDCALL;
2021 src_swapchain = src_texture->swapchain;
2023 else
2025 src_texture = NULL;
2026 src_swapchain = NULL;
2029 /* Early sort out of cases where no render target is used */
2030 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
2032 TRACE("No surface is render target, not using hardware blit.\n");
2033 return WINED3DERR_INVALIDCALL;
2036 /* No destination color keying supported */
2037 if (flags & (WINED3D_BLT_DST_CKEY | WINED3D_BLT_DST_CKEY_OVERRIDE))
2039 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2040 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2041 return WINED3DERR_INVALIDCALL;
2044 if (dst_swapchain && dst_swapchain == src_swapchain)
2046 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
2047 return WINED3DERR_INVALIDCALL;
2050 if (dst_swapchain && src_swapchain)
2052 FIXME("Implement hardware blit between two different swapchains\n");
2053 return WINED3DERR_INVALIDCALL;
2056 if (dst_swapchain)
2058 /* Handled with regular texture -> swapchain blit */
2059 if (src_surface == rt)
2060 TRACE("Blit from active render target to a swapchain\n");
2062 else if (src_swapchain && dst_surface == rt)
2064 FIXME("Implement blit from a swapchain to the active render target\n");
2065 return WINED3DERR_INVALIDCALL;
2068 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
2070 unsigned int src_width, src_height;
2071 /* Blit from render target to texture */
2072 BOOL stretchx;
2074 /* P8 read back is not implemented */
2075 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT
2076 || dst_texture->resource.format->id == WINED3DFMT_P8_UINT)
2078 TRACE("P8 read back not supported by frame buffer to texture blit\n");
2079 return WINED3DERR_INVALIDCALL;
2082 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_SRC_CKEY_OVERRIDE))
2084 TRACE("Color keying not supported by frame buffer to texture blit\n");
2085 return WINED3DERR_INVALIDCALL;
2086 /* Destination color key is checked above */
2089 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
2090 stretchx = TRUE;
2091 else
2092 stretchx = FALSE;
2094 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2095 * flip the image nor scale it.
2097 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2098 * -> If the app wants an image width an unscaled width, copy it line per line
2099 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
2100 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2101 * back buffer. This is slower than reading line per line, thus not used for flipping
2102 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2103 * pixel by pixel. */
2104 src_width = wined3d_texture_get_level_width(src_texture, src_surface->texture_level);
2105 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
2106 if (!stretchx || dst_rect->right - dst_rect->left > src_width
2107 || dst_rect->bottom - dst_rect->top > src_height)
2109 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
2110 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
2112 else
2114 TRACE("Using hardware stretching to flip / stretch the texture.\n");
2115 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
2118 return WINED3D_OK;
2121 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
2122 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
2123 return WINED3DERR_INVALIDCALL;
2126 /* Context activation is done by the caller. */
2127 static BOOL surface_load_sysmem(struct wined3d_surface *surface,
2128 struct wined3d_context *context, DWORD dst_location)
2130 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2131 const struct wined3d_gl_info *gl_info = context->gl_info;
2132 struct wined3d_texture *texture = surface->container;
2133 struct wined3d_texture_sub_resource *sub_resource;
2135 sub_resource = &texture->sub_resources[sub_resource_idx];
2136 wined3d_texture_prepare_location(texture, sub_resource_idx, context, dst_location);
2138 if (sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
2139 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
2141 /* Download the surface to system memory. */
2142 if (sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
2144 wined3d_texture_bind_and_dirtify(texture, context,
2145 !(sub_resource->locations & WINED3D_LOCATION_TEXTURE_RGB));
2146 surface_download_data(surface, gl_info, dst_location);
2147 ++texture->download_count;
2149 return TRUE;
2152 if (!(texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2153 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
2155 read_from_framebuffer(surface, context, dst_location);
2156 return TRUE;
2159 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
2160 surface, wined3d_debug_location(sub_resource->locations));
2161 return FALSE;
2164 /* Context activation is done by the caller. */
2165 static BOOL surface_load_drawable(struct wined3d_surface *surface,
2166 struct wined3d_context *context)
2168 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2169 struct wined3d_texture *texture = surface->container;
2170 struct wined3d_surface *restore_rt = NULL;
2171 struct wined3d_device *device;
2172 RECT r;
2174 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2176 DWORD current = texture->sub_resources[sub_resource_idx].locations;
2177 FIXME("Unimplemented copy from %s for depth/stencil buffers.\n",
2178 wined3d_debug_location(current));
2179 return FALSE;
2182 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
2183 && wined3d_resource_is_offscreen(&texture->resource))
2185 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
2186 return FALSE;
2189 device = texture->resource.device;
2190 restore_rt = context_get_rt_surface(context);
2191 if (restore_rt != surface)
2192 context = context_acquire(device, texture, sub_resource_idx);
2193 else
2194 restore_rt = NULL;
2196 surface_get_rect(surface, NULL, &r);
2197 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
2198 device->blitter->ops->blitter_blit(device->blitter, WINED3D_BLIT_OP_COLOR_BLIT, context,
2199 surface, WINED3D_LOCATION_TEXTURE_RGB, &r,
2200 surface, WINED3D_LOCATION_DRAWABLE, &r,
2201 NULL, WINED3D_TEXF_POINT);
2203 if (restore_rt)
2204 context_restore(context, restore_rt);
2206 return TRUE;
2209 static BOOL surface_load_texture(struct wined3d_surface *surface,
2210 struct wined3d_context *context, BOOL srgb)
2212 unsigned int width, height, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch;
2213 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2214 const struct wined3d_gl_info *gl_info = context->gl_info;
2215 struct wined3d_texture *texture = surface->container;
2216 struct wined3d_device *device = texture->resource.device;
2217 const struct wined3d_color_key_conversion *conversion;
2218 struct wined3d_texture_sub_resource *sub_resource;
2219 struct wined3d_bo_address data;
2220 BYTE *src_mem, *dst_mem = NULL;
2221 struct wined3d_format format;
2222 POINT dst_point = {0, 0};
2223 RECT src_rect;
2224 BOOL depth;
2226 depth = texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL;
2227 sub_resource = surface_get_sub_resource(surface);
2229 if (!depth && wined3d_settings.offscreen_rendering_mode != ORM_FBO
2230 && wined3d_resource_is_offscreen(&texture->resource)
2231 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
2233 surface_load_fb_texture(surface, srgb, context);
2235 return TRUE;
2238 width = wined3d_texture_get_level_width(texture, surface->texture_level);
2239 height = wined3d_texture_get_level_height(texture, surface->texture_level);
2240 SetRect(&src_rect, 0, 0, width, height);
2242 if (!depth && sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
2243 && (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
2244 && fbo_blitter_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
2245 texture->resource.usage, texture->resource.pool, texture->resource.format,
2246 texture->resource.usage, texture->resource.pool, texture->resource.format))
2248 if (srgb)
2249 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
2250 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
2251 else
2252 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
2253 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
2255 return TRUE;
2258 if (!depth && sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
2259 && (!srgb || (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
2260 && fbo_blitter_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
2261 texture->resource.usage, texture->resource.pool, texture->resource.format,
2262 texture->resource.usage, texture->resource.pool, texture->resource.format))
2264 DWORD src_location = sub_resource->locations & WINED3D_LOCATION_RB_RESOLVED ?
2265 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
2266 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2268 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, src_location,
2269 &src_rect, surface, dst_location, &src_rect);
2271 return TRUE;
2274 /* Upload from system memory */
2276 if (srgb)
2278 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | texture->resource.map_binding))
2279 == WINED3D_LOCATION_TEXTURE_RGB)
2281 FIXME_(d3d_perf)("Downloading RGB surface %p to reload it as sRGB.\n", surface);
2282 wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding);
2285 else
2287 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | texture->resource.map_binding))
2288 == WINED3D_LOCATION_TEXTURE_SRGB)
2290 FIXME_(d3d_perf)("Downloading sRGB surface %p to reload it as RGB.\n", surface);
2291 wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding);
2295 if (!(sub_resource->locations & surface_simple_locations))
2297 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
2298 /* Lets hope we get it from somewhere... */
2299 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM);
2302 wined3d_texture_prepare_texture(texture, context, srgb);
2303 wined3d_texture_bind_and_dirtify(texture, context, srgb);
2304 wined3d_texture_get_pitch(texture, surface->texture_level, &src_row_pitch, &src_slice_pitch);
2306 format = *texture->resource.format;
2307 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
2308 format = *wined3d_get_format(gl_info, conversion->dst_format, texture->resource.usage);
2310 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
2311 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
2312 * getting called. */
2313 if ((format.convert || conversion) && texture->sub_resources[sub_resource_idx].buffer_object)
2315 TRACE("Removing the pbo attached to surface %p.\n", surface);
2317 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM);
2318 wined3d_texture_set_map_binding(texture, WINED3D_LOCATION_SYSMEM);
2321 wined3d_texture_get_memory(texture, sub_resource_idx, &data, sub_resource->locations);
2322 if (format.convert)
2324 /* This code is entered for texture formats which need a fixup. */
2325 format.byte_count = format.conv_byte_count;
2326 wined3d_format_calculate_pitch(&format, 1, width, height, &dst_row_pitch, &dst_slice_pitch);
2328 src_mem = wined3d_texture_map_bo_address(&data, src_slice_pitch,
2329 gl_info, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READONLY);
2330 if (!(dst_mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
2332 ERR("Out of memory (%u).\n", dst_slice_pitch);
2333 context_release(context);
2334 return FALSE;
2336 format.convert(src_mem, dst_mem, src_row_pitch, src_slice_pitch,
2337 dst_row_pitch, dst_slice_pitch, width, height, 1);
2338 src_row_pitch = dst_row_pitch;
2339 wined3d_texture_unmap_bo_address(&data, gl_info, GL_PIXEL_UNPACK_BUFFER);
2341 data.buffer_object = 0;
2342 data.addr = dst_mem;
2344 else if (conversion)
2346 /* This code is only entered for color keying fixups */
2347 struct wined3d_palette *palette = NULL;
2349 wined3d_format_calculate_pitch(&format, device->surface_alignment,
2350 width, height, &dst_row_pitch, &dst_slice_pitch);
2352 src_mem = wined3d_texture_map_bo_address(&data, src_slice_pitch,
2353 gl_info, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READONLY);
2354 if (!(dst_mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
2356 ERR("Out of memory (%u).\n", dst_slice_pitch);
2357 context_release(context);
2358 return FALSE;
2360 if (texture->swapchain && texture->swapchain->palette)
2361 palette = texture->swapchain->palette;
2362 conversion->convert(src_mem, src_row_pitch, dst_mem, dst_row_pitch,
2363 width, height, palette, &texture->async.gl_color_key);
2364 src_row_pitch = dst_row_pitch;
2365 wined3d_texture_unmap_bo_address(&data, gl_info, GL_PIXEL_UNPACK_BUFFER);
2367 data.buffer_object = 0;
2368 data.addr = dst_mem;
2371 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
2372 src_row_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
2374 HeapFree(GetProcessHeap(), 0, dst_mem);
2376 return TRUE;
2379 /* Context activation is done by the caller. */
2380 static BOOL surface_load_renderbuffer(struct wined3d_surface *surface, struct wined3d_context *context,
2381 DWORD dst_location)
2383 struct wined3d_texture *texture = surface->container;
2384 const RECT rect = {0, 0,
2385 wined3d_texture_get_level_width(texture, surface->texture_level),
2386 wined3d_texture_get_level_height(texture, surface->texture_level)};
2387 DWORD locations = surface_get_sub_resource(surface)->locations;
2388 DWORD src_location;
2390 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2392 FIXME("Unimplemented copy from %s for depth/stencil buffers.\n",
2393 wined3d_debug_location(locations));
2394 return FALSE;
2397 if (locations & WINED3D_LOCATION_RB_MULTISAMPLE)
2398 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
2399 else if (locations & WINED3D_LOCATION_RB_RESOLVED)
2400 src_location = WINED3D_LOCATION_RB_RESOLVED;
2401 else if (locations & WINED3D_LOCATION_TEXTURE_SRGB)
2402 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
2403 else /* surface_blt_fbo will load the source location if necessary. */
2404 src_location = WINED3D_LOCATION_TEXTURE_RGB;
2406 surface_blt_fbo(texture->resource.device, context, WINED3D_TEXF_POINT,
2407 surface, src_location, &rect, surface, dst_location, &rect);
2409 return TRUE;
2412 /* Context activation is done by the caller. Context may be NULL in ddraw-only mode. */
2413 BOOL surface_load_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
2415 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
2417 switch (location)
2419 case WINED3D_LOCATION_USER_MEMORY:
2420 case WINED3D_LOCATION_SYSMEM:
2421 case WINED3D_LOCATION_BUFFER:
2422 return surface_load_sysmem(surface, context, location);
2424 case WINED3D_LOCATION_DRAWABLE:
2425 return surface_load_drawable(surface, context);
2427 case WINED3D_LOCATION_RB_RESOLVED:
2428 case WINED3D_LOCATION_RB_MULTISAMPLE:
2429 return surface_load_renderbuffer(surface, context, location);
2431 case WINED3D_LOCATION_TEXTURE_RGB:
2432 case WINED3D_LOCATION_TEXTURE_SRGB:
2433 return surface_load_texture(surface, context,
2434 location == WINED3D_LOCATION_TEXTURE_SRGB);
2436 default:
2437 ERR("Don't know how to handle location %#x.\n", location);
2438 return FALSE;
2442 /* Context activation is done by the caller. */
2443 static void fbo_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2445 struct wined3d_blitter *next;
2447 if ((next = blitter->next))
2448 next->ops->blitter_destroy(next, context);
2451 static void fbo_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
2452 struct wined3d_rendertarget_view *view, const RECT *rect, DWORD flags,
2453 const struct wined3d_color *colour, float depth, DWORD stencil)
2455 struct wined3d_blitter *next;
2457 if ((next = blitter->next))
2458 next->ops->blitter_clear(next, device, view, rect, flags, colour, depth, stencil);
2461 static void fbo_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
2462 struct wined3d_context *context, struct wined3d_surface *src_surface, DWORD src_location,
2463 const RECT *src_rect, struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect,
2464 const struct wined3d_color_key *colour_key, enum wined3d_texture_filter_type filter)
2466 struct wined3d_resource *src_resource = &src_surface->container->resource;
2467 struct wined3d_resource *dst_resource = &dst_surface->container->resource;
2468 struct wined3d_device *device = dst_resource->device;
2469 struct wined3d_blitter *next;
2471 if (!fbo_blitter_supported(&device->adapter->gl_info, op,
2472 src_resource->usage, src_resource->pool, src_resource->format,
2473 src_resource->usage, dst_resource->pool, dst_resource->format))
2475 if ((next = blitter->next))
2476 next->ops->blitter_blit(next, op, context, src_surface, src_location,
2477 src_rect, dst_surface, dst_location, dst_rect, colour_key, filter);
2478 return;
2481 if (op == WINED3D_BLIT_OP_COLOR_BLIT)
2483 TRACE("Colour blit.\n");
2484 surface_blt_fbo(device, context, filter, src_surface, src_location,
2485 src_rect, dst_surface, dst_location, dst_rect);
2486 return;
2489 if (op == WINED3D_BLIT_OP_DEPTH_BLIT)
2491 TRACE("Depth/stencil blit.\n");
2492 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
2493 return;
2496 ERR("This blitter does not implement blit op %#x.\n", op);
2499 static const struct wined3d_blitter_ops fbo_blitter_ops =
2501 fbo_blitter_destroy,
2502 fbo_blitter_clear,
2503 fbo_blitter_blit,
2506 void wined3d_fbo_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
2508 struct wined3d_blitter *blitter;
2510 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
2511 return;
2513 if (!(blitter = HeapAlloc(GetProcessHeap(), 0, sizeof(*blitter))))
2514 return;
2516 TRACE("Created blitter %p.\n", blitter);
2518 blitter->ops = &fbo_blitter_ops;
2519 blitter->next = *next;
2520 *next = blitter;
2523 /* Context activation is done by the caller. */
2524 static void ffp_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2526 struct wined3d_blitter *next;
2528 if ((next = blitter->next))
2529 next->ops->blitter_destroy(next, context);
2532 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info,
2533 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
2534 DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
2535 DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
2537 BOOL decompress;
2539 decompress = src_format && (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
2540 && !(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED);
2541 if (!decompress && (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM))
2543 TRACE("Source or destination is in system memory.\n");
2544 return FALSE;
2547 switch (blit_op)
2549 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
2550 if (d3d_info->shader_color_key)
2552 TRACE("Color keying requires converted textures.\n");
2553 return FALSE;
2555 case WINED3D_BLIT_OP_COLOR_BLIT:
2556 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
2557 if (!gl_info->supported[WINED3D_GL_LEGACY_CONTEXT])
2558 return FALSE;
2560 if (TRACE_ON(d3d))
2562 TRACE("Checking support for fixup:\n");
2563 dump_color_fixup_desc(src_format->color_fixup);
2566 /* We only support identity conversions. */
2567 if (!is_identity_fixup(src_format->color_fixup)
2568 || !is_identity_fixup(dst_format->color_fixup))
2570 TRACE("Fixups are not supported.\n");
2571 return FALSE;
2574 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
2576 TRACE("Can only blit to render targets.\n");
2577 return FALSE;
2579 return TRUE;
2581 default:
2582 TRACE("Unsupported blit_op=%d\n", blit_op);
2583 return FALSE;
2587 static void ffp_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
2588 struct wined3d_rendertarget_view *view, const RECT *rect, DWORD flags,
2589 const struct wined3d_color *colour, float depth, DWORD stencil)
2591 const RECT draw_rect = {0, 0, view->width, view->height};
2592 struct wined3d_resource *resource = view->resource;
2593 struct wined3d_fb_state fb = {&view, NULL};
2594 struct wined3d_blitter *next;
2596 if (resource->pool == WINED3D_POOL_SYSTEM_MEM)
2597 goto next;
2599 if (flags != WINED3DCLEAR_TARGET)
2601 struct wined3d_fb_state fb = {NULL, view};
2603 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, flags, NULL, depth, stencil);
2604 return;
2607 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
2609 if (!((view->format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE)
2610 || (resource->usage & WINED3DUSAGE_RENDERTARGET)))
2611 goto next;
2613 else if (!(resource->usage & WINED3DUSAGE_RENDERTARGET))
2615 goto next;
2618 /* FIXME: We should reject colour fills on formats with fixups, but this
2619 * would break P8 colour fills for example. */
2621 device_clear_render_targets(device, 1, &fb, 1, rect, &draw_rect, flags, colour, 0.0f, 0);
2622 return;
2624 next:
2625 if ((next = blitter->next))
2626 next->ops->blitter_clear(next, device, view, rect, flags, colour, depth, stencil);
2629 static void ffp_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
2630 struct wined3d_context *context, struct wined3d_surface *src_surface, DWORD src_location,
2631 const RECT *src_rect, struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect,
2632 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter)
2634 struct wined3d_texture *src_texture = src_surface->container;
2635 struct wined3d_texture *dst_texture = dst_surface->container;
2636 const struct wined3d_gl_info *gl_info = context->gl_info;
2637 struct wined3d_resource *src_resource, *dst_resource;
2638 struct wined3d_color_key old_blt_key;
2639 struct wined3d_device *device;
2640 struct wined3d_blitter *next;
2641 DWORD old_color_key_flags;
2642 RECT r;
2644 src_resource = &src_texture->resource;
2645 dst_resource = &dst_texture->resource;
2646 device = dst_resource->device;
2648 if (!ffp_blit_supported(&device->adapter->gl_info, &device->adapter->d3d_info, op,
2649 src_resource->usage, src_resource->pool, src_resource->format,
2650 dst_resource->usage, dst_resource->pool, dst_resource->format))
2652 if ((next = blitter->next))
2653 next->ops->blitter_blit(next, op, context, src_surface, src_location,
2654 src_rect, dst_surface, dst_location, dst_rect, color_key, filter);
2655 return;
2658 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
2660 old_blt_key = src_texture->async.src_blt_color_key;
2661 old_color_key_flags = src_texture->async.color_key_flags;
2662 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT, color_key);
2664 /* Make sure the surface is up-to-date. This should probably use
2665 * surface_load_location() and worry about the destination surface too,
2666 * unless we're overwriting it completely. */
2667 wined3d_texture_load(src_texture, context, FALSE);
2669 /* Activate the destination context, set it up for blitting. */
2670 context_apply_blit_state(context, device);
2672 if (dst_location == WINED3D_LOCATION_DRAWABLE)
2674 r = *dst_rect;
2675 surface_translate_drawable_coords(dst_surface, context->win_handle, &r);
2676 dst_rect = &r;
2679 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
2681 GLenum buffer;
2683 if (dst_location == WINED3D_LOCATION_DRAWABLE)
2685 TRACE("Destination surface %p is onscreen.\n", dst_surface);
2686 buffer = wined3d_texture_get_gl_buffer(dst_texture);
2688 else
2690 TRACE("Destination surface %p is offscreen.\n", dst_surface);
2691 buffer = GL_COLOR_ATTACHMENT0;
2693 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
2694 context_set_draw_buffer(context, buffer);
2695 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
2696 context_invalidate_state(context, STATE_FRAMEBUFFER);
2699 gl_info->gl_ops.gl.p_glEnable(src_texture->target);
2700 checkGLcall("glEnable(target)");
2702 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST || color_key)
2704 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
2705 checkGLcall("glEnable(GL_ALPHA_TEST)");
2708 if (color_key)
2710 /* For P8 surfaces, the alpha component contains the palette index.
2711 * Which means that the colorkey is one of the palette entries. In
2712 * other cases pixels that should be masked away have alpha set to 0. */
2713 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT)
2714 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
2715 (float)src_texture->async.src_blt_color_key.color_space_low_value / 255.0f);
2716 else
2717 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
2718 checkGLcall("glAlphaFunc");
2721 draw_textured_quad(src_surface, context, src_rect, dst_rect, filter);
2723 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST || color_key)
2725 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2726 checkGLcall("glDisable(GL_ALPHA_TEST)");
2729 /* Leave the OpenGL state valid for blitting. */
2730 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
2731 checkGLcall("glDisable(GL_TEXTURE_2D)");
2732 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
2734 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
2735 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
2737 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
2739 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
2740 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
2743 if (wined3d_settings.strict_draw_ordering
2744 || (dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture))
2745 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2747 /* Restore the color key parameters */
2748 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT,
2749 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
2752 static const struct wined3d_blitter_ops ffp_blitter_ops =
2754 ffp_blitter_destroy,
2755 ffp_blitter_clear,
2756 ffp_blitter_blit,
2759 void wined3d_ffp_blitter_create(struct wined3d_blitter **next, const struct wined3d_gl_info *gl_info)
2761 struct wined3d_blitter *blitter;
2763 if (!(blitter = HeapAlloc(GetProcessHeap(), 0, sizeof(*blitter))))
2764 return;
2766 TRACE("Created blitter %p.\n", blitter);
2768 blitter->ops = &ffp_blitter_ops;
2769 blitter->next = *next;
2770 *next = blitter;
2773 /* Context activation is done by the caller. */
2774 static void cpu_blitter_destroy(struct wined3d_blitter *blitter, struct wined3d_context *context)
2776 struct wined3d_blitter *next;
2778 if ((next = blitter->next))
2779 next->ops->blitter_destroy(next, context);
2782 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
2783 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
2784 const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx)
2786 UINT row_block_count;
2787 const BYTE *src_row;
2788 BYTE *dst_row;
2789 UINT x, y;
2791 src_row = src_data;
2792 dst_row = dst_data;
2794 row_block_count = (update_w + format->block_width - 1) / format->block_width;
2796 if (!flags)
2798 for (y = 0; y < update_h; y += format->block_height)
2800 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
2801 src_row += src_pitch;
2802 dst_row += dst_pitch;
2805 return WINED3D_OK;
2808 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
2810 src_row += (((update_h / format->block_height) - 1) * src_pitch);
2812 switch (format->id)
2814 case WINED3DFMT_DXT1:
2815 for (y = 0; y < update_h; y += format->block_height)
2817 struct block
2819 WORD color[2];
2820 BYTE control_row[4];
2823 const struct block *s = (const struct block *)src_row;
2824 struct block *d = (struct block *)dst_row;
2826 for (x = 0; x < row_block_count; ++x)
2828 d[x].color[0] = s[x].color[0];
2829 d[x].color[1] = s[x].color[1];
2830 d[x].control_row[0] = s[x].control_row[3];
2831 d[x].control_row[1] = s[x].control_row[2];
2832 d[x].control_row[2] = s[x].control_row[1];
2833 d[x].control_row[3] = s[x].control_row[0];
2835 src_row -= src_pitch;
2836 dst_row += dst_pitch;
2838 return WINED3D_OK;
2840 case WINED3DFMT_DXT2:
2841 case WINED3DFMT_DXT3:
2842 for (y = 0; y < update_h; y += format->block_height)
2844 struct block
2846 WORD alpha_row[4];
2847 WORD color[2];
2848 BYTE control_row[4];
2851 const struct block *s = (const struct block *)src_row;
2852 struct block *d = (struct block *)dst_row;
2854 for (x = 0; x < row_block_count; ++x)
2856 d[x].alpha_row[0] = s[x].alpha_row[3];
2857 d[x].alpha_row[1] = s[x].alpha_row[2];
2858 d[x].alpha_row[2] = s[x].alpha_row[1];
2859 d[x].alpha_row[3] = s[x].alpha_row[0];
2860 d[x].color[0] = s[x].color[0];
2861 d[x].color[1] = s[x].color[1];
2862 d[x].control_row[0] = s[x].control_row[3];
2863 d[x].control_row[1] = s[x].control_row[2];
2864 d[x].control_row[2] = s[x].control_row[1];
2865 d[x].control_row[3] = s[x].control_row[0];
2867 src_row -= src_pitch;
2868 dst_row += dst_pitch;
2870 return WINED3D_OK;
2872 default:
2873 FIXME("Compressed flip not implemented for format %s.\n",
2874 debug_d3dformat(format->id));
2875 return E_NOTIMPL;
2879 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
2880 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
2882 return E_NOTIMPL;
2885 static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
2886 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
2887 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
2888 enum wined3d_texture_filter_type filter)
2890 unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count;
2891 const struct wined3d_format *src_format, *dst_format;
2892 struct wined3d_texture *converted_texture = NULL;
2893 unsigned int src_fmt_flags, dst_fmt_flags;
2894 struct wined3d_map_desc dst_map, src_map;
2895 const BYTE *sbase = NULL;
2896 HRESULT hr = WINED3D_OK;
2897 BOOL same_sub_resource;
2898 const BYTE *sbuf;
2899 BYTE *dbuf;
2900 int x, y;
2902 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
2903 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
2904 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture,
2905 src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
2907 if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx)
2909 same_sub_resource = TRUE;
2910 wined3d_resource_map(&dst_texture->resource, dst_sub_resource_idx, &dst_map, NULL, 0);
2911 src_map = dst_map;
2912 src_format = dst_texture->resource.format;
2913 dst_format = src_format;
2914 dst_fmt_flags = dst_texture->resource.format_flags;
2915 src_fmt_flags = dst_fmt_flags;
2917 else
2919 same_sub_resource = FALSE;
2920 dst_format = dst_texture->resource.format;
2921 dst_fmt_flags = dst_texture->resource.format_flags;
2922 if (src_texture)
2924 if (dst_texture->resource.format->id != src_texture->resource.format->id)
2926 if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format)))
2928 FIXME("Cannot convert %s to %s.\n", debug_d3dformat(src_texture->resource.format->id),
2929 debug_d3dformat(dst_texture->resource.format->id));
2930 return WINED3DERR_NOTAVAILABLE;
2932 src_texture = converted_texture;
2933 src_sub_resource_idx = 0;
2935 wined3d_resource_map(&src_texture->resource, src_sub_resource_idx, &src_map, NULL, WINED3D_MAP_READONLY);
2936 src_format = src_texture->resource.format;
2937 src_fmt_flags = src_texture->resource.format_flags;
2939 else
2941 src_format = dst_format;
2942 src_fmt_flags = dst_fmt_flags;
2945 wined3d_resource_map(&dst_texture->resource, dst_sub_resource_idx, &dst_map, dst_box, 0);
2948 bpp = dst_format->byte_count;
2949 src_height = src_box->bottom - src_box->top;
2950 src_width = src_box->right - src_box->left;
2951 dst_height = dst_box->bottom - dst_box->top;
2952 dst_width = dst_box->right - dst_box->left;
2953 row_byte_count = dst_width * bpp;
2955 if (src_texture)
2956 sbase = (BYTE *)src_map.data
2957 + ((src_box->top / src_format->block_height) * src_map.row_pitch)
2958 + ((src_box->left / src_format->block_width) * src_format->block_byte_count);
2959 if (same_sub_resource)
2960 dbuf = (BYTE *)dst_map.data
2961 + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch)
2962 + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count);
2963 else
2964 dbuf = dst_map.data;
2966 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
2968 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
2970 if (same_sub_resource)
2972 FIXME("Only plain blits supported on compressed surfaces.\n");
2973 hr = E_NOTIMPL;
2974 goto release;
2977 if (src_height != dst_height || src_width != dst_width)
2979 WARN("Stretching not supported on compressed surfaces.\n");
2980 hr = WINED3DERR_INVALIDCALL;
2981 goto release;
2984 hr = surface_cpu_blt_compressed(sbase, dbuf,
2985 src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height,
2986 src_format, flags, fx);
2987 goto release;
2990 /* Now the 'with source' blits. */
2991 if (src_texture)
2993 int sx, xinc, sy, yinc;
2995 if (!dst_width || !dst_height) /* Hmm... stupid program? */
2996 goto release;
2998 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
2999 && (src_width != dst_width || src_height != dst_height))
3001 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
3002 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
3005 xinc = (src_width << 16) / dst_width;
3006 yinc = (src_height << 16) / dst_height;
3008 if (!flags)
3010 /* No effects, we can cheat here. */
3011 if (dst_width == src_width)
3013 if (dst_height == src_height)
3015 /* No stretching in either direction. This needs to be as
3016 * fast as possible. */
3017 sbuf = sbase;
3019 /* Check for overlapping surfaces. */
3020 if (!same_sub_resource || dst_box->top < src_box->top
3021 || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
3023 /* No overlap, or dst above src, so copy from top downwards. */
3024 for (y = 0; y < dst_height; ++y)
3026 memcpy(dbuf, sbuf, row_byte_count);
3027 sbuf += src_map.row_pitch;
3028 dbuf += dst_map.row_pitch;
3031 else if (dst_box->top > src_box->top)
3033 /* Copy from bottom upwards. */
3034 sbuf += src_map.row_pitch * dst_height;
3035 dbuf += dst_map.row_pitch * dst_height;
3036 for (y = 0; y < dst_height; ++y)
3038 sbuf -= src_map.row_pitch;
3039 dbuf -= dst_map.row_pitch;
3040 memcpy(dbuf, sbuf, row_byte_count);
3043 else
3045 /* Src and dst overlapping on the same line, use memmove. */
3046 for (y = 0; y < dst_height; ++y)
3048 memmove(dbuf, sbuf, row_byte_count);
3049 sbuf += src_map.row_pitch;
3050 dbuf += dst_map.row_pitch;
3054 else
3056 /* Stretching in y direction only. */
3057 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3059 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3060 memcpy(dbuf, sbuf, row_byte_count);
3061 dbuf += dst_map.row_pitch;
3065 else
3067 /* Stretching in X direction. */
3068 int last_sy = -1;
3069 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3071 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3073 if ((sy >> 16) == (last_sy >> 16))
3075 /* This source row is the same as last source row -
3076 * Copy the already stretched row. */
3077 memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count);
3079 else
3081 #define STRETCH_ROW(type) \
3082 do { \
3083 const type *s = (const type *)sbuf; \
3084 type *d = (type *)dbuf; \
3085 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3086 d[x] = s[sx >> 16]; \
3087 } while(0)
3089 switch(bpp)
3091 case 1:
3092 STRETCH_ROW(BYTE);
3093 break;
3094 case 2:
3095 STRETCH_ROW(WORD);
3096 break;
3097 case 4:
3098 STRETCH_ROW(DWORD);
3099 break;
3100 case 3:
3102 const BYTE *s;
3103 BYTE *d = dbuf;
3104 for (x = sx = 0; x < dst_width; x++, sx+= xinc)
3106 DWORD pixel;
3108 s = sbuf + 3 * (sx >> 16);
3109 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3110 d[0] = (pixel ) & 0xff;
3111 d[1] = (pixel >> 8) & 0xff;
3112 d[2] = (pixel >> 16) & 0xff;
3113 d += 3;
3115 break;
3117 default:
3118 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
3119 hr = WINED3DERR_NOTAVAILABLE;
3120 goto error;
3122 #undef STRETCH_ROW
3124 dbuf += dst_map.row_pitch;
3125 last_sy = sy;
3129 else
3131 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
3132 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
3133 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
3134 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3135 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
3137 /* The color keying flags are checked for correctness in ddraw */
3138 if (flags & WINED3D_BLT_SRC_CKEY)
3140 keylow = src_texture->async.src_blt_color_key.color_space_low_value;
3141 keyhigh = src_texture->async.src_blt_color_key.color_space_high_value;
3143 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3145 keylow = fx->src_color_key.color_space_low_value;
3146 keyhigh = fx->src_color_key.color_space_high_value;
3149 if (flags & WINED3D_BLT_DST_CKEY)
3151 /* Destination color keys are taken from the source surface! */
3152 destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value;
3153 destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value;
3155 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
3157 destkeylow = fx->dst_color_key.color_space_low_value;
3158 destkeyhigh = fx->dst_color_key.color_space_high_value;
3161 if (bpp == 1)
3163 keymask = 0xff;
3165 else
3167 DWORD masks[3];
3168 get_color_masks(src_format, masks);
3169 keymask = masks[0]
3170 | masks[1]
3171 | masks[2];
3173 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3174 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
3177 if (flags & WINED3D_BLT_FX)
3179 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
3180 LONG tmpxy;
3181 dTopLeft = dbuf;
3182 dTopRight = dbuf + ((dst_width - 1) * bpp);
3183 dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
3184 dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
3186 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
3188 /* I don't think we need to do anything about this flag */
3189 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
3191 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
3193 tmp = dTopRight;
3194 dTopRight = dTopLeft;
3195 dTopLeft = tmp;
3196 tmp = dBottomRight;
3197 dBottomRight = dBottomLeft;
3198 dBottomLeft = tmp;
3199 dstxinc = dstxinc * -1;
3201 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
3203 tmp = dTopLeft;
3204 dTopLeft = dBottomLeft;
3205 dBottomLeft = tmp;
3206 tmp = dTopRight;
3207 dTopRight = dBottomRight;
3208 dBottomRight = tmp;
3209 dstyinc = dstyinc * -1;
3211 if (fx->fx & WINEDDBLTFX_NOTEARING)
3213 /* I don't think we need to do anything about this flag */
3214 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
3216 if (fx->fx & WINEDDBLTFX_ROTATE180)
3218 tmp = dBottomRight;
3219 dBottomRight = dTopLeft;
3220 dTopLeft = tmp;
3221 tmp = dBottomLeft;
3222 dBottomLeft = dTopRight;
3223 dTopRight = tmp;
3224 dstxinc = dstxinc * -1;
3225 dstyinc = dstyinc * -1;
3227 if (fx->fx & WINEDDBLTFX_ROTATE270)
3229 tmp = dTopLeft;
3230 dTopLeft = dBottomLeft;
3231 dBottomLeft = dBottomRight;
3232 dBottomRight = dTopRight;
3233 dTopRight = tmp;
3234 tmpxy = dstxinc;
3235 dstxinc = dstyinc;
3236 dstyinc = tmpxy;
3237 dstxinc = dstxinc * -1;
3239 if (fx->fx & WINEDDBLTFX_ROTATE90)
3241 tmp = dTopLeft;
3242 dTopLeft = dTopRight;
3243 dTopRight = dBottomRight;
3244 dBottomRight = dBottomLeft;
3245 dBottomLeft = tmp;
3246 tmpxy = dstxinc;
3247 dstxinc = dstyinc;
3248 dstyinc = tmpxy;
3249 dstyinc = dstyinc * -1;
3251 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
3253 /* I don't think we need to do anything about this flag */
3254 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
3256 dbuf = dTopLeft;
3257 flags &= ~(WINED3D_BLT_FX);
3260 #define COPY_COLORKEY_FX(type) \
3261 do { \
3262 const type *s; \
3263 type *d = (type *)dbuf, *dx, tmp; \
3264 for (y = sy = 0; y < dst_height; ++y, sy += yinc) \
3266 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
3267 dx = d; \
3268 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3270 tmp = s[sx >> 16]; \
3271 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
3272 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
3274 dx[0] = tmp; \
3276 dx = (type *)(((BYTE *)dx) + dstxinc); \
3278 d = (type *)(((BYTE *)d) + dstyinc); \
3280 } while(0)
3282 switch (bpp)
3284 case 1:
3285 COPY_COLORKEY_FX(BYTE);
3286 break;
3287 case 2:
3288 COPY_COLORKEY_FX(WORD);
3289 break;
3290 case 4:
3291 COPY_COLORKEY_FX(DWORD);
3292 break;
3293 case 3:
3295 const BYTE *s;
3296 BYTE *d = dbuf, *dx;
3297 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3299 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3300 dx = d;
3301 for (x = sx = 0; x < dst_width; ++x, sx+= xinc)
3303 DWORD pixel, dpixel = 0;
3304 s = sbuf + 3 * (sx>>16);
3305 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3306 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
3307 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
3308 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
3310 dx[0] = (pixel ) & 0xff;
3311 dx[1] = (pixel >> 8) & 0xff;
3312 dx[2] = (pixel >> 16) & 0xff;
3314 dx += dstxinc;
3316 d += dstyinc;
3318 break;
3320 default:
3321 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
3322 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
3323 hr = WINED3DERR_NOTAVAILABLE;
3324 goto error;
3325 #undef COPY_COLORKEY_FX
3330 error:
3331 if (flags)
3332 FIXME(" Unsupported flags %#x.\n", flags);
3334 release:
3335 wined3d_resource_unmap(&dst_texture->resource, dst_sub_resource_idx);
3336 if (src_texture && !same_sub_resource)
3337 wined3d_resource_unmap(&src_texture->resource, src_sub_resource_idx);
3338 if (converted_texture)
3339 wined3d_texture_decref(converted_texture);
3341 return hr;
3344 static void surface_cpu_blt_colour_fill(struct wined3d_rendertarget_view *view,
3345 const struct wined3d_box *box, const struct wined3d_color *colour)
3347 unsigned int x, y, w, h, bpp;
3348 struct wined3d_map_desc map;
3349 BYTE *row;
3350 DWORD c;
3352 TRACE("view %p, box %s, colour %s.\n", view, debug_box(box), debug_color(colour));
3354 if (view->format_flags & WINED3DFMT_FLAG_BLOCKS)
3356 FIXME("Not implemented for format %s.\n", debug_d3dformat(view->format->id));
3357 return;
3360 c = wined3d_format_convert_from_float(view->format, colour);
3361 bpp = view->format->byte_count;
3362 w = box->right - box->left;
3363 h = box->bottom - box->top;
3365 wined3d_resource_map(view->resource, view->sub_resource_idx, &map, box, 0);
3367 switch (bpp)
3369 case 1:
3370 for (x = 0; x < w; ++x)
3372 ((BYTE *)map.data)[x] = c;
3374 break;
3376 case 2:
3377 for (x = 0; x < w; ++x)
3379 ((WORD *)map.data)[x] = c;
3381 break;
3383 case 3:
3385 row = map.data;
3386 for (x = 0; x < w; ++x, row += 3)
3388 row[0] = (c ) & 0xff;
3389 row[1] = (c >> 8) & 0xff;
3390 row[2] = (c >> 16) & 0xff;
3392 break;
3394 case 4:
3395 for (x = 0; x < w; ++x)
3397 ((DWORD *)map.data)[x] = c;
3399 break;
3401 default:
3402 FIXME("Not implemented for bpp %u.\n", bpp);
3403 wined3d_resource_unmap(view->resource, view->sub_resource_idx);
3404 return;
3407 row = map.data;
3408 for (y = 1; y < h; ++y)
3410 row += map.row_pitch;
3411 memcpy(row, map.data, w * bpp);
3413 wined3d_resource_unmap(view->resource, view->sub_resource_idx);
3416 static void cpu_blitter_clear(struct wined3d_blitter *blitter, struct wined3d_device *device,
3417 struct wined3d_rendertarget_view *view, const RECT *rect, DWORD flags,
3418 const struct wined3d_color *colour, float depth, DWORD stencil)
3420 const struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
3421 struct wined3d_color c = {depth, 0.0f, 0.0f, 0.0f};
3423 if (flags == WINED3DCLEAR_TARGET)
3425 surface_cpu_blt_colour_fill(view, &box, colour);
3426 return;
3429 if (flags == WINED3DCLEAR_ZBUFFER)
3431 surface_cpu_blt_colour_fill(view, &box, &c);
3432 return;
3435 FIXME("flags %#x not implemented.\n", flags);
3438 static void cpu_blitter_blit(struct wined3d_blitter *blitter, enum wined3d_blit_op op,
3439 struct wined3d_context *context, struct wined3d_surface *src_surface, DWORD src_location,
3440 const RECT *src_rect, struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect,
3441 const struct wined3d_color_key *color_key, enum wined3d_texture_filter_type filter)
3443 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
3444 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
3445 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
3446 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
3447 struct wined3d_texture *dst_texture = dst_surface->container;
3448 struct wined3d_texture *src_texture = src_surface->container;
3449 struct wined3d_blt_fx fx;
3450 DWORD flags = 0;
3452 memset(&fx, 0, sizeof(fx));
3453 switch (op)
3455 case WINED3D_BLIT_OP_COLOR_BLIT:
3456 case WINED3D_BLIT_OP_DEPTH_BLIT:
3457 break;
3458 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
3459 flags |= WINED3D_BLT_ALPHA_TEST;
3460 break;
3461 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
3462 flags |= WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_FX;
3463 fx.src_color_key = *color_key;
3464 break;
3465 default:
3466 FIXME("Unhandled op %#x.\n", op);
3467 break;
3470 if (FAILED(surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
3471 src_texture, src_sub_resource_idx, &src_box, flags, &fx, filter)))
3472 ERR("Failed to blit.\n");
3473 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
3476 static const struct wined3d_blitter_ops cpu_blitter_ops =
3478 cpu_blitter_destroy,
3479 cpu_blitter_clear,
3480 cpu_blitter_blit,
3483 struct wined3d_blitter *wined3d_cpu_blitter_create(void)
3485 struct wined3d_blitter *blitter;
3487 if (!(blitter = HeapAlloc(GetProcessHeap(), 0, sizeof(*blitter))))
3488 return NULL;
3490 TRACE("Created blitter %p.\n", blitter);
3492 blitter->ops = &cpu_blitter_ops;
3493 blitter->next = NULL;
3495 return blitter;
3498 HRESULT wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3499 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
3500 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
3502 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
3503 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
3504 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
3505 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
3506 struct wined3d_texture *dst_texture = dst_surface->container;
3507 struct wined3d_texture *src_texture = src_surface->container;
3508 struct wined3d_device *device = dst_texture->resource.device;
3509 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3510 DWORD src_ds_flags, dst_ds_flags;
3511 struct wined3d_context *context;
3512 BOOL scale, convert;
3513 DWORD dst_location;
3515 static const DWORD simple_blit = WINED3D_BLT_ASYNC
3516 | WINED3D_BLT_SRC_CKEY
3517 | WINED3D_BLT_SRC_CKEY_OVERRIDE
3518 | WINED3D_BLT_WAIT
3519 | WINED3D_BLT_DO_NOT_WAIT
3520 | WINED3D_BLT_ALPHA_TEST;
3522 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
3523 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3524 flags, fx, debug_d3dtexturefiltertype(filter));
3525 TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage));
3527 if (fx)
3529 TRACE("fx %#x.\n", fx->fx);
3530 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
3531 fx->dst_color_key.color_space_low_value,
3532 fx->dst_color_key.color_space_high_value);
3533 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
3534 fx->src_color_key.color_space_low_value,
3535 fx->src_color_key.color_space_high_value);
3538 if (!fx || !(fx->fx))
3539 flags &= ~WINED3D_BLT_FX;
3541 if (flags & WINED3D_BLT_WAIT)
3542 flags &= ~WINED3D_BLT_WAIT;
3544 if (flags & WINED3D_BLT_ASYNC)
3546 static unsigned int once;
3548 if (!once++)
3549 FIXME("Can't handle WINED3D_BLT_ASYNC flag.\n");
3550 flags &= ~WINED3D_BLT_ASYNC;
3553 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
3554 if (flags & WINED3D_BLT_DO_NOT_WAIT)
3556 static unsigned int once;
3558 if (!once++)
3559 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
3560 flags &= ~WINED3D_BLT_DO_NOT_WAIT;
3563 if (!device->d3d_initialized)
3565 WARN("D3D not initialized, using fallback.\n");
3566 goto cpu;
3569 /* We want to avoid invalidating the sysmem location for converted
3570 * surfaces, since otherwise we'd have to convert the data back when
3571 * locking them. */
3572 if (dst_texture->flags & WINED3D_TEXTURE_CONVERTED || dst_texture->resource.format->convert
3573 || wined3d_format_get_color_key_conversion(dst_texture, TRUE))
3575 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
3576 goto cpu;
3579 if (flags & ~simple_blit)
3581 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
3582 goto fallback;
3585 src_swapchain = src_texture->swapchain;
3586 dst_swapchain = dst_texture->swapchain;
3588 /* This isn't strictly needed. FBO blits for example could deal with
3589 * cross-swapchain blits by first downloading the source to a texture
3590 * before switching to the destination context. We just have this here to
3591 * not have to deal with the issue, since cross-swapchain blits should be
3592 * rare. */
3593 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
3595 FIXME("Using fallback for cross-swapchain blit.\n");
3596 goto fallback;
3599 scale = src_rect->right - src_rect->left != dst_rect->right - dst_rect->left
3600 || src_rect->bottom - src_rect->top != dst_rect->bottom - dst_rect->top;
3601 convert = src_texture->resource.format->id != dst_texture->resource.format->id;
3603 dst_ds_flags = dst_texture->resource.format_flags
3604 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
3605 src_ds_flags = src_texture->resource.format_flags
3606 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
3608 if (src_ds_flags || dst_ds_flags)
3610 TRACE("Depth/stencil blit.\n");
3612 if (dst_texture->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3613 dst_location = dst_texture->resource.map_binding;
3614 else
3615 dst_location = dst_texture->resource.draw_binding;
3617 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3618 device->blitter->ops->blitter_blit(device->blitter, WINED3D_BLIT_OP_DEPTH_BLIT, context,
3619 src_surface, src_texture->resource.draw_binding, src_rect,
3620 dst_surface, dst_location, dst_rect, NULL, filter);
3621 context_release(context);
3623 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, dst_location);
3624 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~dst_location);
3626 return WINED3D_OK;
3628 else
3630 struct wined3d_texture_sub_resource *src_sub_resource, *dst_sub_resource;
3631 enum wined3d_blit_op blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
3632 const struct wined3d_color_key *colour_key = NULL;
3634 TRACE("Colour blit.\n");
3636 dst_sub_resource = surface_get_sub_resource(dst_surface);
3637 src_sub_resource = &src_texture->sub_resources[src_sub_resource_idx];
3639 /* In principle this would apply to depth blits as well, but we don't
3640 * implement those in the CPU blitter at the moment. */
3641 if ((dst_sub_resource->locations & dst_texture->resource.map_binding)
3642 && (src_sub_resource->locations & src_texture->resource.map_binding))
3644 if (scale)
3645 TRACE("Not doing sysmem blit because of scaling.\n");
3646 else if (convert)
3647 TRACE("Not doing sysmem blit because of format conversion.\n");
3648 else
3649 goto cpu;
3652 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3654 colour_key = &fx->src_color_key;
3655 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
3657 else if (flags & WINED3D_BLT_SRC_CKEY)
3659 colour_key = &src_texture->async.src_blt_color_key;
3660 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
3662 else if (flags & WINED3D_BLT_ALPHA_TEST)
3664 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
3666 else if ((src_sub_resource->locations & WINED3D_LOCATION_SYSMEM)
3667 && !(dst_sub_resource->locations & WINED3D_LOCATION_SYSMEM))
3669 /* Upload */
3670 if (scale)
3671 TRACE("Not doing upload because of scaling.\n");
3672 else if (convert)
3673 TRACE("Not doing upload because of format conversion.\n");
3674 else
3676 POINT dst_point = {dst_rect->left, dst_rect->top};
3678 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, src_rect)))
3680 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
3682 context = context_acquire(device,
3683 dst_texture, dst_sub_resource_idx);
3684 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx,
3685 context, dst_texture->resource.draw_binding);
3686 context_release(context);
3688 return WINED3D_OK;
3692 else if (dst_swapchain && dst_swapchain->back_buffers
3693 && dst_texture == dst_swapchain->front_buffer
3694 && src_texture == dst_swapchain->back_buffers[0])
3696 /* Use present for back -> front blits. The idea behind this is
3697 * that present is potentially faster than a blit, in particular
3698 * when FBO blits aren't available. Some ddraw applications like
3699 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
3700 * to the frontbuffer instead of doing a Flip(). D3d8 and d3d9
3701 * applications can't blit directly to the frontbuffer. */
3702 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
3704 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
3706 /* Set the swap effect to COPY, we don't want the backbuffer to
3707 * become undefined. */
3708 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
3709 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, 0);
3710 dst_swapchain->desc.swap_effect = swap_effect;
3712 return WINED3D_OK;
3715 if (dst_texture->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3716 dst_location = dst_texture->resource.map_binding;
3717 else
3718 dst_location = dst_texture->resource.draw_binding;
3720 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
3721 device->blitter->ops->blitter_blit(device->blitter, blit_op, context,
3722 src_surface, src_texture->resource.draw_binding, src_rect,
3723 dst_surface, dst_location, dst_rect, colour_key, filter);
3724 context_release(context);
3726 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, dst_location);
3727 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~dst_location);
3729 return WINED3D_OK;
3732 fallback:
3733 /* Special cases for render targets. */
3734 if (SUCCEEDED(surface_blt_special(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter)))
3735 return WINED3D_OK;
3737 cpu:
3738 return surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
3739 src_texture, src_sub_resource_idx, &src_box, flags, fx, filter);