wined3d: Return a BOOL from surface_load_location().
[wine.git] / dlls / wined3d / surface.c
blob96819a65c3f858b0e215f467a48b1404083f44ed
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 void wined3d_surface_destroy_dc(struct wined3d_surface *surface)
257 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
258 struct wined3d_texture *texture = surface->container;
259 struct wined3d_device *device = texture->resource.device;
260 const struct wined3d_gl_info *gl_info = NULL;
261 D3DKMT_DESTROYDCFROMMEMORY destroy_desc;
262 struct wined3d_context *context = NULL;
263 struct wined3d_bo_address data;
264 NTSTATUS status;
266 if (!surface->dc)
268 ERR("Surface %p has no DC.\n", surface);
269 return;
272 TRACE("dc %p, bitmap %p.\n", surface->dc, surface->bitmap);
274 destroy_desc.hDc = surface->dc;
275 destroy_desc.hBitmap = surface->bitmap;
276 if ((status = D3DKMTDestroyDCFromMemory(&destroy_desc)))
277 ERR("Failed to destroy dc, status %#x.\n", status);
278 surface->dc = NULL;
279 surface->bitmap = NULL;
281 if (device->d3d_initialized)
283 context = context_acquire(device, NULL, 0);
284 gl_info = context->gl_info;
287 wined3d_texture_get_memory(texture, sub_resource_idx, &data, texture->resource.map_binding);
288 wined3d_texture_unmap_bo_address(&data, gl_info, GL_PIXEL_UNPACK_BUFFER);
290 if (context)
291 context_release(context);
294 HRESULT wined3d_surface_create_dc(struct wined3d_surface *surface)
296 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
297 struct wined3d_texture *texture = surface->container;
298 const struct wined3d_format *format = texture->resource.format;
299 struct wined3d_device *device = texture->resource.device;
300 const struct wined3d_gl_info *gl_info = NULL;
301 struct wined3d_context *context = NULL;
302 unsigned int row_pitch, slice_pitch;
303 struct wined3d_bo_address data;
304 D3DKMT_CREATEDCFROMMEMORY desc;
305 NTSTATUS status;
307 TRACE("surface %p.\n", surface);
309 if (!format->ddi_format)
311 WARN("Cannot create a DC for format %s.\n", debug_d3dformat(format->id));
312 return WINED3DERR_INVALIDCALL;
315 wined3d_texture_get_pitch(texture, surface->texture_level, &row_pitch, &slice_pitch);
317 if (device->d3d_initialized)
319 context = context_acquire(device, NULL, 0);
320 gl_info = context->gl_info;
323 wined3d_texture_get_memory(texture, sub_resource_idx, &data, texture->resource.map_binding);
324 desc.pMemory = wined3d_texture_map_bo_address(&data, texture->sub_resources[sub_resource_idx].size,
325 gl_info, GL_PIXEL_UNPACK_BUFFER, 0);
327 if (context)
328 context_release(context);
330 desc.Format = format->ddi_format;
331 desc.Width = wined3d_texture_get_level_width(texture, surface->texture_level);
332 desc.Height = wined3d_texture_get_level_height(texture, surface->texture_level);
333 desc.Pitch = row_pitch;
334 desc.hDeviceDc = CreateCompatibleDC(NULL);
335 desc.pColorTable = NULL;
337 status = D3DKMTCreateDCFromMemory(&desc);
338 DeleteDC(desc.hDeviceDc);
339 if (status)
341 WARN("Failed to create DC, status %#x.\n", status);
342 return WINED3DERR_INVALIDCALL;
345 surface->dc = desc.hDc;
346 surface->bitmap = desc.hBitmap;
348 TRACE("Created DC %p, bitmap %p for surface %p.\n", surface->dc, surface->bitmap, surface);
350 return WINED3D_OK;
353 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
355 unsigned int t;
357 t = wined3d_texture_get_level_width(surface->container, surface->texture_level);
358 if ((r->left && r->right) || abs(r->right - r->left) != t)
359 return FALSE;
360 t = wined3d_texture_get_level_height(surface->container, surface->texture_level);
361 if ((r->top && r->bottom) || abs(r->bottom - r->top) != t)
362 return FALSE;
363 return TRUE;
366 static void surface_depth_blt_fbo(const struct wined3d_device *device,
367 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
368 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
370 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
371 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
372 struct wined3d_texture *dst_texture = dst_surface->container;
373 struct wined3d_texture *src_texture = src_surface->container;
374 const struct wined3d_gl_info *gl_info;
375 struct wined3d_context *context;
376 DWORD src_mask, dst_mask;
377 GLbitfield gl_mask;
379 TRACE("device %p\n", device);
380 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
381 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
382 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
383 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
385 src_mask = src_texture->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
386 dst_mask = dst_texture->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
388 if (src_mask != dst_mask)
390 ERR("Incompatible formats %s and %s.\n",
391 debug_d3dformat(src_texture->resource.format->id),
392 debug_d3dformat(dst_texture->resource.format->id));
393 return;
396 if (!src_mask)
398 ERR("Not a depth / stencil format: %s.\n",
399 debug_d3dformat(src_texture->resource.format->id));
400 return;
403 gl_mask = 0;
404 if (src_mask & WINED3DFMT_FLAG_DEPTH)
405 gl_mask |= GL_DEPTH_BUFFER_BIT;
406 if (src_mask & WINED3DFMT_FLAG_STENCIL)
407 gl_mask |= GL_STENCIL_BUFFER_BIT;
409 context = context_acquire(device, NULL, 0);
410 if (!context->valid)
412 context_release(context);
413 WARN("Invalid context, skipping blit.\n");
414 return;
417 /* Make sure the locations are up-to-date. Loading the destination
418 * surface isn't required if the entire surface is overwritten. */
419 wined3d_texture_load_location(src_texture, src_sub_resource_idx, context, src_location);
420 if (!surface_is_full_rect(dst_surface, dst_rect))
421 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, dst_location);
422 else
423 wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, dst_location);
425 gl_info = context->gl_info;
427 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
428 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
430 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
431 context_set_draw_buffer(context, GL_NONE);
432 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
433 context_invalidate_state(context, STATE_FRAMEBUFFER);
435 if (gl_mask & GL_DEPTH_BUFFER_BIT)
437 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
438 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
440 if (gl_mask & GL_STENCIL_BUFFER_BIT)
442 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
444 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
445 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
447 gl_info->gl_ops.gl.p_glStencilMask(~0U);
448 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
451 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
452 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
454 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
455 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
456 checkGLcall("glBlitFramebuffer()");
458 if (wined3d_settings.strict_draw_ordering)
459 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
461 context_release(context);
464 /* Blit between surface locations. Onscreen on different swapchains is not supported.
465 * Depth / stencil is not supported. Context activation is done by the caller. */
466 static void surface_blt_fbo(const struct wined3d_device *device,
467 struct wined3d_context *old_ctx, enum wined3d_texture_filter_type filter,
468 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
469 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
471 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
472 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
473 struct wined3d_texture *dst_texture = dst_surface->container;
474 struct wined3d_texture *src_texture = src_surface->container;
475 const struct wined3d_gl_info *gl_info;
476 struct wined3d_context *context = old_ctx;
477 struct wined3d_surface *required_rt, *restore_rt = NULL;
478 RECT src_rect, dst_rect;
479 GLenum gl_filter;
480 GLenum buffer;
482 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
483 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
484 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
485 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
486 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
488 src_rect = *src_rect_in;
489 dst_rect = *dst_rect_in;
491 switch (filter)
493 case WINED3D_TEXF_LINEAR:
494 gl_filter = GL_LINEAR;
495 break;
497 default:
498 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
499 case WINED3D_TEXF_NONE:
500 case WINED3D_TEXF_POINT:
501 gl_filter = GL_NEAREST;
502 break;
505 /* Resolve the source surface first if needed. */
506 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
507 && (src_texture->resource.format->id != dst_texture->resource.format->id
508 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
509 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
510 src_location = WINED3D_LOCATION_RB_RESOLVED;
512 /* Make sure the locations are up-to-date. Loading the destination
513 * surface isn't required if the entire surface is overwritten. (And is
514 * in fact harmful if we're being called by surface_load_location() with
515 * the purpose of loading the destination surface.) */
516 wined3d_texture_load_location(src_texture, src_sub_resource_idx, old_ctx, src_location);
517 if (!surface_is_full_rect(dst_surface, &dst_rect))
518 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, old_ctx, dst_location);
519 else
520 wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, old_ctx, dst_location);
523 if (src_location == WINED3D_LOCATION_DRAWABLE) required_rt = src_surface;
524 else if (dst_location == WINED3D_LOCATION_DRAWABLE) required_rt = dst_surface;
525 else required_rt = NULL;
527 restore_rt = context_get_rt_surface(old_ctx);
528 if (restore_rt != required_rt)
529 context = context_acquire(device, required_rt ? required_rt->container : NULL,
530 required_rt ? surface_get_sub_resource_idx(required_rt) : 0);
531 else
532 restore_rt = NULL;
534 if (!context->valid)
536 context_release(context);
537 WARN("Invalid context, skipping blit.\n");
538 return;
541 gl_info = context->gl_info;
543 if (src_location == WINED3D_LOCATION_DRAWABLE)
545 TRACE("Source surface %p is onscreen.\n", src_surface);
546 buffer = wined3d_texture_get_gl_buffer(src_texture);
547 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
549 else
551 TRACE("Source surface %p is offscreen.\n", src_surface);
552 buffer = GL_COLOR_ATTACHMENT0;
555 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
556 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
557 checkGLcall("glReadBuffer()");
558 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
560 if (dst_location == WINED3D_LOCATION_DRAWABLE)
562 TRACE("Destination surface %p is onscreen.\n", dst_surface);
563 buffer = wined3d_texture_get_gl_buffer(dst_texture);
564 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
566 else
568 TRACE("Destination surface %p is offscreen.\n", dst_surface);
569 buffer = GL_COLOR_ATTACHMENT0;
572 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
573 context_set_draw_buffer(context, buffer);
574 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
575 context_invalidate_state(context, STATE_FRAMEBUFFER);
577 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
578 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
579 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
580 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
581 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
583 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
584 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
586 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
587 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
588 checkGLcall("glBlitFramebuffer()");
590 if (wined3d_settings.strict_draw_ordering || (dst_location == WINED3D_LOCATION_DRAWABLE
591 && dst_texture->swapchain->front_buffer == dst_texture))
592 gl_info->gl_ops.gl.p_glFlush();
594 if (restore_rt)
595 context_restore(context, restore_rt);
598 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
599 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
600 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
602 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
603 return FALSE;
605 /* Source and/or destination need to be on the GL side */
606 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
607 return FALSE;
609 switch (blit_op)
611 case WINED3D_BLIT_OP_COLOR_BLIT:
612 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
613 || (src_usage & WINED3DUSAGE_RENDERTARGET)))
614 return FALSE;
615 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
616 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
617 return FALSE;
618 if (!(src_format->id == dst_format->id
619 || (is_identity_fixup(src_format->color_fixup)
620 && is_identity_fixup(dst_format->color_fixup))))
621 return FALSE;
622 break;
624 case WINED3D_BLIT_OP_DEPTH_BLIT:
625 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
626 return FALSE;
627 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
628 return FALSE;
629 /* Accept pure swizzle fixups for depth formats. In general we
630 * ignore the stencil component (if present) at the moment and the
631 * swizzle is not relevant with just the depth component. */
632 if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup)
633 || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup))
634 return FALSE;
635 break;
637 default:
638 return FALSE;
641 return TRUE;
644 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
646 const struct wined3d_format *format = surface->container->resource.format;
648 switch (format->id)
650 case WINED3DFMT_S1_UINT_D15_UNORM:
651 *float_depth = depth / (float)0x00007fff;
652 break;
654 case WINED3DFMT_D16_UNORM:
655 *float_depth = depth / (float)0x0000ffff;
656 break;
658 case WINED3DFMT_D24_UNORM_S8_UINT:
659 case WINED3DFMT_X8D24_UNORM:
660 *float_depth = depth / (float)0x00ffffff;
661 break;
663 case WINED3DFMT_D32_UNORM:
664 *float_depth = depth / (float)0xffffffff;
665 break;
667 default:
668 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
669 return FALSE;
672 return TRUE;
675 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
677 struct wined3d_resource *resource = &surface->container->resource;
678 struct wined3d_device *device = resource->device;
679 struct wined3d_rendertarget_view *view;
680 struct wined3d_view_desc view_desc;
681 const struct blit_shader *blitter;
682 HRESULT hr;
684 if (!(blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info,
685 WINED3D_BLIT_OP_DEPTH_FILL, NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format)))
687 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
688 return WINED3DERR_INVALIDCALL;
691 view_desc.format_id = resource->format->id;
692 view_desc.flags = 0;
693 view_desc.u.texture.level_idx = surface->texture_level;
694 view_desc.u.texture.level_count = 1;
695 view_desc.u.texture.layer_idx = surface->texture_layer;
696 view_desc.u.texture.layer_count = 1;
697 if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc,
698 resource, NULL, &wined3d_null_parent_ops, &view)))
700 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
701 return hr;
704 hr = blitter->depth_fill(device, view, rect, WINED3DCLEAR_ZBUFFER, depth, 0);
705 wined3d_rendertarget_view_decref(view);
707 return hr;
710 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
711 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
713 struct wined3d_texture *src_texture = src_surface->container;
714 struct wined3d_texture *dst_texture = dst_surface->container;
715 struct wined3d_device *device = src_texture->resource.device;
716 unsigned int dst_sub_resource_idx;
718 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
719 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
720 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format))
721 return WINED3DERR_INVALIDCALL;
723 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
725 dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
726 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, dst_location);
727 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~dst_location);
729 return WINED3D_OK;
732 /* This call just downloads data, the caller is responsible for binding the
733 * correct texture. */
734 /* Context activation is done by the caller. */
735 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
736 DWORD dst_location)
738 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
739 struct wined3d_texture *texture = surface->container;
740 const struct wined3d_format *format = texture->resource.format;
741 struct wined3d_texture_sub_resource *sub_resource;
742 unsigned int dst_row_pitch, dst_slice_pitch;
743 unsigned int src_row_pitch, src_slice_pitch;
744 struct wined3d_bo_address data;
745 BYTE *temporary_mem = NULL;
746 void *mem;
748 /* Only support read back of converted P8 surfaces. */
749 if (texture->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
751 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
752 return;
755 sub_resource = &texture->sub_resources[sub_resource_idx];
757 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
759 /* NP2 emulation is not allowed on array textures. */
760 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
761 ERR("Array texture %p uses NP2 emulation.\n", texture);
763 WARN_(d3d_perf)("Downloading all miplevel layers to get the surface data for a single sub-resource.\n");
765 if (!(temporary_mem = wined3d_calloc(texture->layer_count, sub_resource->size)))
767 ERR("Out of memory.\n");
768 return;
772 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
774 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
776 wined3d_texture_get_pitch(texture, surface->texture_level, &dst_row_pitch, &dst_slice_pitch);
777 wined3d_format_calculate_pitch(format, texture->resource.device->surface_alignment,
778 wined3d_texture_get_level_pow2_width(texture, surface->texture_level),
779 wined3d_texture_get_level_pow2_height(texture, surface->texture_level),
780 &src_row_pitch, &src_slice_pitch);
781 if (!(temporary_mem = HeapAlloc(GetProcessHeap(), 0, src_slice_pitch)))
783 ERR("Out of memory.\n");
784 return;
787 if (data.buffer_object)
788 ERR("NP2 emulated texture uses PBO unexpectedly.\n");
789 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
790 ERR("Unexpected compressed format for NP2 emulated texture.\n");
793 if (temporary_mem)
795 mem = temporary_mem;
797 else if (data.buffer_object)
799 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
800 checkGLcall("glBindBuffer");
801 mem = data.addr;
803 else
805 mem = data.addr;
808 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
810 TRACE("Downloading compressed surface %p, level %u, format %#x, type %#x, data %p.\n",
811 surface, surface->texture_level, format->glFormat, format->glType, mem);
813 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target, surface->texture_level, mem));
814 checkGLcall("glGetCompressedTexImage");
816 else
818 TRACE("Downloading surface %p, level %u, format %#x, type %#x, data %p.\n",
819 surface, surface->texture_level, format->glFormat, format->glType, mem);
821 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
822 format->glFormat, format->glType, mem);
823 checkGLcall("glGetTexImage");
826 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
828 const BYTE *src_data;
829 unsigned int h, y;
830 BYTE *dst_data;
832 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
833 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
834 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
836 * We're doing this...
838 * instead of boxing the texture :
839 * |<-texture width ->| -->pow2width| /\
840 * |111111111111111111| | |
841 * |222 Texture 222222| boxed empty | texture height
842 * |3333 Data 33333333| | |
843 * |444444444444444444| | \/
844 * ----------------------------------- |
845 * | boxed empty | boxed empty | pow2height
846 * | | | \/
847 * -----------------------------------
850 * we're repacking the data to the expected texture width
852 * |<-texture width ->| -->pow2width| /\
853 * |111111111111111111222222222222222| |
854 * |222333333333333333333444444444444| texture height
855 * |444444 | |
856 * | | \/
857 * | | |
858 * | empty | pow2height
859 * | | \/
860 * -----------------------------------
862 * == is the same as
864 * |<-texture width ->| /\
865 * |111111111111111111|
866 * |222222222222222222|texture height
867 * |333333333333333333|
868 * |444444444444444444| \/
869 * --------------------
871 * This also means that any references to surface memory should work with the data as if it were a
872 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
874 * internally the texture is still stored in a boxed format so any references to textureName will
875 * get a boxed texture with width pow2width and not a texture of width resource.width. */
876 src_data = mem;
877 dst_data = data.addr;
878 TRACE("Repacking the surface data from pitch %u to pitch %u.\n", src_row_pitch, dst_row_pitch);
879 h = wined3d_texture_get_level_height(texture, surface->texture_level);
880 for (y = 0; y < h; ++y)
882 memcpy(dst_data, src_data, dst_row_pitch);
883 src_data += src_row_pitch;
884 dst_data += dst_row_pitch;
887 else if (temporary_mem)
889 void *src_data = temporary_mem + surface->texture_layer * sub_resource->size;
890 if (data.buffer_object)
892 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
893 checkGLcall("glBindBuffer");
894 GL_EXTCALL(glBufferSubData(GL_PIXEL_PACK_BUFFER, 0, sub_resource->size, src_data));
895 checkGLcall("glBufferSubData");
897 else
899 memcpy(data.addr, src_data, sub_resource->size);
903 if (data.buffer_object)
905 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
906 checkGLcall("glBindBuffer");
909 HeapFree(GetProcessHeap(), 0, temporary_mem);
912 /* This call just uploads data, the caller is responsible for binding the
913 * correct texture. */
914 /* Context activation is done by the caller. */
915 void wined3d_surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
916 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
917 BOOL srgb, const struct wined3d_const_bo_address *data)
919 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
920 struct wined3d_texture *texture = surface->container;
921 UINT update_w = src_rect->right - src_rect->left;
922 UINT update_h = src_rect->bottom - src_rect->top;
924 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
925 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
926 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
928 if (texture->sub_resources[sub_resource_idx].map_count)
930 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
931 texture->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
934 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE)
936 update_h *= format->height_scale.numerator;
937 update_h /= format->height_scale.denominator;
940 if (data->buffer_object)
942 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
943 checkGLcall("glBindBuffer");
946 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
948 unsigned int dst_row_pitch, dst_slice_pitch;
949 const BYTE *addr = data->addr;
950 GLenum internal;
952 addr += (src_rect->top / format->block_height) * src_pitch;
953 addr += (src_rect->left / format->block_width) * format->block_byte_count;
955 if (srgb)
956 internal = format->glGammaInternal;
957 else if (texture->resource.usage & WINED3DUSAGE_RENDERTARGET
958 && wined3d_resource_is_offscreen(&texture->resource))
959 internal = format->rtInternal;
960 else
961 internal = format->glInternal;
963 wined3d_format_calculate_pitch(format, 1, update_w, update_h, &dst_row_pitch, &dst_slice_pitch);
965 TRACE("Uploading compressed data, target %#x, level %u, layer %u, x %d, y %d, w %u, h %u, "
966 "format %#x, image_size %#x, addr %p.\n",
967 surface->texture_target, surface->texture_level, surface->texture_layer,
968 dst_point->x, dst_point->y, update_w, update_h, internal, dst_slice_pitch, addr);
970 if (dst_row_pitch == src_pitch)
972 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
974 GL_EXTCALL(glCompressedTexSubImage3D(surface->texture_target, surface->texture_level,
975 dst_point->x, dst_point->y, surface->texture_layer, update_w, update_h, 1,
976 internal, dst_slice_pitch, addr));
978 else
980 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
981 dst_point->x, dst_point->y, update_w, update_h,
982 internal, dst_slice_pitch, addr));
985 else
987 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
988 UINT row, y;
990 /* glCompressedTexSubImage2D() ignores pixel store state, so we
991 * can't use the unpack row length like for glTexSubImage2D. */
992 for (row = 0, y = dst_point->y; row < row_count; ++row)
994 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
996 GL_EXTCALL(glCompressedTexSubImage3D(surface->texture_target, surface->texture_level,
997 dst_point->x, y, surface->texture_layer, update_w, format->block_height, 1,
998 internal, dst_row_pitch, addr));
1000 else
1002 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1003 dst_point->x, y, update_w, format->block_height, internal, dst_row_pitch, addr));
1006 y += format->block_height;
1007 addr += src_pitch;
1010 checkGLcall("Upload compressed surface data");
1012 else
1014 const BYTE *addr = data->addr;
1016 addr += src_rect->top * src_pitch;
1017 addr += src_rect->left * format->byte_count;
1019 TRACE("Uploading data, target %#x, level %u, layer %u, x %d, y %d, w %u, h %u, "
1020 "format %#x, type %#x, addr %p.\n",
1021 surface->texture_target, surface->texture_level, surface->texture_layer,
1022 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1024 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1025 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
1027 GL_EXTCALL(glTexSubImage3D(surface->texture_target, surface->texture_level,
1028 dst_point->x, dst_point->y, surface->texture_layer, update_w, update_h, 1,
1029 format->glFormat, format->glType, addr));
1031 else
1033 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1034 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1036 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1037 checkGLcall("Upload surface data");
1040 if (data->buffer_object)
1042 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
1043 checkGLcall("glBindBuffer");
1046 if (wined3d_settings.strict_draw_ordering)
1047 gl_info->gl_ops.gl.p_glFlush();
1049 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1051 struct wined3d_device *device = texture->resource.device;
1052 unsigned int i;
1054 for (i = 0; i < device->context_count; ++i)
1056 context_surface_update(device->contexts[i], surface);
1061 static BOOL surface_check_block_align_rect(struct wined3d_surface *surface, const RECT *rect)
1063 struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
1065 return wined3d_texture_check_block_align(surface->container, surface->texture_level, &box);
1068 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1069 struct wined3d_surface *src_surface, const RECT *src_rect)
1071 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
1072 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1073 struct wined3d_texture *src_texture = src_surface->container;
1074 struct wined3d_texture *dst_texture = dst_surface->container;
1075 unsigned int src_row_pitch, src_slice_pitch;
1076 const struct wined3d_format *src_format;
1077 const struct wined3d_format *dst_format;
1078 unsigned int src_fmt_flags, dst_fmt_flags;
1079 const struct wined3d_gl_info *gl_info;
1080 struct wined3d_context *context;
1081 struct wined3d_bo_address data;
1082 UINT update_w, update_h;
1083 UINT dst_w, dst_h;
1084 RECT r, dst_rect;
1085 POINT p;
1087 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1088 dst_surface, wine_dbgstr_point(dst_point),
1089 src_surface, wine_dbgstr_rect(src_rect));
1091 src_format = src_texture->resource.format;
1092 dst_format = dst_texture->resource.format;
1093 src_fmt_flags = src_texture->resource.format_flags;
1094 dst_fmt_flags = dst_texture->resource.format_flags;
1096 if (src_format->id != dst_format->id)
1098 WARN("Source and destination surfaces should have the same format.\n");
1099 return WINED3DERR_INVALIDCALL;
1102 if (!dst_point)
1104 p.x = 0;
1105 p.y = 0;
1106 dst_point = &p;
1108 else if (dst_point->x < 0 || dst_point->y < 0)
1110 WARN("Invalid destination point.\n");
1111 return WINED3DERR_INVALIDCALL;
1114 if (!src_rect)
1116 SetRect(&r, 0, 0, wined3d_texture_get_level_width(src_texture, src_surface->texture_level),
1117 wined3d_texture_get_level_height(src_texture, src_surface->texture_level));
1118 src_rect = &r;
1120 else if (src_rect->left < 0 || src_rect->top < 0 || IsRectEmpty(src_rect))
1122 WARN("Invalid source rectangle.\n");
1123 return WINED3DERR_INVALIDCALL;
1126 dst_w = wined3d_texture_get_level_width(dst_texture, dst_surface->texture_level);
1127 dst_h = wined3d_texture_get_level_height(dst_texture, dst_surface->texture_level);
1129 update_w = src_rect->right - src_rect->left;
1130 update_h = src_rect->bottom - src_rect->top;
1132 if (update_w > dst_w || dst_point->x > dst_w - update_w
1133 || update_h > dst_h || dst_point->y > dst_h - update_h)
1135 WARN("Destination out of bounds.\n");
1136 return WINED3DERR_INVALIDCALL;
1139 if ((src_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(src_surface, src_rect))
1141 WARN("Source rectangle not block-aligned.\n");
1142 return WINED3DERR_INVALIDCALL;
1145 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1146 if ((dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(dst_surface, &dst_rect))
1148 WARN("Destination rectangle not block-aligned.\n");
1149 return WINED3DERR_INVALIDCALL;
1152 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1153 if (dst_format->convert || wined3d_format_get_color_key_conversion(dst_texture, FALSE))
1154 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1156 context = context_acquire(dst_texture->resource.device, NULL, 0);
1157 gl_info = context->gl_info;
1159 /* Only load the surface for partial updates. For newly allocated texture
1160 * the texture wouldn't be the current location, and we'd upload zeroes
1161 * just to overwrite them again. */
1162 if (update_w == dst_w && update_h == dst_h)
1163 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
1164 else
1165 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
1166 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
1168 wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &data,
1169 src_texture->sub_resources[src_sub_resource_idx].locations);
1170 wined3d_texture_get_pitch(src_texture, src_surface->texture_level, &src_row_pitch, &src_slice_pitch);
1172 wined3d_surface_upload_data(dst_surface, gl_info, src_format, src_rect,
1173 src_row_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
1175 context_release(context);
1177 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1178 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1180 return WINED3D_OK;
1183 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1184 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1185 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1186 /* Context activation is done by the caller. */
1187 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1189 const struct wined3d_gl_info *gl_info = &surface->container->resource.device->adapter->gl_info;
1190 struct wined3d_renderbuffer_entry *entry;
1191 GLuint renderbuffer = 0;
1192 unsigned int src_width, src_height;
1193 unsigned int width, height;
1195 if (rt && rt->container->resource.format->id != WINED3DFMT_NULL)
1197 width = wined3d_texture_get_level_pow2_width(rt->container, rt->texture_level);
1198 height = wined3d_texture_get_level_pow2_height(rt->container, rt->texture_level);
1200 else
1202 width = wined3d_texture_get_level_pow2_width(surface->container, surface->texture_level);
1203 height = wined3d_texture_get_level_pow2_height(surface->container, surface->texture_level);
1206 src_width = wined3d_texture_get_level_pow2_width(surface->container, surface->texture_level);
1207 src_height = wined3d_texture_get_level_pow2_height(surface->container, surface->texture_level);
1209 /* A depth stencil smaller than the render target is not valid */
1210 if (width > src_width || height > src_height) return;
1212 /* Remove any renderbuffer set if the sizes match */
1213 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1214 || (width == src_width && height == src_height))
1216 surface->current_renderbuffer = NULL;
1217 return;
1220 /* Look if we've already got a renderbuffer of the correct dimensions */
1221 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1223 if (entry->width == width && entry->height == height)
1225 renderbuffer = entry->id;
1226 surface->current_renderbuffer = entry;
1227 break;
1231 if (!renderbuffer)
1233 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1234 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1235 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1236 surface->container->resource.format->glInternal, width, height);
1238 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1239 entry->width = width;
1240 entry->height = height;
1241 entry->id = renderbuffer;
1242 list_add_head(&surface->renderbuffers, &entry->entry);
1244 surface->current_renderbuffer = entry;
1247 checkGLcall("set_compatible_renderbuffer");
1250 /* See also float_16_to_32() in wined3d_private.h */
1251 static inline unsigned short float_32_to_16(const float *in)
1253 int exp = 0;
1254 float tmp = fabsf(*in);
1255 unsigned int mantissa;
1256 unsigned short ret;
1258 /* Deal with special numbers */
1259 if (*in == 0.0f)
1260 return 0x0000;
1261 if (isnan(*in))
1262 return 0x7c01;
1263 if (isinf(*in))
1264 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1266 if (tmp < (float)(1u << 10))
1270 tmp = tmp * 2.0f;
1271 exp--;
1272 } while (tmp < (float)(1u << 10));
1274 else if (tmp >= (float)(1u << 11))
1278 tmp /= 2.0f;
1279 exp++;
1280 } while (tmp >= (float)(1u << 11));
1283 mantissa = (unsigned int)tmp;
1284 if (tmp - mantissa >= 0.5f)
1285 ++mantissa; /* Round to nearest, away from zero. */
1287 exp += 10; /* Normalize the mantissa. */
1288 exp += 15; /* Exponent is encoded with excess 15. */
1290 if (exp > 30) /* too big */
1292 ret = 0x7c00; /* INF */
1294 else if (exp <= 0)
1296 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1297 while (exp <= 0)
1299 mantissa = mantissa >> 1;
1300 ++exp;
1302 ret = mantissa & 0x3ff;
1304 else
1306 ret = (exp << 10) | (mantissa & 0x3ff);
1309 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1310 return ret;
1313 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
1314 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1316 unsigned short *dst_s;
1317 const float *src_f;
1318 unsigned int x, y;
1320 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1322 for (y = 0; y < h; ++y)
1324 src_f = (const float *)(src + y * pitch_in);
1325 dst_s = (unsigned short *) (dst + y * pitch_out);
1326 for (x = 0; x < w; ++x)
1328 dst_s[x] = float_32_to_16(src_f + x);
1333 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
1334 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1336 static const unsigned char convert_5to8[] =
1338 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
1339 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
1340 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
1341 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
1343 static const unsigned char convert_6to8[] =
1345 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
1346 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
1347 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
1348 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
1349 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
1350 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
1351 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
1352 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
1354 unsigned int x, y;
1356 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1358 for (y = 0; y < h; ++y)
1360 const WORD *src_line = (const WORD *)(src + y * pitch_in);
1361 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1362 for (x = 0; x < w; ++x)
1364 WORD pixel = src_line[x];
1365 dst_line[x] = 0xff000000u
1366 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
1367 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
1368 | convert_5to8[(pixel & 0x001fu)];
1373 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
1374 * in both cases we're just setting the X / Alpha channel to 0xff. */
1375 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
1376 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1378 unsigned int x, y;
1380 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1382 for (y = 0; y < h; ++y)
1384 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
1385 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1387 for (x = 0; x < w; ++x)
1389 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
1394 static inline BYTE cliptobyte(int x)
1396 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
1399 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
1400 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1402 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1403 unsigned int x, y;
1405 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1407 for (y = 0; y < h; ++y)
1409 const BYTE *src_line = src + y * pitch_in;
1410 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1411 for (x = 0; x < w; ++x)
1413 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1414 * C = Y - 16; D = U - 128; E = V - 128;
1415 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1416 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1417 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1418 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1419 * U and V are shared between the pixels. */
1420 if (!(x & 1)) /* For every even pixel, read new U and V. */
1422 d = (int) src_line[1] - 128;
1423 e = (int) src_line[3] - 128;
1424 r2 = 409 * e + 128;
1425 g2 = - 100 * d - 208 * e + 128;
1426 b2 = 516 * d + 128;
1428 c2 = 298 * ((int) src_line[0] - 16);
1429 dst_line[x] = 0xff000000
1430 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
1431 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
1432 | cliptobyte((c2 + b2) >> 8); /* blue */
1433 /* Scale RGB values to 0..255 range,
1434 * then clip them if still not in range (may be negative),
1435 * then shift them within DWORD if necessary. */
1436 src_line += 2;
1441 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
1442 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1444 unsigned int x, y;
1445 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1447 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
1449 for (y = 0; y < h; ++y)
1451 const BYTE *src_line = src + y * pitch_in;
1452 WORD *dst_line = (WORD *)(dst + y * pitch_out);
1453 for (x = 0; x < w; ++x)
1455 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1456 * C = Y - 16; D = U - 128; E = V - 128;
1457 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1458 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1459 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1460 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1461 * U and V are shared between the pixels. */
1462 if (!(x & 1)) /* For every even pixel, read new U and V. */
1464 d = (int) src_line[1] - 128;
1465 e = (int) src_line[3] - 128;
1466 r2 = 409 * e + 128;
1467 g2 = - 100 * d - 208 * e + 128;
1468 b2 = 516 * d + 128;
1470 c2 = 298 * ((int) src_line[0] - 16);
1471 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
1472 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
1473 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
1474 /* Scale RGB values to 0..255 range,
1475 * then clip them if still not in range (may be negative),
1476 * then shift them within DWORD if necessary. */
1477 src_line += 2;
1482 struct d3dfmt_converter_desc
1484 enum wined3d_format_id from, to;
1485 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
1488 static const struct d3dfmt_converter_desc converters[] =
1490 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
1491 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
1492 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1493 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1494 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
1495 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
1498 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
1499 enum wined3d_format_id to)
1501 unsigned int i;
1503 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
1505 if (converters[i].from == from && converters[i].to == to)
1506 return &converters[i];
1509 return NULL;
1512 static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture,
1513 unsigned int sub_resource_idx, const struct wined3d_format *dst_format)
1515 unsigned int texture_level = sub_resource_idx % src_texture->level_count;
1516 const struct wined3d_format *src_format = src_texture->resource.format;
1517 struct wined3d_device *device = src_texture->resource.device;
1518 const struct d3dfmt_converter_desc *conv = NULL;
1519 struct wined3d_texture *dst_texture;
1520 struct wined3d_resource_desc desc;
1521 struct wined3d_map_desc src_map;
1523 if (!(conv = find_converter(src_format->id, dst_format->id)) && (!device->d3d_initialized
1524 || !is_identity_fixup(src_format->color_fixup) || src_format->convert
1525 || !is_identity_fixup(dst_format->color_fixup) || dst_format->convert
1526 || (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)))
1528 FIXME("Cannot find a conversion function from format %s to %s.\n",
1529 debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
1530 return NULL;
1533 /* FIXME: Multisampled conversion? */
1534 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
1535 desc.format = dst_format->id;
1536 desc.multisample_type = WINED3D_MULTISAMPLE_NONE;
1537 desc.multisample_quality = 0;
1538 desc.usage = 0;
1539 desc.pool = WINED3D_POOL_SCRATCH;
1540 desc.width = wined3d_texture_get_level_width(src_texture, texture_level);
1541 desc.height = wined3d_texture_get_level_height(src_texture, texture_level);
1542 desc.depth = 1;
1543 desc.size = 0;
1544 if (FAILED(wined3d_texture_create(device, &desc, 1, 1,
1545 WINED3D_TEXTURE_CREATE_MAPPABLE | WINED3D_TEXTURE_CREATE_DISCARD,
1546 NULL, NULL, &wined3d_null_parent_ops, &dst_texture)))
1548 ERR("Failed to create a destination texture for conversion.\n");
1549 return NULL;
1552 memset(&src_map, 0, sizeof(src_map));
1553 if (FAILED(wined3d_resource_map(&src_texture->resource, sub_resource_idx,
1554 &src_map, NULL, WINED3D_MAP_READONLY)))
1556 ERR("Failed to map the source texture.\n");
1557 wined3d_texture_decref(dst_texture);
1558 return NULL;
1560 if (conv)
1562 struct wined3d_map_desc dst_map;
1564 memset(&dst_map, 0, sizeof(dst_map));
1565 if (FAILED(wined3d_resource_map(&dst_texture->resource, 0, &dst_map, NULL, 0)))
1567 ERR("Failed to map the destination texture.\n");
1568 wined3d_resource_unmap(&src_texture->resource, sub_resource_idx);
1569 wined3d_texture_decref(dst_texture);
1570 return NULL;
1573 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch, desc.width, desc.height);
1575 wined3d_resource_unmap(&dst_texture->resource, 0);
1577 else
1579 struct wined3d_bo_address data = {0, src_map.data};
1580 RECT src_rect = {0, 0, desc.width, desc.height};
1581 const struct wined3d_gl_info *gl_info;
1582 struct wined3d_context *context;
1583 POINT dst_point = {0, 0};
1585 TRACE("Using upload conversion.\n");
1586 context = context_acquire(device, NULL, 0);
1587 gl_info = context->gl_info;
1589 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
1590 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
1591 wined3d_surface_upload_data(dst_texture->sub_resources[0].u.surface, gl_info, src_format,
1592 &src_rect, src_map.row_pitch, &dst_point, FALSE, wined3d_const_bo_address(&data));
1594 context_release(context);
1596 wined3d_texture_validate_location(dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB);
1597 wined3d_texture_invalidate_location(dst_texture, 0, ~WINED3D_LOCATION_TEXTURE_RGB);
1599 wined3d_resource_unmap(&src_texture->resource, sub_resource_idx);
1601 return dst_texture;
1604 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
1605 unsigned int bpp, UINT pitch, DWORD color)
1607 BYTE *first;
1608 unsigned int x, y;
1610 /* Do first row */
1612 #define COLORFILL_ROW(type) \
1613 do { \
1614 type *d = (type *)buf; \
1615 for (x = 0; x < width; ++x) \
1616 d[x] = (type)color; \
1617 } while(0)
1619 switch (bpp)
1621 case 1:
1622 COLORFILL_ROW(BYTE);
1623 break;
1625 case 2:
1626 COLORFILL_ROW(WORD);
1627 break;
1629 case 3:
1631 BYTE *d = buf;
1632 for (x = 0; x < width; ++x, d += 3)
1634 d[0] = (color ) & 0xff;
1635 d[1] = (color >> 8) & 0xff;
1636 d[2] = (color >> 16) & 0xff;
1638 break;
1640 case 4:
1641 COLORFILL_ROW(DWORD);
1642 break;
1644 default:
1645 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
1646 return WINED3DERR_NOTAVAILABLE;
1649 #undef COLORFILL_ROW
1651 /* Now copy first row. */
1652 first = buf;
1653 for (y = 1; y < height; ++y)
1655 buf += pitch;
1656 memcpy(buf, first, width * bpp);
1659 return WINED3D_OK;
1662 static void read_from_framebuffer(struct wined3d_surface *surface,
1663 struct wined3d_context *old_ctx, DWORD dst_location)
1665 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
1666 struct wined3d_texture *texture = surface->container;
1667 struct wined3d_device *device = texture->resource.device;
1668 const struct wined3d_gl_info *gl_info;
1669 struct wined3d_context *context = old_ctx;
1670 struct wined3d_surface *restore_rt = NULL;
1671 unsigned int row_pitch, slice_pitch;
1672 unsigned int width, height;
1673 BYTE *mem;
1674 BYTE *row, *top, *bottom;
1675 int i;
1676 BOOL srcIsUpsideDown;
1677 struct wined3d_bo_address data;
1679 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
1681 restore_rt = context_get_rt_surface(old_ctx);
1682 if (restore_rt != surface)
1683 context = context_acquire(device, texture, sub_resource_idx);
1684 else
1685 restore_rt = NULL;
1687 context_apply_blit_state(context, device);
1688 gl_info = context->gl_info;
1690 /* Select the correct read buffer, and give some debug output.
1691 * There is no need to keep track of the current read buffer or reset it, every part of the code
1692 * that reads sets the read buffer as desired.
1694 if (wined3d_resource_is_offscreen(&texture->resource))
1696 /* Mapping the primary render target which is not on a swapchain.
1697 * Read from the back buffer. */
1698 TRACE("Mapping offscreen render target.\n");
1699 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1700 srcIsUpsideDown = TRUE;
1702 else
1704 /* Onscreen surfaces are always part of a swapchain */
1705 GLenum buffer = wined3d_texture_get_gl_buffer(texture);
1706 TRACE("Mapping %#x buffer.\n", buffer);
1707 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
1708 checkGLcall("glReadBuffer");
1709 srcIsUpsideDown = FALSE;
1712 if (data.buffer_object)
1714 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1715 checkGLcall("glBindBuffer");
1718 wined3d_texture_get_pitch(texture, surface->texture_level, &row_pitch, &slice_pitch);
1720 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1721 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / texture->resource.format->byte_count);
1722 checkGLcall("glPixelStorei");
1724 width = wined3d_texture_get_level_width(texture, surface->texture_level);
1725 height = wined3d_texture_get_level_height(texture, surface->texture_level);
1726 gl_info->gl_ops.gl.p_glReadPixels(0, 0, width, height,
1727 texture->resource.format->glFormat,
1728 texture->resource.format->glType, data.addr);
1729 checkGLcall("glReadPixels");
1731 /* Reset previous pixel store pack state */
1732 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
1733 checkGLcall("glPixelStorei");
1735 if (!srcIsUpsideDown)
1737 /* glReadPixels returns the image upside down, and there is no way to
1738 * prevent this. Flip the lines in software. */
1740 if (!(row = HeapAlloc(GetProcessHeap(), 0, row_pitch)))
1741 goto error;
1743 if (data.buffer_object)
1745 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
1746 checkGLcall("glMapBuffer");
1748 else
1749 mem = data.addr;
1751 top = mem;
1752 bottom = mem + row_pitch * (height - 1);
1753 for (i = 0; i < height / 2; i++)
1755 memcpy(row, top, row_pitch);
1756 memcpy(top, bottom, row_pitch);
1757 memcpy(bottom, row, row_pitch);
1758 top += row_pitch;
1759 bottom -= row_pitch;
1761 HeapFree(GetProcessHeap(), 0, row);
1763 if (data.buffer_object)
1764 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
1767 error:
1768 if (data.buffer_object)
1770 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1771 checkGLcall("glBindBuffer");
1774 if (restore_rt)
1775 context_restore(context, restore_rt);
1778 /* Read the framebuffer contents into a texture. Note that this function
1779 * doesn't do any kind of flipping. Using this on an onscreen surface will
1780 * result in a flipped D3D texture.
1782 * Context activation is done by the caller. This function may temporarily
1783 * switch to a different context and restore the original one before return. */
1784 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb, struct wined3d_context *old_ctx)
1786 struct wined3d_texture *texture = surface->container;
1787 struct wined3d_device *device = texture->resource.device;
1788 const struct wined3d_gl_info *gl_info;
1789 struct wined3d_context *context = old_ctx;
1790 struct wined3d_surface *restore_rt = NULL;
1792 restore_rt = context_get_rt_surface(old_ctx);
1793 if (restore_rt != surface)
1794 context = context_acquire(device, texture, surface_get_sub_resource_idx(surface));
1795 else
1796 restore_rt = NULL;
1798 gl_info = context->gl_info;
1799 device_invalidate_state(device, STATE_FRAMEBUFFER);
1801 wined3d_texture_prepare_texture(texture, context, srgb);
1802 wined3d_texture_bind_and_dirtify(texture, context, srgb);
1804 TRACE("Reading back offscreen render target %p.\n", surface);
1806 if (wined3d_resource_is_offscreen(&texture->resource))
1807 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1808 else
1809 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(texture));
1810 checkGLcall("glReadBuffer");
1812 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
1813 0, 0, 0, 0, wined3d_texture_get_level_width(texture, surface->texture_level),
1814 wined3d_texture_get_level_height(texture, surface->texture_level));
1815 checkGLcall("glCopyTexSubImage2D");
1817 if (restore_rt)
1818 context_restore(context, restore_rt);
1821 /* Does a direct frame buffer -> texture copy. Stretching is done with single
1822 * pixel copy calls. */
1823 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
1824 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
1826 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
1827 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1828 struct wined3d_texture *src_texture = src_surface->container;
1829 struct wined3d_texture *dst_texture = dst_surface->container;
1830 struct wined3d_device *device = dst_texture->resource.device;
1831 const struct wined3d_gl_info *gl_info;
1832 float xrel, yrel;
1833 struct wined3d_context *context;
1834 BOOL upsidedown = FALSE;
1835 RECT dst_rect = *dst_rect_in;
1836 unsigned int src_height;
1838 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1839 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1841 if(dst_rect.top > dst_rect.bottom) {
1842 UINT tmp = dst_rect.bottom;
1843 dst_rect.bottom = dst_rect.top;
1844 dst_rect.top = tmp;
1845 upsidedown = TRUE;
1848 context = context_acquire(device, src_texture, src_sub_resource_idx);
1849 gl_info = context->gl_info;
1850 context_apply_blit_state(context, device);
1851 wined3d_texture_load(dst_texture, context, FALSE);
1853 /* Bind the target texture */
1854 context_bind_texture(context, dst_texture->target, dst_texture->texture_rgb.name);
1855 if (wined3d_resource_is_offscreen(&src_texture->resource))
1857 TRACE("Reading from an offscreen target\n");
1858 upsidedown = !upsidedown;
1859 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1861 else
1863 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
1865 checkGLcall("glReadBuffer");
1867 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
1868 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
1870 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1872 FIXME_(d3d_perf)("Doing a pixel by pixel copy from the framebuffer to a texture.\n");
1874 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1875 ERR("Texture filtering not supported in direct blit.\n");
1877 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1878 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1880 ERR("Texture filtering not supported in direct blit\n");
1883 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
1884 if (upsidedown
1885 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1886 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1888 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
1889 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1890 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
1891 src_rect->left, src_height - src_rect->bottom,
1892 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1894 else
1896 LONG row;
1897 UINT yoffset = src_height - src_rect->top + dst_rect.top - 1;
1898 /* I have to process this row by row to swap the image,
1899 * otherwise it would be upside down, so stretching in y direction
1900 * doesn't cost extra time
1902 * However, stretching in x direction can be avoided if not necessary
1904 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
1905 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1907 /* Well, that stuff works, but it's very slow.
1908 * find a better way instead
1910 LONG col;
1912 for (col = dst_rect.left; col < dst_rect.right; ++col)
1914 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1915 dst_rect.left + col /* x offset */, row /* y offset */,
1916 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
1919 else
1921 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1922 dst_rect.left /* x offset */, row /* y offset */,
1923 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
1927 checkGLcall("glCopyTexSubImage2D");
1929 context_release(context);
1931 /* The texture is now most up to date - If the surface is a render target
1932 * and has a drawable, this path is never entered. */
1933 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1934 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1937 /* Uses the hardware to stretch and flip the image */
1938 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
1939 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
1941 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
1942 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1943 unsigned int src_width, src_height, src_pow2_width, src_pow2_height;
1944 struct wined3d_texture *src_texture = src_surface->container;
1945 struct wined3d_texture *dst_texture = dst_surface->container;
1946 struct wined3d_device *device = dst_texture->resource.device;
1947 GLuint src, backup = 0;
1948 float left, right, top, bottom; /* Texture coordinates */
1949 const struct wined3d_gl_info *gl_info;
1950 struct wined3d_context *context;
1951 GLenum drawBuffer = GL_BACK;
1952 GLenum offscreen_buffer;
1953 GLenum texture_target;
1954 BOOL noBackBufferBackup;
1955 BOOL src_offscreen;
1956 BOOL upsidedown = FALSE;
1957 RECT dst_rect = *dst_rect_in;
1959 TRACE("Using hwstretch blit\n");
1960 /* Activate the Proper context for reading from the source surface, set it up for blitting */
1961 context = context_acquire(device, src_texture, src_sub_resource_idx);
1962 gl_info = context->gl_info;
1963 context_apply_blit_state(context, device);
1964 wined3d_texture_load(dst_texture, context, FALSE);
1966 offscreen_buffer = context_get_offscreen_gl_buffer(context);
1967 src_width = wined3d_texture_get_level_width(src_texture, src_surface->texture_level);
1968 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
1969 src_pow2_width = wined3d_texture_get_level_pow2_width(src_texture, src_surface->texture_level);
1970 src_pow2_height = wined3d_texture_get_level_pow2_height(src_texture, src_surface->texture_level);
1972 src_offscreen = wined3d_resource_is_offscreen(&src_texture->resource);
1973 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
1974 if (!noBackBufferBackup && !src_texture->texture_rgb.name)
1976 /* Get it a description */
1977 wined3d_texture_load(src_texture, context, FALSE);
1980 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
1981 * This way we don't have to wait for the 2nd readback to finish to leave this function.
1983 if (context->aux_buffers >= 2)
1985 /* Got more than one aux buffer? Use the 2nd aux buffer */
1986 drawBuffer = GL_AUX1;
1988 else if ((!src_offscreen || offscreen_buffer == GL_BACK) && context->aux_buffers >= 1)
1990 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
1991 drawBuffer = GL_AUX0;
1994 if (noBackBufferBackup)
1996 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
1997 checkGLcall("glGenTextures");
1998 context_bind_texture(context, GL_TEXTURE_2D, backup);
1999 texture_target = GL_TEXTURE_2D;
2001 else
2003 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2004 * we are reading from the back buffer, the backup can be used as source texture
2006 texture_target = src_surface->texture_target;
2007 context_bind_texture(context, texture_target, src_texture->texture_rgb.name);
2008 gl_info->gl_ops.gl.p_glEnable(texture_target);
2009 checkGLcall("glEnable(texture_target)");
2011 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2012 surface_get_sub_resource(src_surface)->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
2015 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2016 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2018 if(dst_rect.top > dst_rect.bottom) {
2019 UINT tmp = dst_rect.bottom;
2020 dst_rect.bottom = dst_rect.top;
2021 dst_rect.top = tmp;
2022 upsidedown = TRUE;
2025 if (src_offscreen)
2027 TRACE("Reading from an offscreen target\n");
2028 upsidedown = !upsidedown;
2029 gl_info->gl_ops.gl.p_glReadBuffer(offscreen_buffer);
2031 else
2033 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
2036 /* TODO: Only back up the part that will be overwritten */
2037 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, src_width, src_height);
2039 checkGLcall("glCopyTexSubImage2D");
2041 /* No issue with overriding these - the sampler is dirty due to blit usage */
2042 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
2043 checkGLcall("glTexParameteri");
2044 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2045 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
2046 checkGLcall("glTexParameteri");
2048 if (!src_texture->swapchain || src_texture == src_texture->swapchain->back_buffers[0])
2050 src = backup ? backup : src_texture->texture_rgb.name;
2052 else
2054 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
2055 checkGLcall("glReadBuffer(GL_FRONT)");
2057 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
2058 checkGLcall("glGenTextures(1, &src)");
2059 context_bind_texture(context, GL_TEXTURE_2D, src);
2061 /* TODO: Only copy the part that will be read. Use src_rect->left,
2062 * src_rect->bottom as origin, but with the width watch out for power
2063 * of 2 sizes. */
2064 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_pow2_width,
2065 src_pow2_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2066 checkGLcall("glTexImage2D");
2067 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, src_width, src_height);
2069 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2070 checkGLcall("glTexParameteri");
2071 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2072 checkGLcall("glTexParameteri");
2074 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
2075 checkGLcall("glReadBuffer(GL_BACK)");
2077 if (texture_target != GL_TEXTURE_2D)
2079 gl_info->gl_ops.gl.p_glDisable(texture_target);
2080 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
2081 texture_target = GL_TEXTURE_2D;
2084 checkGLcall("glEnd and previous");
2086 left = src_rect->left;
2087 right = src_rect->right;
2089 if (!upsidedown)
2091 top = src_height - src_rect->top;
2092 bottom = src_height - src_rect->bottom;
2094 else
2096 top = src_height - src_rect->bottom;
2097 bottom = src_height - src_rect->top;
2100 if (src_texture->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
2102 left /= src_pow2_width;
2103 right /= src_pow2_width;
2104 top /= src_pow2_height;
2105 bottom /= src_pow2_height;
2108 /* draw the source texture stretched and upside down. The correct surface is bound already */
2109 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2110 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2112 context_set_draw_buffer(context, drawBuffer);
2113 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
2115 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
2116 /* bottom left */
2117 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
2118 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
2120 /* top left */
2121 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
2122 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
2124 /* top right */
2125 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
2126 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2128 /* bottom right */
2129 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
2130 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
2131 gl_info->gl_ops.gl.p_glEnd();
2132 checkGLcall("glEnd and previous");
2134 if (texture_target != dst_surface->texture_target)
2136 gl_info->gl_ops.gl.p_glDisable(texture_target);
2137 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
2138 texture_target = dst_surface->texture_target;
2141 /* Now read the stretched and upside down image into the destination texture */
2142 context_bind_texture(context, texture_target, dst_texture->texture_rgb.name);
2143 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
2145 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
2146 0, 0, /* We blitted the image to the origin */
2147 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2148 checkGLcall("glCopyTexSubImage2D");
2150 if (drawBuffer == GL_BACK)
2152 /* Write the back buffer backup back. */
2153 if (backup)
2155 if (texture_target != GL_TEXTURE_2D)
2157 gl_info->gl_ops.gl.p_glDisable(texture_target);
2158 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
2159 texture_target = GL_TEXTURE_2D;
2161 context_bind_texture(context, GL_TEXTURE_2D, backup);
2163 else
2165 if (texture_target != src_surface->texture_target)
2167 gl_info->gl_ops.gl.p_glDisable(texture_target);
2168 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
2169 texture_target = src_surface->texture_target;
2171 context_bind_texture(context, src_surface->texture_target, src_texture->texture_rgb.name);
2174 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
2175 /* top left */
2176 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
2177 gl_info->gl_ops.gl.p_glVertex2i(0, src_height);
2179 /* bottom left */
2180 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)src_height / (float)src_pow2_height);
2181 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
2183 /* bottom right */
2184 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width,
2185 (float)src_height / (float)src_pow2_height);
2186 gl_info->gl_ops.gl.p_glVertex2i(src_width, 0);
2188 /* top right */
2189 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width, 0.0f);
2190 gl_info->gl_ops.gl.p_glVertex2i(src_width, src_height);
2191 gl_info->gl_ops.gl.p_glEnd();
2193 gl_info->gl_ops.gl.p_glDisable(texture_target);
2194 checkGLcall("glDisable(texture_target)");
2196 /* Cleanup */
2197 if (src != src_texture->texture_rgb.name && src != backup)
2199 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
2200 checkGLcall("glDeleteTextures(1, &src)");
2202 if (backup)
2204 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
2205 checkGLcall("glDeleteTextures(1, &backup)");
2208 if (wined3d_settings.strict_draw_ordering)
2209 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2211 context_release(context);
2213 /* The texture is now most up to date - If the surface is a render target
2214 * and has a drawable, this path is never entered. */
2215 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
2216 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
2219 /* Front buffer coordinates are always full screen coordinates, but our GL
2220 * drawable is limited to the window's client area. The sysmem and texture
2221 * copies do have the full screen size. Note that GL has a bottom-left
2222 * origin, while D3D has a top-left origin. */
2223 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
2225 struct wined3d_texture *texture = surface->container;
2226 UINT drawable_height;
2228 if (texture->swapchain && texture == texture->swapchain->front_buffer)
2230 POINT offset = {0, 0};
2231 RECT windowsize;
2233 ScreenToClient(window, &offset);
2234 OffsetRect(rect, offset.x, offset.y);
2236 GetClientRect(window, &windowsize);
2237 drawable_height = windowsize.bottom - windowsize.top;
2239 else
2241 drawable_height = wined3d_texture_get_level_height(texture, surface->texture_level);
2244 rect->top = drawable_height - rect->top;
2245 rect->bottom = drawable_height - rect->bottom;
2248 /* Context activation is done by the caller. */
2249 static void surface_blt_to_drawable(const struct wined3d_device *device,
2250 struct wined3d_context *old_ctx,
2251 enum wined3d_texture_filter_type filter, BOOL alpha_test,
2252 struct wined3d_surface *src_surface, const RECT *src_rect_in,
2253 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
2255 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
2256 struct wined3d_texture *src_texture = src_surface->container;
2257 struct wined3d_texture *dst_texture = dst_surface->container;
2258 const struct wined3d_gl_info *gl_info;
2259 struct wined3d_context *context = old_ctx;
2260 struct wined3d_surface *restore_rt = NULL;
2261 RECT src_rect, dst_rect;
2263 src_rect = *src_rect_in;
2264 dst_rect = *dst_rect_in;
2266 restore_rt = context_get_rt_surface(old_ctx);
2267 if (restore_rt != dst_surface)
2268 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
2269 else
2270 restore_rt = NULL;
2272 gl_info = context->gl_info;
2274 /* Make sure the surface is up-to-date. This should probably use
2275 * surface_load_location() and worry about the destination surface too,
2276 * unless we're overwriting it completely. */
2277 wined3d_texture_load(src_texture, context, FALSE);
2279 /* Activate the destination context, set it up for blitting */
2280 context_apply_blit_state(context, device);
2282 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
2283 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
2285 device->blitter->set_shader(device->blit_priv, context, src_surface, NULL);
2287 if (alpha_test)
2289 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
2290 checkGLcall("glEnable(GL_ALPHA_TEST)");
2292 /* For P8 surfaces, the alpha component contains the palette index.
2293 * Which means that the colorkey is one of the palette entries. In
2294 * other cases pixels that should be masked away have alpha set to 0. */
2295 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT)
2296 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
2297 (float)src_texture->async.src_blt_color_key.color_space_low_value / 255.0f);
2298 else
2299 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
2300 checkGLcall("glAlphaFunc");
2302 else
2304 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2305 checkGLcall("glDisable(GL_ALPHA_TEST)");
2308 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
2310 if (alpha_test)
2312 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2313 checkGLcall("glDisable(GL_ALPHA_TEST)");
2316 /* Leave the opengl state valid for blitting */
2317 device->blitter->unset_shader(context->gl_info);
2319 if (wined3d_settings.strict_draw_ordering
2320 || (dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture))
2321 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2323 if (restore_rt)
2324 context_restore(context, restore_rt);
2327 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
2329 struct wined3d_resource *resource = &s->container->resource;
2330 struct wined3d_device *device = resource->device;
2331 struct wined3d_rendertarget_view *view;
2332 struct wined3d_view_desc view_desc;
2333 const struct blit_shader *blitter;
2334 HRESULT hr;
2336 if (!(blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info,
2337 WINED3D_BLIT_OP_COLOR_FILL, NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format)))
2339 FIXME("No blitter is capable of performing the requested color fill operation.\n");
2340 return WINED3DERR_INVALIDCALL;
2343 view_desc.format_id = resource->format->id;
2344 view_desc.flags = 0;
2345 view_desc.u.texture.level_idx = s->texture_level;
2346 view_desc.u.texture.level_count = 1;
2347 view_desc.u.texture.layer_idx = s->texture_layer;
2348 view_desc.u.texture.layer_count = 1;
2349 if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc,
2350 resource, NULL, &wined3d_null_parent_ops, &view)))
2352 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
2353 return hr;
2356 hr = blitter->color_fill(device, view, rect, color);
2357 wined3d_rendertarget_view_decref(view);
2359 return hr;
2362 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
2363 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
2364 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
2366 struct wined3d_texture *dst_texture = dst_surface->container;
2367 struct wined3d_device *device = dst_texture->resource.device;
2368 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
2369 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
2370 struct wined3d_texture *src_texture;
2372 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
2373 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
2374 flags, fx, debug_d3dtexturefiltertype(filter));
2376 /* Get the swapchain. One of the surfaces has to be a primary surface */
2377 if (dst_texture->resource.pool == WINED3D_POOL_SYSTEM_MEM)
2379 WARN("Destination is in sysmem, rejecting gl blt\n");
2380 return WINED3DERR_INVALIDCALL;
2383 dst_swapchain = dst_texture->swapchain;
2385 if (src_surface)
2387 src_texture = src_surface->container;
2388 if (src_texture->resource.pool == WINED3D_POOL_SYSTEM_MEM)
2390 WARN("Src is in sysmem, rejecting gl blt\n");
2391 return WINED3DERR_INVALIDCALL;
2394 src_swapchain = src_texture->swapchain;
2396 else
2398 src_texture = NULL;
2399 src_swapchain = NULL;
2402 /* Early sort out of cases where no render target is used */
2403 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
2405 TRACE("No surface is render target, not using hardware blit.\n");
2406 return WINED3DERR_INVALIDCALL;
2409 /* No destination color keying supported */
2410 if (flags & (WINED3D_BLT_DST_CKEY | WINED3D_BLT_DST_CKEY_OVERRIDE))
2412 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2413 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2414 return WINED3DERR_INVALIDCALL;
2417 if (dst_swapchain && dst_swapchain == src_swapchain)
2419 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
2420 return WINED3DERR_INVALIDCALL;
2423 if (dst_swapchain && src_swapchain)
2425 FIXME("Implement hardware blit between two different swapchains\n");
2426 return WINED3DERR_INVALIDCALL;
2429 if (dst_swapchain)
2431 /* Handled with regular texture -> swapchain blit */
2432 if (src_surface == rt)
2433 TRACE("Blit from active render target to a swapchain\n");
2435 else if (src_swapchain && dst_surface == rt)
2437 FIXME("Implement blit from a swapchain to the active render target\n");
2438 return WINED3DERR_INVALIDCALL;
2441 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
2443 unsigned int src_width, src_height;
2444 /* Blit from render target to texture */
2445 BOOL stretchx;
2447 /* P8 read back is not implemented */
2448 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT
2449 || dst_texture->resource.format->id == WINED3DFMT_P8_UINT)
2451 TRACE("P8 read back not supported by frame buffer to texture blit\n");
2452 return WINED3DERR_INVALIDCALL;
2455 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_SRC_CKEY_OVERRIDE))
2457 TRACE("Color keying not supported by frame buffer to texture blit\n");
2458 return WINED3DERR_INVALIDCALL;
2459 /* Destination color key is checked above */
2462 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
2463 stretchx = TRUE;
2464 else
2465 stretchx = FALSE;
2467 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2468 * flip the image nor scale it.
2470 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2471 * -> If the app wants an image width an unscaled width, copy it line per line
2472 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
2473 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2474 * back buffer. This is slower than reading line per line, thus not used for flipping
2475 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2476 * pixel by pixel. */
2477 src_width = wined3d_texture_get_level_width(src_texture, src_surface->texture_level);
2478 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
2479 if (!stretchx || dst_rect->right - dst_rect->left > src_width
2480 || dst_rect->bottom - dst_rect->top > src_height)
2482 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
2483 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
2485 else
2487 TRACE("Using hardware stretching to flip / stretch the texture.\n");
2488 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
2491 return WINED3D_OK;
2494 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
2495 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
2496 return WINED3DERR_INVALIDCALL;
2499 /* Context activation is done by the caller. */
2500 static void surface_load_sysmem(struct wined3d_surface *surface,
2501 struct wined3d_context *context, DWORD dst_location)
2503 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2504 const struct wined3d_gl_info *gl_info = context->gl_info;
2505 struct wined3d_texture *texture = surface->container;
2506 struct wined3d_texture_sub_resource *sub_resource;
2508 wined3d_texture_prepare_location(texture, sub_resource_idx, context, dst_location);
2510 sub_resource = &texture->sub_resources[sub_resource_idx];
2511 if (sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
2512 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
2514 /* Download the surface to system memory. */
2515 if (sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
2517 wined3d_texture_bind_and_dirtify(texture, context,
2518 !(sub_resource->locations & WINED3D_LOCATION_TEXTURE_RGB));
2519 surface_download_data(surface, gl_info, dst_location);
2520 ++texture->download_count;
2522 return;
2525 if (sub_resource->locations & WINED3D_LOCATION_DRAWABLE)
2527 read_from_framebuffer(surface, context, dst_location);
2528 return;
2531 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
2532 surface, wined3d_debug_location(sub_resource->locations));
2535 /* Context activation is done by the caller. */
2536 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
2537 struct wined3d_context *context)
2539 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2540 struct wined3d_texture *texture = surface->container;
2541 RECT r;
2543 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
2544 && wined3d_resource_is_offscreen(&texture->resource))
2546 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
2547 return WINED3DERR_INVALIDCALL;
2550 surface_get_rect(surface, NULL, &r);
2551 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
2552 surface_blt_to_drawable(texture->resource.device, context,
2553 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
2555 return WINED3D_OK;
2558 static HRESULT surface_load_texture(struct wined3d_surface *surface,
2559 struct wined3d_context *context, BOOL srgb)
2561 unsigned int width, height, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch;
2562 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2563 const struct wined3d_gl_info *gl_info = context->gl_info;
2564 struct wined3d_texture *texture = surface->container;
2565 struct wined3d_device *device = texture->resource.device;
2566 const struct wined3d_color_key_conversion *conversion;
2567 struct wined3d_texture_sub_resource *sub_resource;
2568 struct wined3d_bo_address data;
2569 BYTE *src_mem, *dst_mem = NULL;
2570 struct wined3d_format format;
2571 POINT dst_point = {0, 0};
2572 RECT src_rect;
2574 sub_resource = surface_get_sub_resource(surface);
2575 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
2576 && wined3d_resource_is_offscreen(&texture->resource)
2577 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
2579 surface_load_fb_texture(surface, srgb, context);
2581 return WINED3D_OK;
2584 width = wined3d_texture_get_level_width(texture, surface->texture_level);
2585 height = wined3d_texture_get_level_height(texture, surface->texture_level);
2586 SetRect(&src_rect, 0, 0, width, height);
2588 if (sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
2589 && (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
2590 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
2591 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format,
2592 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format))
2594 if (srgb)
2595 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
2596 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
2597 else
2598 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
2599 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
2601 return WINED3D_OK;
2604 if (sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
2605 && (!srgb || (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
2606 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
2607 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format,
2608 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format))
2610 DWORD src_location = sub_resource->locations & WINED3D_LOCATION_RB_RESOLVED ?
2611 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
2612 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2614 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, src_location,
2615 &src_rect, surface, dst_location, &src_rect);
2617 return WINED3D_OK;
2620 /* Upload from system memory */
2622 if (srgb)
2624 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | texture->resource.map_binding))
2625 == WINED3D_LOCATION_TEXTURE_RGB)
2627 FIXME_(d3d_perf)("Downloading RGB surface %p to reload it as sRGB.\n", surface);
2628 wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding);
2631 else
2633 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | texture->resource.map_binding))
2634 == WINED3D_LOCATION_TEXTURE_SRGB)
2636 FIXME_(d3d_perf)("Downloading sRGB surface %p to reload it as RGB.\n", surface);
2637 wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding);
2641 if (!(sub_resource->locations & surface_simple_locations))
2643 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
2644 /* Lets hope we get it from somewhere... */
2645 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM);
2648 wined3d_texture_prepare_texture(texture, context, srgb);
2649 wined3d_texture_bind_and_dirtify(texture, context, srgb);
2650 wined3d_texture_get_pitch(texture, surface->texture_level, &src_row_pitch, &src_slice_pitch);
2652 format = *texture->resource.format;
2653 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
2654 format = *wined3d_get_format(gl_info, conversion->dst_format, texture->resource.usage);
2656 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
2657 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
2658 * getting called. */
2659 if ((format.convert || conversion) && texture->sub_resources[sub_resource_idx].buffer_object)
2661 TRACE("Removing the pbo attached to surface %p.\n", surface);
2663 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM);
2664 wined3d_texture_set_map_binding(texture, WINED3D_LOCATION_SYSMEM);
2667 wined3d_texture_get_memory(texture, sub_resource_idx, &data, sub_resource->locations);
2668 if (format.convert)
2670 /* This code is entered for texture formats which need a fixup. */
2671 format.byte_count = format.conv_byte_count;
2672 wined3d_format_calculate_pitch(&format, 1, width, height, &dst_row_pitch, &dst_slice_pitch);
2674 src_mem = wined3d_texture_map_bo_address(&data, src_slice_pitch,
2675 gl_info, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READONLY);
2676 if (!(dst_mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
2678 ERR("Out of memory (%u).\n", dst_slice_pitch);
2679 context_release(context);
2680 return E_OUTOFMEMORY;
2682 format.convert(src_mem, dst_mem, src_row_pitch, src_slice_pitch,
2683 dst_row_pitch, dst_slice_pitch, width, height, 1);
2684 src_row_pitch = dst_row_pitch;
2685 wined3d_texture_unmap_bo_address(&data, gl_info, GL_PIXEL_UNPACK_BUFFER);
2687 data.buffer_object = 0;
2688 data.addr = dst_mem;
2690 else if (conversion)
2692 /* This code is only entered for color keying fixups */
2693 struct wined3d_palette *palette = NULL;
2695 wined3d_format_calculate_pitch(&format, device->surface_alignment,
2696 width, height, &dst_row_pitch, &dst_slice_pitch);
2698 src_mem = wined3d_texture_map_bo_address(&data, src_slice_pitch,
2699 gl_info, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READONLY);
2700 if (!(dst_mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
2702 ERR("Out of memory (%u).\n", dst_slice_pitch);
2703 context_release(context);
2704 return E_OUTOFMEMORY;
2706 if (texture->swapchain && texture->swapchain->palette)
2707 palette = texture->swapchain->palette;
2708 conversion->convert(src_mem, src_row_pitch, dst_mem, dst_row_pitch,
2709 width, height, palette, &texture->async.gl_color_key);
2710 src_row_pitch = dst_row_pitch;
2711 wined3d_texture_unmap_bo_address(&data, gl_info, GL_PIXEL_UNPACK_BUFFER);
2713 data.buffer_object = 0;
2714 data.addr = dst_mem;
2717 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
2718 src_row_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
2720 HeapFree(GetProcessHeap(), 0, dst_mem);
2722 return WINED3D_OK;
2725 /* Context activation is done by the caller. */
2726 static void surface_load_renderbuffer(struct wined3d_surface *surface, struct wined3d_context *context,
2727 DWORD dst_location)
2729 struct wined3d_texture *texture = surface->container;
2730 const RECT rect = {0, 0,
2731 wined3d_texture_get_level_width(texture, surface->texture_level),
2732 wined3d_texture_get_level_height(texture, surface->texture_level)};
2733 DWORD locations = surface_get_sub_resource(surface)->locations;
2734 DWORD src_location;
2736 if (locations & WINED3D_LOCATION_RB_MULTISAMPLE)
2737 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
2738 else if (locations & WINED3D_LOCATION_RB_RESOLVED)
2739 src_location = WINED3D_LOCATION_RB_RESOLVED;
2740 else if (locations & WINED3D_LOCATION_TEXTURE_SRGB)
2741 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
2742 else /* surface_blt_fbo will load the source location if necessary. */
2743 src_location = WINED3D_LOCATION_TEXTURE_RGB;
2745 surface_blt_fbo(texture->resource.device, context, WINED3D_TEXF_POINT,
2746 surface, src_location, &rect, surface, dst_location, &rect);
2749 /* Context activation is done by the caller. Context may be NULL in ddraw-only mode. */
2750 BOOL surface_load_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
2752 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2753 struct wined3d_texture *texture = surface->container;
2755 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
2757 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2759 DWORD current = texture->sub_resources[sub_resource_idx].locations;
2760 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
2761 wined3d_debug_location(current), wined3d_debug_location(location));
2762 return FALSE;
2765 switch (location)
2767 case WINED3D_LOCATION_USER_MEMORY:
2768 case WINED3D_LOCATION_SYSMEM:
2769 case WINED3D_LOCATION_BUFFER:
2770 surface_load_sysmem(surface, context, location);
2771 return TRUE;
2773 case WINED3D_LOCATION_DRAWABLE:
2774 return SUCCEEDED(surface_load_drawable(surface, context));
2776 case WINED3D_LOCATION_RB_RESOLVED:
2777 case WINED3D_LOCATION_RB_MULTISAMPLE:
2778 surface_load_renderbuffer(surface, context, location);
2779 return TRUE;
2781 case WINED3D_LOCATION_TEXTURE_RGB:
2782 case WINED3D_LOCATION_TEXTURE_SRGB:
2783 return SUCCEEDED(surface_load_texture(surface, context,
2784 location == WINED3D_LOCATION_TEXTURE_SRGB));
2786 default:
2787 ERR("Don't know how to handle location %#x.\n", location);
2788 return FALSE;
2792 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
2793 /* Context activation is done by the caller. */
2794 static void ffp_blit_free(struct wined3d_device *device) { }
2796 /* Context activation is done by the caller. */
2797 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
2798 const struct wined3d_color_key *color_key)
2800 const struct wined3d_gl_info *gl_info = context->gl_info;
2802 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
2803 checkGLcall("glEnable(target)");
2805 return WINED3D_OK;
2808 /* Context activation is done by the caller. */
2809 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
2811 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
2812 checkGLcall("glDisable(GL_TEXTURE_2D)");
2813 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
2815 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
2816 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
2818 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
2820 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
2821 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
2825 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info,
2826 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
2827 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
2828 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
2830 BOOL decompress;
2832 decompress = src_format && (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
2833 && !(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED);
2834 if (!decompress && (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM))
2836 TRACE("Source or destination is in system memory.\n");
2837 return FALSE;
2840 switch (blit_op)
2842 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
2843 if (d3d_info->shader_color_key)
2845 TRACE("Color keying requires converted textures.\n");
2846 return FALSE;
2848 case WINED3D_BLIT_OP_COLOR_BLIT:
2849 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
2850 if (TRACE_ON(d3d))
2852 TRACE("Checking support for fixup:\n");
2853 dump_color_fixup_desc(src_format->color_fixup);
2856 /* We only support identity conversions. */
2857 if (!is_identity_fixup(src_format->color_fixup)
2858 || !is_identity_fixup(dst_format->color_fixup))
2860 TRACE("Fixups are not supported.\n");
2861 return FALSE;
2864 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
2866 TRACE("Can only blit to render targets.\n");
2867 return FALSE;
2869 return TRUE;
2871 case WINED3D_BLIT_OP_COLOR_FILL:
2872 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
2874 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
2875 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
2876 return FALSE;
2878 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
2880 TRACE("Color fill not supported\n");
2881 return FALSE;
2884 /* FIXME: We should reject color fills on formats with fixups,
2885 * but this would break P8 color fills for example. */
2887 return TRUE;
2889 case WINED3D_BLIT_OP_DEPTH_FILL:
2890 return TRUE;
2892 default:
2893 TRACE("Unsupported blit_op=%d\n", blit_op);
2894 return FALSE;
2898 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
2899 const RECT *rect, const struct wined3d_color *color)
2901 const RECT draw_rect = {0, 0, view->width, view->height};
2902 struct wined3d_fb_state fb = {&view, NULL};
2904 device_clear_render_targets(device, 1, &fb, 1, rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
2906 return WINED3D_OK;
2909 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
2910 struct wined3d_rendertarget_view *view, const RECT *rect, DWORD clear_flags,
2911 float depth, DWORD stencil)
2913 const RECT draw_rect = {0, 0, view->width, view->height};
2914 struct wined3d_fb_state fb = {NULL, view};
2916 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, clear_flags, NULL, depth, stencil);
2918 return WINED3D_OK;
2921 static void ffp_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
2922 struct wined3d_surface *src_surface, const RECT *src_rect,
2923 struct wined3d_surface *dst_surface, const RECT *dst_rect,
2924 const struct wined3d_color_key *color_key)
2926 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
2927 struct wined3d_texture *dst_texture = dst_surface->container;
2928 struct wined3d_texture *src_texture = src_surface->container;
2929 const struct wined3d_gl_info *gl_info;
2930 struct wined3d_context *context;
2932 /* Blit from offscreen surface to render target */
2933 struct wined3d_color_key old_blt_key = src_texture->async.src_blt_color_key;
2934 DWORD old_color_key_flags = src_texture->async.color_key_flags;
2936 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
2938 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT, color_key);
2940 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
2941 gl_info = context->gl_info;
2943 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
2944 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
2946 surface_blt_to_drawable(device, context, filter,
2947 !!color_key, src_surface, src_rect, dst_surface, dst_rect);
2949 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
2950 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2952 context_release(context);
2954 /* Restore the color key parameters */
2955 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT,
2956 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
2958 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, dst_texture->resource.draw_binding);
2959 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~dst_texture->resource.draw_binding);
2962 const struct blit_shader ffp_blit = {
2963 ffp_blit_alloc,
2964 ffp_blit_free,
2965 ffp_blit_set,
2966 ffp_blit_unset,
2967 ffp_blit_supported,
2968 ffp_blit_color_fill,
2969 ffp_blit_depth_fill,
2970 ffp_blit_blit_surface,
2973 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
2975 return WINED3D_OK;
2978 /* Context activation is done by the caller. */
2979 static void cpu_blit_free(struct wined3d_device *device)
2983 /* Context activation is done by the caller. */
2984 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
2985 const struct wined3d_color_key *color_key)
2987 return WINED3D_OK;
2990 /* Context activation is done by the caller. */
2991 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
2995 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info,
2996 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
2997 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
2998 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
3000 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
3002 return TRUE;
3005 return FALSE;
3008 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
3009 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
3010 const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx)
3012 UINT row_block_count;
3013 const BYTE *src_row;
3014 BYTE *dst_row;
3015 UINT x, y;
3017 src_row = src_data;
3018 dst_row = dst_data;
3020 row_block_count = (update_w + format->block_width - 1) / format->block_width;
3022 if (!flags)
3024 for (y = 0; y < update_h; y += format->block_height)
3026 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
3027 src_row += src_pitch;
3028 dst_row += dst_pitch;
3031 return WINED3D_OK;
3034 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
3036 src_row += (((update_h / format->block_height) - 1) * src_pitch);
3038 switch (format->id)
3040 case WINED3DFMT_DXT1:
3041 for (y = 0; y < update_h; y += format->block_height)
3043 struct block
3045 WORD color[2];
3046 BYTE control_row[4];
3049 const struct block *s = (const struct block *)src_row;
3050 struct block *d = (struct block *)dst_row;
3052 for (x = 0; x < row_block_count; ++x)
3054 d[x].color[0] = s[x].color[0];
3055 d[x].color[1] = s[x].color[1];
3056 d[x].control_row[0] = s[x].control_row[3];
3057 d[x].control_row[1] = s[x].control_row[2];
3058 d[x].control_row[2] = s[x].control_row[1];
3059 d[x].control_row[3] = s[x].control_row[0];
3061 src_row -= src_pitch;
3062 dst_row += dst_pitch;
3064 return WINED3D_OK;
3066 case WINED3DFMT_DXT2:
3067 case WINED3DFMT_DXT3:
3068 for (y = 0; y < update_h; y += format->block_height)
3070 struct block
3072 WORD alpha_row[4];
3073 WORD color[2];
3074 BYTE control_row[4];
3077 const struct block *s = (const struct block *)src_row;
3078 struct block *d = (struct block *)dst_row;
3080 for (x = 0; x < row_block_count; ++x)
3082 d[x].alpha_row[0] = s[x].alpha_row[3];
3083 d[x].alpha_row[1] = s[x].alpha_row[2];
3084 d[x].alpha_row[2] = s[x].alpha_row[1];
3085 d[x].alpha_row[3] = s[x].alpha_row[0];
3086 d[x].color[0] = s[x].color[0];
3087 d[x].color[1] = s[x].color[1];
3088 d[x].control_row[0] = s[x].control_row[3];
3089 d[x].control_row[1] = s[x].control_row[2];
3090 d[x].control_row[2] = s[x].control_row[1];
3091 d[x].control_row[3] = s[x].control_row[0];
3093 src_row -= src_pitch;
3094 dst_row += dst_pitch;
3096 return WINED3D_OK;
3098 default:
3099 FIXME("Compressed flip not implemented for format %s.\n",
3100 debug_d3dformat(format->id));
3101 return E_NOTIMPL;
3105 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
3106 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
3108 return E_NOTIMPL;
3111 static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
3112 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
3113 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
3114 enum wined3d_texture_filter_type filter)
3116 unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count;
3117 const struct wined3d_format *src_format, *dst_format;
3118 struct wined3d_texture *converted_texture = NULL;
3119 unsigned int src_fmt_flags, dst_fmt_flags;
3120 struct wined3d_map_desc dst_map, src_map;
3121 const BYTE *sbase = NULL;
3122 HRESULT hr = WINED3D_OK;
3123 BOOL same_sub_resource;
3124 const BYTE *sbuf;
3125 BYTE *dbuf;
3126 int x, y;
3128 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
3129 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
3130 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture,
3131 src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
3133 if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx)
3135 same_sub_resource = TRUE;
3136 wined3d_resource_map(&dst_texture->resource, dst_sub_resource_idx, &dst_map, NULL, 0);
3137 src_map = dst_map;
3138 src_format = dst_texture->resource.format;
3139 dst_format = src_format;
3140 dst_fmt_flags = dst_texture->resource.format_flags;
3141 src_fmt_flags = dst_fmt_flags;
3143 else
3145 same_sub_resource = FALSE;
3146 dst_format = dst_texture->resource.format;
3147 dst_fmt_flags = dst_texture->resource.format_flags;
3148 if (src_texture)
3150 if (dst_texture->resource.format->id != src_texture->resource.format->id)
3152 if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format)))
3154 FIXME("Cannot convert %s to %s.\n", debug_d3dformat(src_texture->resource.format->id),
3155 debug_d3dformat(dst_texture->resource.format->id));
3156 return WINED3DERR_NOTAVAILABLE;
3158 src_texture = converted_texture;
3159 src_sub_resource_idx = 0;
3161 wined3d_resource_map(&src_texture->resource, src_sub_resource_idx, &src_map, NULL, WINED3D_MAP_READONLY);
3162 src_format = src_texture->resource.format;
3163 src_fmt_flags = src_texture->resource.format_flags;
3165 else
3167 src_format = dst_format;
3168 src_fmt_flags = dst_fmt_flags;
3171 wined3d_resource_map(&dst_texture->resource, dst_sub_resource_idx, &dst_map, dst_box, 0);
3174 bpp = dst_format->byte_count;
3175 src_height = src_box->bottom - src_box->top;
3176 src_width = src_box->right - src_box->left;
3177 dst_height = dst_box->bottom - dst_box->top;
3178 dst_width = dst_box->right - dst_box->left;
3179 row_byte_count = dst_width * bpp;
3181 if (src_texture)
3182 sbase = (BYTE *)src_map.data
3183 + ((src_box->top / src_format->block_height) * src_map.row_pitch)
3184 + ((src_box->left / src_format->block_width) * src_format->block_byte_count);
3185 if (same_sub_resource)
3186 dbuf = (BYTE *)dst_map.data
3187 + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch)
3188 + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count);
3189 else
3190 dbuf = dst_map.data;
3192 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
3194 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
3196 if (same_sub_resource)
3198 FIXME("Only plain blits supported on compressed surfaces.\n");
3199 hr = E_NOTIMPL;
3200 goto release;
3203 if (src_height != dst_height || src_width != dst_width)
3205 WARN("Stretching not supported on compressed surfaces.\n");
3206 hr = WINED3DERR_INVALIDCALL;
3207 goto release;
3210 if (!wined3d_texture_check_block_align(src_texture,
3211 src_sub_resource_idx % src_texture->level_count, src_box))
3213 WARN("Source rectangle not block-aligned.\n");
3214 hr = WINED3DERR_INVALIDCALL;
3215 goto release;
3218 if (!wined3d_texture_check_block_align(dst_texture,
3219 dst_sub_resource_idx % dst_texture->level_count, dst_box))
3221 WARN("Destination rectangle not block-aligned.\n");
3222 hr = WINED3DERR_INVALIDCALL;
3223 goto release;
3226 hr = surface_cpu_blt_compressed(sbase, dbuf,
3227 src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height,
3228 src_format, flags, fx);
3229 goto release;
3232 /* First, all the 'source-less' blits */
3233 if (flags & WINED3D_BLT_COLOR_FILL)
3235 hr = _Blt_ColorFill(dbuf, dst_width, dst_height, bpp, dst_map.row_pitch, fx->fill_color);
3236 flags &= ~WINED3D_BLT_COLOR_FILL;
3239 if (flags & WINED3D_BLT_DEPTH_FILL)
3240 FIXME("WINED3D_BLT_DEPTH_FILL needs to be implemented!\n");
3242 /* Now the 'with source' blits. */
3243 if (src_texture)
3245 int sx, xinc, sy, yinc;
3247 if (!dst_width || !dst_height) /* Hmm... stupid program? */
3248 goto release;
3250 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
3251 && (src_width != dst_width || src_height != dst_height))
3253 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
3254 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
3257 xinc = (src_width << 16) / dst_width;
3258 yinc = (src_height << 16) / dst_height;
3260 if (!flags)
3262 /* No effects, we can cheat here. */
3263 if (dst_width == src_width)
3265 if (dst_height == src_height)
3267 /* No stretching in either direction. This needs to be as
3268 * fast as possible. */
3269 sbuf = sbase;
3271 /* Check for overlapping surfaces. */
3272 if (!same_sub_resource || dst_box->top < src_box->top
3273 || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
3275 /* No overlap, or dst above src, so copy from top downwards. */
3276 for (y = 0; y < dst_height; ++y)
3278 memcpy(dbuf, sbuf, row_byte_count);
3279 sbuf += src_map.row_pitch;
3280 dbuf += dst_map.row_pitch;
3283 else if (dst_box->top > src_box->top)
3285 /* Copy from bottom upwards. */
3286 sbuf += src_map.row_pitch * dst_height;
3287 dbuf += dst_map.row_pitch * dst_height;
3288 for (y = 0; y < dst_height; ++y)
3290 sbuf -= src_map.row_pitch;
3291 dbuf -= dst_map.row_pitch;
3292 memcpy(dbuf, sbuf, row_byte_count);
3295 else
3297 /* Src and dst overlapping on the same line, use memmove. */
3298 for (y = 0; y < dst_height; ++y)
3300 memmove(dbuf, sbuf, row_byte_count);
3301 sbuf += src_map.row_pitch;
3302 dbuf += dst_map.row_pitch;
3306 else
3308 /* Stretching in y direction only. */
3309 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3311 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3312 memcpy(dbuf, sbuf, row_byte_count);
3313 dbuf += dst_map.row_pitch;
3317 else
3319 /* Stretching in X direction. */
3320 int last_sy = -1;
3321 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3323 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3325 if ((sy >> 16) == (last_sy >> 16))
3327 /* This source row is the same as last source row -
3328 * Copy the already stretched row. */
3329 memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count);
3331 else
3333 #define STRETCH_ROW(type) \
3334 do { \
3335 const type *s = (const type *)sbuf; \
3336 type *d = (type *)dbuf; \
3337 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3338 d[x] = s[sx >> 16]; \
3339 } while(0)
3341 switch(bpp)
3343 case 1:
3344 STRETCH_ROW(BYTE);
3345 break;
3346 case 2:
3347 STRETCH_ROW(WORD);
3348 break;
3349 case 4:
3350 STRETCH_ROW(DWORD);
3351 break;
3352 case 3:
3354 const BYTE *s;
3355 BYTE *d = dbuf;
3356 for (x = sx = 0; x < dst_width; x++, sx+= xinc)
3358 DWORD pixel;
3360 s = sbuf + 3 * (sx >> 16);
3361 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3362 d[0] = (pixel ) & 0xff;
3363 d[1] = (pixel >> 8) & 0xff;
3364 d[2] = (pixel >> 16) & 0xff;
3365 d += 3;
3367 break;
3369 default:
3370 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
3371 hr = WINED3DERR_NOTAVAILABLE;
3372 goto error;
3374 #undef STRETCH_ROW
3376 dbuf += dst_map.row_pitch;
3377 last_sy = sy;
3381 else
3383 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
3384 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
3385 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
3386 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3387 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
3389 /* The color keying flags are checked for correctness in ddraw */
3390 if (flags & WINED3D_BLT_SRC_CKEY)
3392 keylow = src_texture->async.src_blt_color_key.color_space_low_value;
3393 keyhigh = src_texture->async.src_blt_color_key.color_space_high_value;
3395 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3397 keylow = fx->src_color_key.color_space_low_value;
3398 keyhigh = fx->src_color_key.color_space_high_value;
3401 if (flags & WINED3D_BLT_DST_CKEY)
3403 /* Destination color keys are taken from the source surface! */
3404 destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value;
3405 destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value;
3407 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
3409 destkeylow = fx->dst_color_key.color_space_low_value;
3410 destkeyhigh = fx->dst_color_key.color_space_high_value;
3413 if (bpp == 1)
3415 keymask = 0xff;
3417 else
3419 DWORD masks[3];
3420 get_color_masks(src_format, masks);
3421 keymask = masks[0]
3422 | masks[1]
3423 | masks[2];
3425 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3426 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
3429 if (flags & WINED3D_BLT_FX)
3431 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
3432 LONG tmpxy;
3433 dTopLeft = dbuf;
3434 dTopRight = dbuf + ((dst_width - 1) * bpp);
3435 dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
3436 dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
3438 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
3440 /* I don't think we need to do anything about this flag */
3441 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
3443 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
3445 tmp = dTopRight;
3446 dTopRight = dTopLeft;
3447 dTopLeft = tmp;
3448 tmp = dBottomRight;
3449 dBottomRight = dBottomLeft;
3450 dBottomLeft = tmp;
3451 dstxinc = dstxinc * -1;
3453 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
3455 tmp = dTopLeft;
3456 dTopLeft = dBottomLeft;
3457 dBottomLeft = tmp;
3458 tmp = dTopRight;
3459 dTopRight = dBottomRight;
3460 dBottomRight = tmp;
3461 dstyinc = dstyinc * -1;
3463 if (fx->fx & WINEDDBLTFX_NOTEARING)
3465 /* I don't think we need to do anything about this flag */
3466 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
3468 if (fx->fx & WINEDDBLTFX_ROTATE180)
3470 tmp = dBottomRight;
3471 dBottomRight = dTopLeft;
3472 dTopLeft = tmp;
3473 tmp = dBottomLeft;
3474 dBottomLeft = dTopRight;
3475 dTopRight = tmp;
3476 dstxinc = dstxinc * -1;
3477 dstyinc = dstyinc * -1;
3479 if (fx->fx & WINEDDBLTFX_ROTATE270)
3481 tmp = dTopLeft;
3482 dTopLeft = dBottomLeft;
3483 dBottomLeft = dBottomRight;
3484 dBottomRight = dTopRight;
3485 dTopRight = tmp;
3486 tmpxy = dstxinc;
3487 dstxinc = dstyinc;
3488 dstyinc = tmpxy;
3489 dstxinc = dstxinc * -1;
3491 if (fx->fx & WINEDDBLTFX_ROTATE90)
3493 tmp = dTopLeft;
3494 dTopLeft = dTopRight;
3495 dTopRight = dBottomRight;
3496 dBottomRight = dBottomLeft;
3497 dBottomLeft = tmp;
3498 tmpxy = dstxinc;
3499 dstxinc = dstyinc;
3500 dstyinc = tmpxy;
3501 dstyinc = dstyinc * -1;
3503 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
3505 /* I don't think we need to do anything about this flag */
3506 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
3508 dbuf = dTopLeft;
3509 flags &= ~(WINED3D_BLT_FX);
3512 #define COPY_COLORKEY_FX(type) \
3513 do { \
3514 const type *s; \
3515 type *d = (type *)dbuf, *dx, tmp; \
3516 for (y = sy = 0; y < dst_height; ++y, sy += yinc) \
3518 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
3519 dx = d; \
3520 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3522 tmp = s[sx >> 16]; \
3523 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
3524 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
3526 dx[0] = tmp; \
3528 dx = (type *)(((BYTE *)dx) + dstxinc); \
3530 d = (type *)(((BYTE *)d) + dstyinc); \
3532 } while(0)
3534 switch (bpp)
3536 case 1:
3537 COPY_COLORKEY_FX(BYTE);
3538 break;
3539 case 2:
3540 COPY_COLORKEY_FX(WORD);
3541 break;
3542 case 4:
3543 COPY_COLORKEY_FX(DWORD);
3544 break;
3545 case 3:
3547 const BYTE *s;
3548 BYTE *d = dbuf, *dx;
3549 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3551 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3552 dx = d;
3553 for (x = sx = 0; x < dst_width; ++x, sx+= xinc)
3555 DWORD pixel, dpixel = 0;
3556 s = sbuf + 3 * (sx>>16);
3557 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3558 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
3559 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
3560 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
3562 dx[0] = (pixel ) & 0xff;
3563 dx[1] = (pixel >> 8) & 0xff;
3564 dx[2] = (pixel >> 16) & 0xff;
3566 dx += dstxinc;
3568 d += dstyinc;
3570 break;
3572 default:
3573 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
3574 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
3575 hr = WINED3DERR_NOTAVAILABLE;
3576 goto error;
3577 #undef COPY_COLORKEY_FX
3582 error:
3583 if (flags)
3584 FIXME(" Unsupported flags %#x.\n", flags);
3586 release:
3587 wined3d_resource_unmap(&dst_texture->resource, dst_sub_resource_idx);
3588 if (src_texture && !same_sub_resource)
3589 wined3d_resource_unmap(&src_texture->resource, src_sub_resource_idx);
3590 if (converted_texture)
3591 wined3d_texture_decref(converted_texture);
3593 return hr;
3596 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
3597 const RECT *rect, const struct wined3d_color *color)
3599 const struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
3600 static const struct wined3d_box src_box;
3601 struct wined3d_blt_fx fx;
3603 fx.fill_color = wined3d_format_convert_from_float(view->format, color);
3604 return surface_cpu_blt(texture_from_resource(view->resource), view->sub_resource_idx,
3605 &box, NULL, 0, &src_box, WINED3D_BLT_COLOR_FILL, &fx, WINED3D_TEXF_POINT);
3608 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
3609 struct wined3d_rendertarget_view *view, const RECT *rect, DWORD clear_flags,
3610 float depth, DWORD stencil)
3612 FIXME("Depth/stencil filling not implemented by cpu_blit.\n");
3613 return WINED3DERR_INVALIDCALL;
3616 static void cpu_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
3617 struct wined3d_surface *src_surface, const RECT *src_rect,
3618 struct wined3d_surface *dst_surface, const RECT *dst_rect,
3619 const struct wined3d_color_key *color_key)
3621 /* FIXME: Remove error returns from surface_blt_cpu. */
3622 ERR("Blit method not implemented by cpu_blit.\n");
3625 const struct blit_shader cpu_blit = {
3626 cpu_blit_alloc,
3627 cpu_blit_free,
3628 cpu_blit_set,
3629 cpu_blit_unset,
3630 cpu_blit_supported,
3631 cpu_blit_color_fill,
3632 cpu_blit_depth_fill,
3633 cpu_blit_blit_surface,
3636 HRESULT wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3637 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
3638 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
3640 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
3641 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
3642 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
3643 struct wined3d_texture *dst_texture = dst_surface->container;
3644 struct wined3d_device *device = dst_texture->resource.device;
3645 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3646 struct wined3d_texture *src_texture = NULL;
3647 unsigned int dst_w, dst_h, src_w, src_h;
3648 unsigned int src_sub_resource_idx = 0;
3649 DWORD src_ds_flags, dst_ds_flags;
3650 BOOL scale, convert;
3652 static const DWORD simple_blit = WINED3D_BLT_ASYNC
3653 | WINED3D_BLT_COLOR_FILL
3654 | WINED3D_BLT_SRC_CKEY
3655 | WINED3D_BLT_SRC_CKEY_OVERRIDE
3656 | WINED3D_BLT_WAIT
3657 | WINED3D_BLT_DEPTH_FILL
3658 | WINED3D_BLT_DO_NOT_WAIT
3659 | WINED3D_BLT_ALPHA_TEST;
3661 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
3662 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3663 flags, fx, debug_d3dtexturefiltertype(filter));
3664 TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage));
3666 if (fx)
3668 TRACE("fx %#x.\n", fx->fx);
3669 TRACE("fill_color 0x%08x.\n", fx->fill_color);
3670 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
3671 fx->dst_color_key.color_space_low_value,
3672 fx->dst_color_key.color_space_high_value);
3673 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
3674 fx->src_color_key.color_space_low_value,
3675 fx->src_color_key.color_space_high_value);
3678 if (src_surface)
3680 src_texture = src_surface->container;
3681 src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
3684 if (dst_texture->sub_resources[dst_sub_resource_idx].map_count
3685 || (src_texture && src_texture->sub_resources[src_sub_resource_idx].map_count))
3687 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
3688 return WINEDDERR_SURFACEBUSY;
3691 dst_w = wined3d_texture_get_level_width(dst_texture, dst_surface->texture_level);
3692 dst_h = wined3d_texture_get_level_height(dst_texture, dst_surface->texture_level);
3693 if (IsRectEmpty(dst_rect) || dst_rect->left > dst_w || dst_rect->left < 0
3694 || dst_rect->top > dst_h || dst_rect->top < 0
3695 || dst_rect->right > dst_w || dst_rect->right < 0
3696 || dst_rect->bottom > dst_h || dst_rect->bottom < 0)
3698 WARN("The application gave us a bad destination rectangle.\n");
3699 return WINEDDERR_INVALIDRECT;
3702 if (src_texture)
3704 src_w = wined3d_texture_get_level_width(src_texture, src_surface->texture_level);
3705 src_h = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
3706 if (IsRectEmpty(src_rect) || src_rect->left > src_w || src_rect->left < 0
3707 || src_rect->top > src_h || src_rect->top < 0
3708 || src_rect->right > src_w || src_rect->right < 0
3709 || src_rect->bottom > src_h || src_rect->bottom < 0)
3711 WARN("The application gave us a bad source rectangle.\n");
3712 return WINEDDERR_INVALIDRECT;
3716 if (!fx || !(fx->fx))
3717 flags &= ~WINED3D_BLT_FX;
3719 if (flags & WINED3D_BLT_WAIT)
3720 flags &= ~WINED3D_BLT_WAIT;
3722 if (flags & WINED3D_BLT_ASYNC)
3724 static unsigned int once;
3726 if (!once++)
3727 FIXME("Can't handle WINED3D_BLT_ASYNC flag.\n");
3728 flags &= ~WINED3D_BLT_ASYNC;
3731 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
3732 if (flags & WINED3D_BLT_DO_NOT_WAIT)
3734 static unsigned int once;
3736 if (!once++)
3737 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
3738 flags &= ~WINED3D_BLT_DO_NOT_WAIT;
3741 if (!device->d3d_initialized)
3743 WARN("D3D not initialized, using fallback.\n");
3744 goto cpu;
3747 /* We want to avoid invalidating the sysmem location for converted
3748 * surfaces, since otherwise we'd have to convert the data back when
3749 * locking them. */
3750 if (dst_texture->flags & WINED3D_TEXTURE_CONVERTED || dst_texture->resource.format->convert
3751 || wined3d_format_get_color_key_conversion(dst_texture, TRUE))
3753 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
3754 goto cpu;
3757 if (flags & ~simple_blit)
3759 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
3760 goto fallback;
3763 if (src_texture)
3764 src_swapchain = src_texture->swapchain;
3765 else
3766 src_swapchain = NULL;
3768 dst_swapchain = dst_texture->swapchain;
3770 /* This isn't strictly needed. FBO blits for example could deal with
3771 * cross-swapchain blits by first downloading the source to a texture
3772 * before switching to the destination context. We just have this here to
3773 * not have to deal with the issue, since cross-swapchain blits should be
3774 * rare. */
3775 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
3777 FIXME("Using fallback for cross-swapchain blit.\n");
3778 goto fallback;
3781 scale = src_texture
3782 && (src_rect->right - src_rect->left != dst_rect->right - dst_rect->left
3783 || src_rect->bottom - src_rect->top != dst_rect->bottom - dst_rect->top);
3784 convert = src_texture && src_texture->resource.format->id != dst_texture->resource.format->id;
3786 dst_ds_flags = dst_texture->resource.format_flags
3787 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
3788 if (src_texture)
3789 src_ds_flags = src_texture->resource.format_flags
3790 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
3791 else
3792 src_ds_flags = 0;
3794 if (src_ds_flags || dst_ds_flags)
3796 if (flags & WINED3D_BLT_DEPTH_FILL)
3798 float depth;
3800 TRACE("Depth fill.\n");
3802 if (!surface_convert_depth_to_float(dst_surface, fx->fill_color, &depth))
3803 return WINED3DERR_INVALIDCALL;
3805 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, dst_rect, depth)))
3806 return WINED3D_OK;
3808 else
3810 if (src_ds_flags != dst_ds_flags)
3812 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
3813 return WINED3DERR_INVALIDCALL;
3816 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_texture->resource.draw_binding,
3817 src_rect, dst_surface, dst_texture->resource.draw_binding, dst_rect)))
3818 return WINED3D_OK;
3821 else
3823 struct wined3d_texture_sub_resource *src_sub_resource, *dst_sub_resource;
3824 const struct blit_shader *blitter;
3826 dst_sub_resource = surface_get_sub_resource(dst_surface);
3827 src_sub_resource = src_texture ? &src_texture->sub_resources[src_sub_resource_idx] : NULL;
3829 /* In principle this would apply to depth blits as well, but we don't
3830 * implement those in the CPU blitter at the moment. */
3831 if ((dst_sub_resource->locations & dst_texture->resource.map_binding)
3832 && (!src_texture || (src_sub_resource->locations & src_texture->resource.map_binding)))
3834 if (scale)
3835 TRACE("Not doing sysmem blit because of scaling.\n");
3836 else if (convert)
3837 TRACE("Not doing sysmem blit because of format conversion.\n");
3838 else
3839 goto cpu;
3842 if (flags & WINED3D_BLT_COLOR_FILL)
3844 struct wined3d_color color;
3845 const struct wined3d_palette *palette = dst_swapchain ? dst_swapchain->palette : NULL;
3847 TRACE("Color fill.\n");
3849 if (!wined3d_format_convert_color_to_float(dst_texture->resource.format,
3850 palette, fx->fill_color, &color))
3851 goto fallback;
3853 if (SUCCEEDED(surface_color_fill(dst_surface, dst_rect, &color)))
3854 return WINED3D_OK;
3856 else
3858 enum wined3d_blit_op blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
3859 const struct wined3d_color_key *color_key = NULL;
3861 TRACE("Color blit.\n");
3862 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3864 color_key = &fx->src_color_key;
3865 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
3867 else if (flags & WINED3D_BLT_SRC_CKEY)
3869 color_key = &src_texture->async.src_blt_color_key;
3870 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
3872 else if (flags & WINED3D_BLT_ALPHA_TEST)
3874 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
3876 else if ((src_sub_resource->locations & WINED3D_LOCATION_SYSMEM)
3877 && !(dst_sub_resource->locations & WINED3D_LOCATION_SYSMEM))
3879 /* Upload */
3880 if (scale)
3881 TRACE("Not doing upload because of scaling.\n");
3882 else if (convert)
3883 TRACE("Not doing upload because of format conversion.\n");
3884 else
3886 POINT dst_point = {dst_rect->left, dst_rect->top};
3888 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, src_rect)))
3890 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
3892 struct wined3d_context *context = context_acquire(device,
3893 dst_texture, dst_sub_resource_idx);
3894 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx,
3895 context, dst_texture->resource.draw_binding);
3896 context_release(context);
3898 return WINED3D_OK;
3902 else if (dst_swapchain && dst_swapchain->back_buffers
3903 && dst_texture == dst_swapchain->front_buffer
3904 && src_texture == dst_swapchain->back_buffers[0])
3906 /* Use present for back -> front blits. The idea behind this is
3907 * that present is potentially faster than a blit, in particular
3908 * when FBO blits aren't available. Some ddraw applications like
3909 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
3910 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
3911 * applications can't blit directly to the frontbuffer. */
3912 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
3914 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
3916 /* Set the swap effect to COPY, we don't want the backbuffer
3917 * to become undefined. */
3918 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
3919 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, 0);
3920 dst_swapchain->desc.swap_effect = swap_effect;
3922 return WINED3D_OK;
3925 if (fbo_blit_supported(&device->adapter->gl_info, blit_op,
3926 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
3927 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format))
3929 struct wined3d_context *context;
3930 TRACE("Using FBO blit.\n");
3932 context = context_acquire(device, NULL, 0);
3933 surface_blt_fbo(device, context, filter,
3934 src_surface, src_texture->resource.draw_binding, src_rect,
3935 dst_surface, dst_texture->resource.draw_binding, dst_rect);
3936 context_release(context);
3938 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx,
3939 dst_texture->resource.draw_binding);
3940 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx,
3941 ~dst_texture->resource.draw_binding);
3943 return WINED3D_OK;
3946 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, blit_op,
3947 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
3948 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format);
3949 if (blitter)
3951 blitter->blit_surface(device, blit_op, filter, src_surface,
3952 src_rect, dst_surface, dst_rect, color_key);
3953 return WINED3D_OK;
3958 fallback:
3959 /* Special cases for render targets. */
3960 if (SUCCEEDED(surface_blt_special(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter)))
3961 return WINED3D_OK;
3963 cpu:
3964 return surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
3965 src_texture, src_sub_resource_idx, &src_box, flags, fx, filter);