reg/tests: Test import with non-standard registry file headers.
[wine.git] / dlls / wined3d / surface.c
blob51200d259e726e3869833abb875e1a42cb23a5ee
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 HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
646 struct wined3d_resource *resource = &surface->container->resource;
647 struct wined3d_device *device = resource->device;
648 struct wined3d_rendertarget_view *view;
649 struct wined3d_view_desc view_desc;
650 const struct blit_shader *blitter;
651 HRESULT hr;
653 if (!(blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info,
654 WINED3D_BLIT_OP_DEPTH_FILL, NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format)))
656 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
657 return WINED3DERR_INVALIDCALL;
660 view_desc.format_id = resource->format->id;
661 view_desc.flags = 0;
662 view_desc.u.texture.level_idx = surface->texture_level;
663 view_desc.u.texture.level_count = 1;
664 view_desc.u.texture.layer_idx = surface->texture_layer;
665 view_desc.u.texture.layer_count = 1;
666 if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc,
667 resource, NULL, &wined3d_null_parent_ops, &view)))
669 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
670 return hr;
673 hr = blitter->depth_fill(device, view, rect, WINED3DCLEAR_ZBUFFER, depth, 0);
674 wined3d_rendertarget_view_decref(view);
676 return hr;
679 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
680 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
682 struct wined3d_texture *src_texture = src_surface->container;
683 struct wined3d_texture *dst_texture = dst_surface->container;
684 struct wined3d_device *device = src_texture->resource.device;
685 unsigned int dst_sub_resource_idx;
687 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
688 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
689 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format))
690 return WINED3DERR_INVALIDCALL;
692 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
694 dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
695 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, dst_location);
696 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~dst_location);
698 return WINED3D_OK;
701 /* This call just downloads data, the caller is responsible for binding the
702 * correct texture. */
703 /* Context activation is done by the caller. */
704 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
705 DWORD dst_location)
707 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
708 struct wined3d_texture *texture = surface->container;
709 const struct wined3d_format *format = texture->resource.format;
710 struct wined3d_texture_sub_resource *sub_resource;
711 unsigned int dst_row_pitch, dst_slice_pitch;
712 unsigned int src_row_pitch, src_slice_pitch;
713 struct wined3d_bo_address data;
714 BYTE *temporary_mem = NULL;
715 void *mem;
717 /* Only support read back of converted P8 surfaces. */
718 if (texture->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
720 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
721 return;
724 sub_resource = &texture->sub_resources[sub_resource_idx];
726 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
728 /* NP2 emulation is not allowed on array textures. */
729 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
730 ERR("Array texture %p uses NP2 emulation.\n", texture);
732 WARN_(d3d_perf)("Downloading all miplevel layers to get the surface data for a single sub-resource.\n");
734 if (!(temporary_mem = wined3d_calloc(texture->layer_count, sub_resource->size)))
736 ERR("Out of memory.\n");
737 return;
741 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
743 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
745 wined3d_texture_get_pitch(texture, surface->texture_level, &dst_row_pitch, &dst_slice_pitch);
746 wined3d_format_calculate_pitch(format, texture->resource.device->surface_alignment,
747 wined3d_texture_get_level_pow2_width(texture, surface->texture_level),
748 wined3d_texture_get_level_pow2_height(texture, surface->texture_level),
749 &src_row_pitch, &src_slice_pitch);
750 if (!(temporary_mem = HeapAlloc(GetProcessHeap(), 0, src_slice_pitch)))
752 ERR("Out of memory.\n");
753 return;
756 if (data.buffer_object)
757 ERR("NP2 emulated texture uses PBO unexpectedly.\n");
758 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
759 ERR("Unexpected compressed format for NP2 emulated texture.\n");
762 if (temporary_mem)
764 mem = temporary_mem;
766 else if (data.buffer_object)
768 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
769 checkGLcall("glBindBuffer");
770 mem = data.addr;
772 else
774 mem = data.addr;
777 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
779 TRACE("Downloading compressed surface %p, level %u, format %#x, type %#x, data %p.\n",
780 surface, surface->texture_level, format->glFormat, format->glType, mem);
782 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target, surface->texture_level, mem));
783 checkGLcall("glGetCompressedTexImage");
785 else
787 TRACE("Downloading surface %p, level %u, format %#x, type %#x, data %p.\n",
788 surface, surface->texture_level, format->glFormat, format->glType, mem);
790 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
791 format->glFormat, format->glType, mem);
792 checkGLcall("glGetTexImage");
795 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
797 const BYTE *src_data;
798 unsigned int h, y;
799 BYTE *dst_data;
801 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
802 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
803 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
805 * We're doing this...
807 * instead of boxing the texture :
808 * |<-texture width ->| -->pow2width| /\
809 * |111111111111111111| | |
810 * |222 Texture 222222| boxed empty | texture height
811 * |3333 Data 33333333| | |
812 * |444444444444444444| | \/
813 * ----------------------------------- |
814 * | boxed empty | boxed empty | pow2height
815 * | | | \/
816 * -----------------------------------
819 * we're repacking the data to the expected texture width
821 * |<-texture width ->| -->pow2width| /\
822 * |111111111111111111222222222222222| |
823 * |222333333333333333333444444444444| texture height
824 * |444444 | |
825 * | | \/
826 * | | |
827 * | empty | pow2height
828 * | | \/
829 * -----------------------------------
831 * == is the same as
833 * |<-texture width ->| /\
834 * |111111111111111111|
835 * |222222222222222222|texture height
836 * |333333333333333333|
837 * |444444444444444444| \/
838 * --------------------
840 * This also means that any references to surface memory should work with the data as if it were a
841 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
843 * internally the texture is still stored in a boxed format so any references to textureName will
844 * get a boxed texture with width pow2width and not a texture of width resource.width. */
845 src_data = mem;
846 dst_data = data.addr;
847 TRACE("Repacking the surface data from pitch %u to pitch %u.\n", src_row_pitch, dst_row_pitch);
848 h = wined3d_texture_get_level_height(texture, surface->texture_level);
849 for (y = 0; y < h; ++y)
851 memcpy(dst_data, src_data, dst_row_pitch);
852 src_data += src_row_pitch;
853 dst_data += dst_row_pitch;
856 else if (temporary_mem)
858 void *src_data = temporary_mem + surface->texture_layer * sub_resource->size;
859 if (data.buffer_object)
861 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
862 checkGLcall("glBindBuffer");
863 GL_EXTCALL(glBufferSubData(GL_PIXEL_PACK_BUFFER, 0, sub_resource->size, src_data));
864 checkGLcall("glBufferSubData");
866 else
868 memcpy(data.addr, src_data, sub_resource->size);
872 if (data.buffer_object)
874 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
875 checkGLcall("glBindBuffer");
878 HeapFree(GetProcessHeap(), 0, temporary_mem);
881 /* This call just uploads data, the caller is responsible for binding the
882 * correct texture. */
883 /* Context activation is done by the caller. */
884 void wined3d_surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
885 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
886 BOOL srgb, const struct wined3d_const_bo_address *data)
888 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
889 struct wined3d_texture *texture = surface->container;
890 UINT update_w = src_rect->right - src_rect->left;
891 UINT update_h = src_rect->bottom - src_rect->top;
893 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
894 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
895 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
897 if (texture->sub_resources[sub_resource_idx].map_count)
899 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
900 texture->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
903 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE)
905 update_h *= format->height_scale.numerator;
906 update_h /= format->height_scale.denominator;
909 if (data->buffer_object)
911 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
912 checkGLcall("glBindBuffer");
915 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
917 unsigned int dst_row_pitch, dst_slice_pitch;
918 const BYTE *addr = data->addr;
919 GLenum internal;
921 addr += (src_rect->top / format->block_height) * src_pitch;
922 addr += (src_rect->left / format->block_width) * format->block_byte_count;
924 if (srgb)
925 internal = format->glGammaInternal;
926 else if (texture->resource.usage & WINED3DUSAGE_RENDERTARGET
927 && wined3d_resource_is_offscreen(&texture->resource))
928 internal = format->rtInternal;
929 else
930 internal = format->glInternal;
932 wined3d_format_calculate_pitch(format, 1, update_w, update_h, &dst_row_pitch, &dst_slice_pitch);
934 TRACE("Uploading compressed data, target %#x, level %u, layer %u, x %d, y %d, w %u, h %u, "
935 "format %#x, image_size %#x, addr %p.\n",
936 surface->texture_target, surface->texture_level, surface->texture_layer,
937 dst_point->x, dst_point->y, update_w, update_h, internal, dst_slice_pitch, addr);
939 if (dst_row_pitch == src_pitch)
941 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
943 GL_EXTCALL(glCompressedTexSubImage3D(surface->texture_target, surface->texture_level,
944 dst_point->x, dst_point->y, surface->texture_layer, update_w, update_h, 1,
945 internal, dst_slice_pitch, addr));
947 else
949 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
950 dst_point->x, dst_point->y, update_w, update_h,
951 internal, dst_slice_pitch, addr));
954 else
956 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
957 UINT row, y;
959 /* glCompressedTexSubImage2D() ignores pixel store state, so we
960 * can't use the unpack row length like for glTexSubImage2D. */
961 for (row = 0, y = dst_point->y; row < row_count; ++row)
963 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
965 GL_EXTCALL(glCompressedTexSubImage3D(surface->texture_target, surface->texture_level,
966 dst_point->x, y, surface->texture_layer, update_w, format->block_height, 1,
967 internal, dst_row_pitch, addr));
969 else
971 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
972 dst_point->x, y, update_w, format->block_height, internal, dst_row_pitch, addr));
975 y += format->block_height;
976 addr += src_pitch;
979 checkGLcall("Upload compressed surface data");
981 else
983 const BYTE *addr = data->addr;
985 addr += src_rect->top * src_pitch;
986 addr += src_rect->left * format->byte_count;
988 TRACE("Uploading data, target %#x, level %u, layer %u, x %d, y %d, w %u, h %u, "
989 "format %#x, type %#x, addr %p.\n",
990 surface->texture_target, surface->texture_level, surface->texture_layer,
991 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
993 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
994 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
996 GL_EXTCALL(glTexSubImage3D(surface->texture_target, surface->texture_level,
997 dst_point->x, dst_point->y, surface->texture_layer, update_w, update_h, 1,
998 format->glFormat, format->glType, addr));
1000 else
1002 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1003 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1005 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1006 checkGLcall("Upload surface data");
1009 if (data->buffer_object)
1011 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
1012 checkGLcall("glBindBuffer");
1015 if (wined3d_settings.strict_draw_ordering)
1016 gl_info->gl_ops.gl.p_glFlush();
1018 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1020 struct wined3d_device *device = texture->resource.device;
1021 unsigned int i;
1023 for (i = 0; i < device->context_count; ++i)
1025 context_surface_update(device->contexts[i], surface);
1030 static BOOL surface_check_block_align_rect(struct wined3d_surface *surface, const RECT *rect)
1032 struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
1034 return wined3d_texture_check_block_align(surface->container, surface->texture_level, &box);
1037 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1038 struct wined3d_surface *src_surface, const RECT *src_rect)
1040 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
1041 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1042 struct wined3d_texture *src_texture = src_surface->container;
1043 struct wined3d_texture *dst_texture = dst_surface->container;
1044 unsigned int src_row_pitch, src_slice_pitch;
1045 const struct wined3d_format *src_format;
1046 const struct wined3d_format *dst_format;
1047 unsigned int src_fmt_flags, dst_fmt_flags;
1048 const struct wined3d_gl_info *gl_info;
1049 struct wined3d_context *context;
1050 struct wined3d_bo_address data;
1051 UINT update_w, update_h;
1052 UINT dst_w, dst_h;
1053 RECT r, dst_rect;
1054 POINT p;
1056 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1057 dst_surface, wine_dbgstr_point(dst_point),
1058 src_surface, wine_dbgstr_rect(src_rect));
1060 src_format = src_texture->resource.format;
1061 dst_format = dst_texture->resource.format;
1062 src_fmt_flags = src_texture->resource.format_flags;
1063 dst_fmt_flags = dst_texture->resource.format_flags;
1065 if (src_format->id != dst_format->id)
1067 WARN("Source and destination surfaces should have the same format.\n");
1068 return WINED3DERR_INVALIDCALL;
1071 if (!dst_point)
1073 p.x = 0;
1074 p.y = 0;
1075 dst_point = &p;
1077 else if (dst_point->x < 0 || dst_point->y < 0)
1079 WARN("Invalid destination point.\n");
1080 return WINED3DERR_INVALIDCALL;
1083 if (!src_rect)
1085 SetRect(&r, 0, 0, wined3d_texture_get_level_width(src_texture, src_surface->texture_level),
1086 wined3d_texture_get_level_height(src_texture, src_surface->texture_level));
1087 src_rect = &r;
1089 else if (src_rect->left < 0 || src_rect->top < 0 || IsRectEmpty(src_rect))
1091 WARN("Invalid source rectangle.\n");
1092 return WINED3DERR_INVALIDCALL;
1095 dst_w = wined3d_texture_get_level_width(dst_texture, dst_surface->texture_level);
1096 dst_h = wined3d_texture_get_level_height(dst_texture, dst_surface->texture_level);
1098 update_w = src_rect->right - src_rect->left;
1099 update_h = src_rect->bottom - src_rect->top;
1101 if (update_w > dst_w || dst_point->x > dst_w - update_w
1102 || update_h > dst_h || dst_point->y > dst_h - update_h)
1104 WARN("Destination out of bounds.\n");
1105 return WINED3DERR_INVALIDCALL;
1108 if ((src_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(src_surface, src_rect))
1110 WARN("Source rectangle not block-aligned.\n");
1111 return WINED3DERR_INVALIDCALL;
1114 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1115 if ((dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(dst_surface, &dst_rect))
1117 WARN("Destination rectangle not block-aligned.\n");
1118 return WINED3DERR_INVALIDCALL;
1121 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1122 if (dst_format->convert || wined3d_format_get_color_key_conversion(dst_texture, FALSE))
1123 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1125 context = context_acquire(dst_texture->resource.device, NULL, 0);
1126 gl_info = context->gl_info;
1128 /* Only load the surface for partial updates. For newly allocated texture
1129 * the texture wouldn't be the current location, and we'd upload zeroes
1130 * just to overwrite them again. */
1131 if (update_w == dst_w && update_h == dst_h)
1132 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
1133 else
1134 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
1135 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
1137 wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &data,
1138 src_texture->sub_resources[src_sub_resource_idx].locations);
1139 wined3d_texture_get_pitch(src_texture, src_surface->texture_level, &src_row_pitch, &src_slice_pitch);
1141 wined3d_surface_upload_data(dst_surface, gl_info, src_format, src_rect,
1142 src_row_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
1144 context_release(context);
1146 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1147 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1149 return WINED3D_OK;
1152 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1153 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1154 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1155 /* Context activation is done by the caller. */
1156 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1158 const struct wined3d_gl_info *gl_info = &surface->container->resource.device->adapter->gl_info;
1159 struct wined3d_renderbuffer_entry *entry;
1160 GLuint renderbuffer = 0;
1161 unsigned int src_width, src_height;
1162 unsigned int width, height;
1164 if (rt && rt->container->resource.format->id != WINED3DFMT_NULL)
1166 width = wined3d_texture_get_level_pow2_width(rt->container, rt->texture_level);
1167 height = wined3d_texture_get_level_pow2_height(rt->container, rt->texture_level);
1169 else
1171 width = wined3d_texture_get_level_pow2_width(surface->container, surface->texture_level);
1172 height = wined3d_texture_get_level_pow2_height(surface->container, surface->texture_level);
1175 src_width = wined3d_texture_get_level_pow2_width(surface->container, surface->texture_level);
1176 src_height = wined3d_texture_get_level_pow2_height(surface->container, surface->texture_level);
1178 /* A depth stencil smaller than the render target is not valid */
1179 if (width > src_width || height > src_height) return;
1181 /* Remove any renderbuffer set if the sizes match */
1182 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1183 || (width == src_width && height == src_height))
1185 surface->current_renderbuffer = NULL;
1186 return;
1189 /* Look if we've already got a renderbuffer of the correct dimensions */
1190 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1192 if (entry->width == width && entry->height == height)
1194 renderbuffer = entry->id;
1195 surface->current_renderbuffer = entry;
1196 break;
1200 if (!renderbuffer)
1202 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1203 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1204 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1205 surface->container->resource.format->glInternal, width, height);
1207 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1208 entry->width = width;
1209 entry->height = height;
1210 entry->id = renderbuffer;
1211 list_add_head(&surface->renderbuffers, &entry->entry);
1213 surface->current_renderbuffer = entry;
1216 checkGLcall("set_compatible_renderbuffer");
1219 /* See also float_16_to_32() in wined3d_private.h */
1220 static inline unsigned short float_32_to_16(const float *in)
1222 int exp = 0;
1223 float tmp = fabsf(*in);
1224 unsigned int mantissa;
1225 unsigned short ret;
1227 /* Deal with special numbers */
1228 if (*in == 0.0f)
1229 return 0x0000;
1230 if (isnan(*in))
1231 return 0x7c01;
1232 if (isinf(*in))
1233 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1235 if (tmp < (float)(1u << 10))
1239 tmp = tmp * 2.0f;
1240 exp--;
1241 } while (tmp < (float)(1u << 10));
1243 else if (tmp >= (float)(1u << 11))
1247 tmp /= 2.0f;
1248 exp++;
1249 } while (tmp >= (float)(1u << 11));
1252 mantissa = (unsigned int)tmp;
1253 if (tmp - mantissa >= 0.5f)
1254 ++mantissa; /* Round to nearest, away from zero. */
1256 exp += 10; /* Normalize the mantissa. */
1257 exp += 15; /* Exponent is encoded with excess 15. */
1259 if (exp > 30) /* too big */
1261 ret = 0x7c00; /* INF */
1263 else if (exp <= 0)
1265 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1266 while (exp <= 0)
1268 mantissa = mantissa >> 1;
1269 ++exp;
1271 ret = mantissa & 0x3ff;
1273 else
1275 ret = (exp << 10) | (mantissa & 0x3ff);
1278 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1279 return ret;
1282 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
1283 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1285 unsigned short *dst_s;
1286 const float *src_f;
1287 unsigned int x, y;
1289 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1291 for (y = 0; y < h; ++y)
1293 src_f = (const float *)(src + y * pitch_in);
1294 dst_s = (unsigned short *) (dst + y * pitch_out);
1295 for (x = 0; x < w; ++x)
1297 dst_s[x] = float_32_to_16(src_f + x);
1302 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
1303 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1305 static const unsigned char convert_5to8[] =
1307 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
1308 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
1309 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
1310 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
1312 static const unsigned char convert_6to8[] =
1314 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
1315 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
1316 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
1317 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
1318 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
1319 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
1320 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
1321 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
1323 unsigned int x, y;
1325 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1327 for (y = 0; y < h; ++y)
1329 const WORD *src_line = (const WORD *)(src + y * pitch_in);
1330 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1331 for (x = 0; x < w; ++x)
1333 WORD pixel = src_line[x];
1334 dst_line[x] = 0xff000000u
1335 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
1336 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
1337 | convert_5to8[(pixel & 0x001fu)];
1342 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
1343 * in both cases we're just setting the X / Alpha channel to 0xff. */
1344 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
1345 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1347 unsigned int x, y;
1349 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1351 for (y = 0; y < h; ++y)
1353 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
1354 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1356 for (x = 0; x < w; ++x)
1358 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
1363 static inline BYTE cliptobyte(int x)
1365 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
1368 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
1369 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1371 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1372 unsigned int x, y;
1374 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1376 for (y = 0; y < h; ++y)
1378 const BYTE *src_line = src + y * pitch_in;
1379 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1380 for (x = 0; x < w; ++x)
1382 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1383 * C = Y - 16; D = U - 128; E = V - 128;
1384 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1385 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1386 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1387 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1388 * U and V are shared between the pixels. */
1389 if (!(x & 1)) /* For every even pixel, read new U and V. */
1391 d = (int) src_line[1] - 128;
1392 e = (int) src_line[3] - 128;
1393 r2 = 409 * e + 128;
1394 g2 = - 100 * d - 208 * e + 128;
1395 b2 = 516 * d + 128;
1397 c2 = 298 * ((int) src_line[0] - 16);
1398 dst_line[x] = 0xff000000
1399 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
1400 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
1401 | cliptobyte((c2 + b2) >> 8); /* blue */
1402 /* Scale RGB values to 0..255 range,
1403 * then clip them if still not in range (may be negative),
1404 * then shift them within DWORD if necessary. */
1405 src_line += 2;
1410 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
1411 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1413 unsigned int x, y;
1414 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1416 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
1418 for (y = 0; y < h; ++y)
1420 const BYTE *src_line = src + y * pitch_in;
1421 WORD *dst_line = (WORD *)(dst + y * pitch_out);
1422 for (x = 0; x < w; ++x)
1424 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1425 * C = Y - 16; D = U - 128; E = V - 128;
1426 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1427 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1428 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1429 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1430 * U and V are shared between the pixels. */
1431 if (!(x & 1)) /* For every even pixel, read new U and V. */
1433 d = (int) src_line[1] - 128;
1434 e = (int) src_line[3] - 128;
1435 r2 = 409 * e + 128;
1436 g2 = - 100 * d - 208 * e + 128;
1437 b2 = 516 * d + 128;
1439 c2 = 298 * ((int) src_line[0] - 16);
1440 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
1441 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
1442 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
1443 /* Scale RGB values to 0..255 range,
1444 * then clip them if still not in range (may be negative),
1445 * then shift them within DWORD if necessary. */
1446 src_line += 2;
1451 struct d3dfmt_converter_desc
1453 enum wined3d_format_id from, to;
1454 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
1457 static const struct d3dfmt_converter_desc converters[] =
1459 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
1460 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
1461 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1462 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1463 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
1464 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
1467 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
1468 enum wined3d_format_id to)
1470 unsigned int i;
1472 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
1474 if (converters[i].from == from && converters[i].to == to)
1475 return &converters[i];
1478 return NULL;
1481 static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture,
1482 unsigned int sub_resource_idx, const struct wined3d_format *dst_format)
1484 unsigned int texture_level = sub_resource_idx % src_texture->level_count;
1485 const struct wined3d_format *src_format = src_texture->resource.format;
1486 struct wined3d_device *device = src_texture->resource.device;
1487 const struct d3dfmt_converter_desc *conv = NULL;
1488 struct wined3d_texture *dst_texture;
1489 struct wined3d_resource_desc desc;
1490 struct wined3d_map_desc src_map;
1492 if (!(conv = find_converter(src_format->id, dst_format->id)) && (!device->d3d_initialized
1493 || !is_identity_fixup(src_format->color_fixup) || src_format->convert
1494 || !is_identity_fixup(dst_format->color_fixup) || dst_format->convert
1495 || (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)))
1497 FIXME("Cannot find a conversion function from format %s to %s.\n",
1498 debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
1499 return NULL;
1502 /* FIXME: Multisampled conversion? */
1503 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
1504 desc.format = dst_format->id;
1505 desc.multisample_type = WINED3D_MULTISAMPLE_NONE;
1506 desc.multisample_quality = 0;
1507 desc.usage = 0;
1508 desc.pool = WINED3D_POOL_SCRATCH;
1509 desc.width = wined3d_texture_get_level_width(src_texture, texture_level);
1510 desc.height = wined3d_texture_get_level_height(src_texture, texture_level);
1511 desc.depth = 1;
1512 desc.size = 0;
1513 if (FAILED(wined3d_texture_create(device, &desc, 1, 1,
1514 WINED3D_TEXTURE_CREATE_MAPPABLE | WINED3D_TEXTURE_CREATE_DISCARD,
1515 NULL, NULL, &wined3d_null_parent_ops, &dst_texture)))
1517 ERR("Failed to create a destination texture for conversion.\n");
1518 return NULL;
1521 memset(&src_map, 0, sizeof(src_map));
1522 if (FAILED(wined3d_resource_map(&src_texture->resource, sub_resource_idx,
1523 &src_map, NULL, WINED3D_MAP_READONLY)))
1525 ERR("Failed to map the source texture.\n");
1526 wined3d_texture_decref(dst_texture);
1527 return NULL;
1529 if (conv)
1531 struct wined3d_map_desc dst_map;
1533 memset(&dst_map, 0, sizeof(dst_map));
1534 if (FAILED(wined3d_resource_map(&dst_texture->resource, 0, &dst_map, NULL, 0)))
1536 ERR("Failed to map the destination texture.\n");
1537 wined3d_resource_unmap(&src_texture->resource, sub_resource_idx);
1538 wined3d_texture_decref(dst_texture);
1539 return NULL;
1542 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch, desc.width, desc.height);
1544 wined3d_resource_unmap(&dst_texture->resource, 0);
1546 else
1548 struct wined3d_bo_address data = {0, src_map.data};
1549 RECT src_rect = {0, 0, desc.width, desc.height};
1550 const struct wined3d_gl_info *gl_info;
1551 struct wined3d_context *context;
1552 POINT dst_point = {0, 0};
1554 TRACE("Using upload conversion.\n");
1555 context = context_acquire(device, NULL, 0);
1556 gl_info = context->gl_info;
1558 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
1559 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
1560 wined3d_surface_upload_data(dst_texture->sub_resources[0].u.surface, gl_info, src_format,
1561 &src_rect, src_map.row_pitch, &dst_point, FALSE, wined3d_const_bo_address(&data));
1563 context_release(context);
1565 wined3d_texture_validate_location(dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB);
1566 wined3d_texture_invalidate_location(dst_texture, 0, ~WINED3D_LOCATION_TEXTURE_RGB);
1568 wined3d_resource_unmap(&src_texture->resource, sub_resource_idx);
1570 return dst_texture;
1573 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
1574 unsigned int bpp, UINT pitch, DWORD color)
1576 BYTE *first;
1577 unsigned int x, y;
1579 /* Do first row */
1581 #define COLORFILL_ROW(type) \
1582 do { \
1583 type *d = (type *)buf; \
1584 for (x = 0; x < width; ++x) \
1585 d[x] = (type)color; \
1586 } while(0)
1588 switch (bpp)
1590 case 1:
1591 COLORFILL_ROW(BYTE);
1592 break;
1594 case 2:
1595 COLORFILL_ROW(WORD);
1596 break;
1598 case 3:
1600 BYTE *d = buf;
1601 for (x = 0; x < width; ++x, d += 3)
1603 d[0] = (color ) & 0xff;
1604 d[1] = (color >> 8) & 0xff;
1605 d[2] = (color >> 16) & 0xff;
1607 break;
1609 case 4:
1610 COLORFILL_ROW(DWORD);
1611 break;
1613 default:
1614 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
1615 return WINED3DERR_NOTAVAILABLE;
1618 #undef COLORFILL_ROW
1620 /* Now copy first row. */
1621 first = buf;
1622 for (y = 1; y < height; ++y)
1624 buf += pitch;
1625 memcpy(buf, first, width * bpp);
1628 return WINED3D_OK;
1631 static void read_from_framebuffer(struct wined3d_surface *surface,
1632 struct wined3d_context *old_ctx, DWORD dst_location)
1634 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
1635 struct wined3d_texture *texture = surface->container;
1636 struct wined3d_device *device = texture->resource.device;
1637 const struct wined3d_gl_info *gl_info;
1638 struct wined3d_context *context = old_ctx;
1639 struct wined3d_surface *restore_rt = NULL;
1640 unsigned int row_pitch, slice_pitch;
1641 unsigned int width, height;
1642 BYTE *mem;
1643 BYTE *row, *top, *bottom;
1644 int i;
1645 BOOL srcIsUpsideDown;
1646 struct wined3d_bo_address data;
1648 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
1650 restore_rt = context_get_rt_surface(old_ctx);
1651 if (restore_rt != surface)
1652 context = context_acquire(device, texture, sub_resource_idx);
1653 else
1654 restore_rt = NULL;
1656 context_apply_blit_state(context, device);
1657 gl_info = context->gl_info;
1659 /* Select the correct read buffer, and give some debug output.
1660 * There is no need to keep track of the current read buffer or reset it, every part of the code
1661 * that reads sets the read buffer as desired.
1663 if (wined3d_resource_is_offscreen(&texture->resource))
1665 /* Mapping the primary render target which is not on a swapchain.
1666 * Read from the back buffer. */
1667 TRACE("Mapping offscreen render target.\n");
1668 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1669 srcIsUpsideDown = TRUE;
1671 else
1673 /* Onscreen surfaces are always part of a swapchain */
1674 GLenum buffer = wined3d_texture_get_gl_buffer(texture);
1675 TRACE("Mapping %#x buffer.\n", buffer);
1676 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
1677 checkGLcall("glReadBuffer");
1678 srcIsUpsideDown = FALSE;
1681 if (data.buffer_object)
1683 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1684 checkGLcall("glBindBuffer");
1687 wined3d_texture_get_pitch(texture, surface->texture_level, &row_pitch, &slice_pitch);
1689 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1690 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / texture->resource.format->byte_count);
1691 checkGLcall("glPixelStorei");
1693 width = wined3d_texture_get_level_width(texture, surface->texture_level);
1694 height = wined3d_texture_get_level_height(texture, surface->texture_level);
1695 gl_info->gl_ops.gl.p_glReadPixels(0, 0, width, height,
1696 texture->resource.format->glFormat,
1697 texture->resource.format->glType, data.addr);
1698 checkGLcall("glReadPixels");
1700 /* Reset previous pixel store pack state */
1701 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
1702 checkGLcall("glPixelStorei");
1704 if (!srcIsUpsideDown)
1706 /* glReadPixels returns the image upside down, and there is no way to
1707 * prevent this. Flip the lines in software. */
1709 if (!(row = HeapAlloc(GetProcessHeap(), 0, row_pitch)))
1710 goto error;
1712 if (data.buffer_object)
1714 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
1715 checkGLcall("glMapBuffer");
1717 else
1718 mem = data.addr;
1720 top = mem;
1721 bottom = mem + row_pitch * (height - 1);
1722 for (i = 0; i < height / 2; i++)
1724 memcpy(row, top, row_pitch);
1725 memcpy(top, bottom, row_pitch);
1726 memcpy(bottom, row, row_pitch);
1727 top += row_pitch;
1728 bottom -= row_pitch;
1730 HeapFree(GetProcessHeap(), 0, row);
1732 if (data.buffer_object)
1733 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
1736 error:
1737 if (data.buffer_object)
1739 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1740 checkGLcall("glBindBuffer");
1743 if (restore_rt)
1744 context_restore(context, restore_rt);
1747 /* Read the framebuffer contents into a texture. Note that this function
1748 * doesn't do any kind of flipping. Using this on an onscreen surface will
1749 * result in a flipped D3D texture.
1751 * Context activation is done by the caller. This function may temporarily
1752 * switch to a different context and restore the original one before return. */
1753 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb, struct wined3d_context *old_ctx)
1755 struct wined3d_texture *texture = surface->container;
1756 struct wined3d_device *device = texture->resource.device;
1757 const struct wined3d_gl_info *gl_info;
1758 struct wined3d_context *context = old_ctx;
1759 struct wined3d_surface *restore_rt = NULL;
1761 restore_rt = context_get_rt_surface(old_ctx);
1762 if (restore_rt != surface)
1763 context = context_acquire(device, texture, surface_get_sub_resource_idx(surface));
1764 else
1765 restore_rt = NULL;
1767 gl_info = context->gl_info;
1768 device_invalidate_state(device, STATE_FRAMEBUFFER);
1770 wined3d_texture_prepare_texture(texture, context, srgb);
1771 wined3d_texture_bind_and_dirtify(texture, context, srgb);
1773 TRACE("Reading back offscreen render target %p.\n", surface);
1775 if (wined3d_resource_is_offscreen(&texture->resource))
1776 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1777 else
1778 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(texture));
1779 checkGLcall("glReadBuffer");
1781 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
1782 0, 0, 0, 0, wined3d_texture_get_level_width(texture, surface->texture_level),
1783 wined3d_texture_get_level_height(texture, surface->texture_level));
1784 checkGLcall("glCopyTexSubImage2D");
1786 if (restore_rt)
1787 context_restore(context, restore_rt);
1790 /* Does a direct frame buffer -> texture copy. Stretching is done with single
1791 * pixel copy calls. */
1792 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
1793 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
1795 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
1796 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1797 struct wined3d_texture *src_texture = src_surface->container;
1798 struct wined3d_texture *dst_texture = dst_surface->container;
1799 struct wined3d_device *device = dst_texture->resource.device;
1800 const struct wined3d_gl_info *gl_info;
1801 float xrel, yrel;
1802 struct wined3d_context *context;
1803 BOOL upsidedown = FALSE;
1804 RECT dst_rect = *dst_rect_in;
1805 unsigned int src_height;
1807 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1808 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1810 if(dst_rect.top > dst_rect.bottom) {
1811 UINT tmp = dst_rect.bottom;
1812 dst_rect.bottom = dst_rect.top;
1813 dst_rect.top = tmp;
1814 upsidedown = TRUE;
1817 context = context_acquire(device, src_texture, src_sub_resource_idx);
1818 gl_info = context->gl_info;
1819 context_apply_blit_state(context, device);
1820 wined3d_texture_load(dst_texture, context, FALSE);
1822 /* Bind the target texture */
1823 context_bind_texture(context, dst_texture->target, dst_texture->texture_rgb.name);
1824 if (wined3d_resource_is_offscreen(&src_texture->resource))
1826 TRACE("Reading from an offscreen target\n");
1827 upsidedown = !upsidedown;
1828 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1830 else
1832 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
1834 checkGLcall("glReadBuffer");
1836 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
1837 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
1839 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1841 FIXME_(d3d_perf)("Doing a pixel by pixel copy from the framebuffer to a texture.\n");
1843 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1844 ERR("Texture filtering not supported in direct blit.\n");
1846 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1847 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1849 ERR("Texture filtering not supported in direct blit\n");
1852 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
1853 if (upsidedown
1854 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1855 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1857 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
1858 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1859 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
1860 src_rect->left, src_height - src_rect->bottom,
1861 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1863 else
1865 LONG row;
1866 UINT yoffset = src_height - src_rect->top + dst_rect.top - 1;
1867 /* I have to process this row by row to swap the image,
1868 * otherwise it would be upside down, so stretching in y direction
1869 * doesn't cost extra time
1871 * However, stretching in x direction can be avoided if not necessary
1873 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
1874 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1876 /* Well, that stuff works, but it's very slow.
1877 * find a better way instead
1879 LONG col;
1881 for (col = dst_rect.left; col < dst_rect.right; ++col)
1883 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1884 dst_rect.left + col /* x offset */, row /* y offset */,
1885 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
1888 else
1890 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1891 dst_rect.left /* x offset */, row /* y offset */,
1892 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
1896 checkGLcall("glCopyTexSubImage2D");
1898 context_release(context);
1900 /* The texture is now most up to date - If the surface is a render target
1901 * and has a drawable, this path is never entered. */
1902 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1903 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1906 /* Uses the hardware to stretch and flip the image */
1907 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
1908 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
1910 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
1911 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1912 unsigned int src_width, src_height, src_pow2_width, src_pow2_height;
1913 struct wined3d_texture *src_texture = src_surface->container;
1914 struct wined3d_texture *dst_texture = dst_surface->container;
1915 struct wined3d_device *device = dst_texture->resource.device;
1916 GLuint src, backup = 0;
1917 float left, right, top, bottom; /* Texture coordinates */
1918 const struct wined3d_gl_info *gl_info;
1919 struct wined3d_context *context;
1920 GLenum drawBuffer = GL_BACK;
1921 GLenum offscreen_buffer;
1922 GLenum texture_target;
1923 BOOL noBackBufferBackup;
1924 BOOL src_offscreen;
1925 BOOL upsidedown = FALSE;
1926 RECT dst_rect = *dst_rect_in;
1928 TRACE("Using hwstretch blit\n");
1929 /* Activate the Proper context for reading from the source surface, set it up for blitting */
1930 context = context_acquire(device, src_texture, src_sub_resource_idx);
1931 gl_info = context->gl_info;
1932 context_apply_blit_state(context, device);
1933 wined3d_texture_load(dst_texture, context, FALSE);
1935 offscreen_buffer = context_get_offscreen_gl_buffer(context);
1936 src_width = wined3d_texture_get_level_width(src_texture, src_surface->texture_level);
1937 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
1938 src_pow2_width = wined3d_texture_get_level_pow2_width(src_texture, src_surface->texture_level);
1939 src_pow2_height = wined3d_texture_get_level_pow2_height(src_texture, src_surface->texture_level);
1941 src_offscreen = wined3d_resource_is_offscreen(&src_texture->resource);
1942 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
1943 if (!noBackBufferBackup && !src_texture->texture_rgb.name)
1945 /* Get it a description */
1946 wined3d_texture_load(src_texture, context, FALSE);
1949 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
1950 * This way we don't have to wait for the 2nd readback to finish to leave this function.
1952 if (context->aux_buffers >= 2)
1954 /* Got more than one aux buffer? Use the 2nd aux buffer */
1955 drawBuffer = GL_AUX1;
1957 else if ((!src_offscreen || offscreen_buffer == GL_BACK) && context->aux_buffers >= 1)
1959 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
1960 drawBuffer = GL_AUX0;
1963 if (noBackBufferBackup)
1965 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
1966 checkGLcall("glGenTextures");
1967 context_bind_texture(context, GL_TEXTURE_2D, backup);
1968 texture_target = GL_TEXTURE_2D;
1970 else
1972 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
1973 * we are reading from the back buffer, the backup can be used as source texture
1975 texture_target = src_surface->texture_target;
1976 context_bind_texture(context, texture_target, src_texture->texture_rgb.name);
1977 gl_info->gl_ops.gl.p_glEnable(texture_target);
1978 checkGLcall("glEnable(texture_target)");
1980 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
1981 surface_get_sub_resource(src_surface)->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
1984 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1985 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1987 if(dst_rect.top > dst_rect.bottom) {
1988 UINT tmp = dst_rect.bottom;
1989 dst_rect.bottom = dst_rect.top;
1990 dst_rect.top = tmp;
1991 upsidedown = TRUE;
1994 if (src_offscreen)
1996 TRACE("Reading from an offscreen target\n");
1997 upsidedown = !upsidedown;
1998 gl_info->gl_ops.gl.p_glReadBuffer(offscreen_buffer);
2000 else
2002 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
2005 /* TODO: Only back up the part that will be overwritten */
2006 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, src_width, src_height);
2008 checkGLcall("glCopyTexSubImage2D");
2010 /* No issue with overriding these - the sampler is dirty due to blit usage */
2011 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
2012 checkGLcall("glTexParameteri");
2013 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2014 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
2015 checkGLcall("glTexParameteri");
2017 if (!src_texture->swapchain || src_texture == src_texture->swapchain->back_buffers[0])
2019 src = backup ? backup : src_texture->texture_rgb.name;
2021 else
2023 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
2024 checkGLcall("glReadBuffer(GL_FRONT)");
2026 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
2027 checkGLcall("glGenTextures(1, &src)");
2028 context_bind_texture(context, GL_TEXTURE_2D, src);
2030 /* TODO: Only copy the part that will be read. Use src_rect->left,
2031 * src_rect->bottom as origin, but with the width watch out for power
2032 * of 2 sizes. */
2033 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_pow2_width,
2034 src_pow2_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2035 checkGLcall("glTexImage2D");
2036 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, src_width, src_height);
2038 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2039 checkGLcall("glTexParameteri");
2040 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2041 checkGLcall("glTexParameteri");
2043 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
2044 checkGLcall("glReadBuffer(GL_BACK)");
2046 if (texture_target != GL_TEXTURE_2D)
2048 gl_info->gl_ops.gl.p_glDisable(texture_target);
2049 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
2050 texture_target = GL_TEXTURE_2D;
2053 checkGLcall("glEnd and previous");
2055 left = src_rect->left;
2056 right = src_rect->right;
2058 if (!upsidedown)
2060 top = src_height - src_rect->top;
2061 bottom = src_height - src_rect->bottom;
2063 else
2065 top = src_height - src_rect->bottom;
2066 bottom = src_height - src_rect->top;
2069 if (src_texture->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
2071 left /= src_pow2_width;
2072 right /= src_pow2_width;
2073 top /= src_pow2_height;
2074 bottom /= src_pow2_height;
2077 /* draw the source texture stretched and upside down. The correct surface is bound already */
2078 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2079 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2081 context_set_draw_buffer(context, drawBuffer);
2082 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
2084 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
2085 /* bottom left */
2086 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
2087 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
2089 /* top left */
2090 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
2091 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
2093 /* top right */
2094 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
2095 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2097 /* bottom right */
2098 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
2099 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
2100 gl_info->gl_ops.gl.p_glEnd();
2101 checkGLcall("glEnd and previous");
2103 if (texture_target != dst_surface->texture_target)
2105 gl_info->gl_ops.gl.p_glDisable(texture_target);
2106 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
2107 texture_target = dst_surface->texture_target;
2110 /* Now read the stretched and upside down image into the destination texture */
2111 context_bind_texture(context, texture_target, dst_texture->texture_rgb.name);
2112 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
2114 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
2115 0, 0, /* We blitted the image to the origin */
2116 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2117 checkGLcall("glCopyTexSubImage2D");
2119 if (drawBuffer == GL_BACK)
2121 /* Write the back buffer backup back. */
2122 if (backup)
2124 if (texture_target != GL_TEXTURE_2D)
2126 gl_info->gl_ops.gl.p_glDisable(texture_target);
2127 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
2128 texture_target = GL_TEXTURE_2D;
2130 context_bind_texture(context, GL_TEXTURE_2D, backup);
2132 else
2134 if (texture_target != src_surface->texture_target)
2136 gl_info->gl_ops.gl.p_glDisable(texture_target);
2137 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
2138 texture_target = src_surface->texture_target;
2140 context_bind_texture(context, src_surface->texture_target, src_texture->texture_rgb.name);
2143 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
2144 /* top left */
2145 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
2146 gl_info->gl_ops.gl.p_glVertex2i(0, src_height);
2148 /* bottom left */
2149 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)src_height / (float)src_pow2_height);
2150 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
2152 /* bottom right */
2153 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width,
2154 (float)src_height / (float)src_pow2_height);
2155 gl_info->gl_ops.gl.p_glVertex2i(src_width, 0);
2157 /* top right */
2158 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width, 0.0f);
2159 gl_info->gl_ops.gl.p_glVertex2i(src_width, src_height);
2160 gl_info->gl_ops.gl.p_glEnd();
2162 gl_info->gl_ops.gl.p_glDisable(texture_target);
2163 checkGLcall("glDisable(texture_target)");
2165 /* Cleanup */
2166 if (src != src_texture->texture_rgb.name && src != backup)
2168 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
2169 checkGLcall("glDeleteTextures(1, &src)");
2171 if (backup)
2173 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
2174 checkGLcall("glDeleteTextures(1, &backup)");
2177 if (wined3d_settings.strict_draw_ordering)
2178 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2180 context_release(context);
2182 /* The texture is now most up to date - If the surface is a render target
2183 * and has a drawable, this path is never entered. */
2184 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
2185 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
2188 /* Front buffer coordinates are always full screen coordinates, but our GL
2189 * drawable is limited to the window's client area. The sysmem and texture
2190 * copies do have the full screen size. Note that GL has a bottom-left
2191 * origin, while D3D has a top-left origin. */
2192 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
2194 struct wined3d_texture *texture = surface->container;
2195 UINT drawable_height;
2197 if (texture->swapchain && texture == texture->swapchain->front_buffer)
2199 POINT offset = {0, 0};
2200 RECT windowsize;
2202 ScreenToClient(window, &offset);
2203 OffsetRect(rect, offset.x, offset.y);
2205 GetClientRect(window, &windowsize);
2206 drawable_height = windowsize.bottom - windowsize.top;
2208 else
2210 drawable_height = wined3d_texture_get_level_height(texture, surface->texture_level);
2213 rect->top = drawable_height - rect->top;
2214 rect->bottom = drawable_height - rect->bottom;
2217 /* Context activation is done by the caller. */
2218 static void surface_blt_to_drawable(const struct wined3d_device *device,
2219 struct wined3d_context *old_ctx,
2220 enum wined3d_texture_filter_type filter, BOOL alpha_test,
2221 struct wined3d_surface *src_surface, const RECT *src_rect_in,
2222 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
2224 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
2225 struct wined3d_texture *src_texture = src_surface->container;
2226 struct wined3d_texture *dst_texture = dst_surface->container;
2227 const struct wined3d_gl_info *gl_info;
2228 struct wined3d_context *context = old_ctx;
2229 struct wined3d_surface *restore_rt = NULL;
2230 RECT src_rect, dst_rect;
2232 src_rect = *src_rect_in;
2233 dst_rect = *dst_rect_in;
2235 restore_rt = context_get_rt_surface(old_ctx);
2236 if (restore_rt != dst_surface)
2237 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
2238 else
2239 restore_rt = NULL;
2241 gl_info = context->gl_info;
2243 /* Make sure the surface is up-to-date. This should probably use
2244 * surface_load_location() and worry about the destination surface too,
2245 * unless we're overwriting it completely. */
2246 wined3d_texture_load(src_texture, context, FALSE);
2248 /* Activate the destination context, set it up for blitting */
2249 context_apply_blit_state(context, device);
2251 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
2252 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
2254 device->blitter->set_shader(device->blit_priv, context, src_surface, NULL);
2256 if (alpha_test)
2258 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
2259 checkGLcall("glEnable(GL_ALPHA_TEST)");
2261 /* For P8 surfaces, the alpha component contains the palette index.
2262 * Which means that the colorkey is one of the palette entries. In
2263 * other cases pixels that should be masked away have alpha set to 0. */
2264 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT)
2265 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
2266 (float)src_texture->async.src_blt_color_key.color_space_low_value / 255.0f);
2267 else
2268 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
2269 checkGLcall("glAlphaFunc");
2271 else
2273 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2274 checkGLcall("glDisable(GL_ALPHA_TEST)");
2277 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
2279 if (alpha_test)
2281 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2282 checkGLcall("glDisable(GL_ALPHA_TEST)");
2285 /* Leave the opengl state valid for blitting */
2286 device->blitter->unset_shader(context->gl_info);
2288 if (wined3d_settings.strict_draw_ordering
2289 || (dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture))
2290 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2292 if (restore_rt)
2293 context_restore(context, restore_rt);
2296 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
2298 struct wined3d_resource *resource = &s->container->resource;
2299 struct wined3d_device *device = resource->device;
2300 struct wined3d_rendertarget_view *view;
2301 struct wined3d_view_desc view_desc;
2302 const struct blit_shader *blitter;
2303 HRESULT hr;
2305 if (!(blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info,
2306 WINED3D_BLIT_OP_COLOR_FILL, NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format)))
2308 FIXME("No blitter is capable of performing the requested color fill operation.\n");
2309 return WINED3DERR_INVALIDCALL;
2312 view_desc.format_id = resource->format->id;
2313 view_desc.flags = 0;
2314 view_desc.u.texture.level_idx = s->texture_level;
2315 view_desc.u.texture.level_count = 1;
2316 view_desc.u.texture.layer_idx = s->texture_layer;
2317 view_desc.u.texture.layer_count = 1;
2318 if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc,
2319 resource, NULL, &wined3d_null_parent_ops, &view)))
2321 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
2322 return hr;
2325 hr = blitter->color_fill(device, view, rect, color);
2326 wined3d_rendertarget_view_decref(view);
2328 return hr;
2331 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
2332 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
2333 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
2335 struct wined3d_texture *dst_texture = dst_surface->container;
2336 struct wined3d_device *device = dst_texture->resource.device;
2337 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
2338 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
2339 struct wined3d_texture *src_texture;
2341 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
2342 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
2343 flags, fx, debug_d3dtexturefiltertype(filter));
2345 /* Get the swapchain. One of the surfaces has to be a primary surface */
2346 if (dst_texture->resource.pool == WINED3D_POOL_SYSTEM_MEM)
2348 WARN("Destination is in sysmem, rejecting gl blt\n");
2349 return WINED3DERR_INVALIDCALL;
2352 dst_swapchain = dst_texture->swapchain;
2354 if (src_surface)
2356 src_texture = src_surface->container;
2357 if (src_texture->resource.pool == WINED3D_POOL_SYSTEM_MEM)
2359 WARN("Src is in sysmem, rejecting gl blt\n");
2360 return WINED3DERR_INVALIDCALL;
2363 src_swapchain = src_texture->swapchain;
2365 else
2367 src_texture = NULL;
2368 src_swapchain = NULL;
2371 /* Early sort out of cases where no render target is used */
2372 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
2374 TRACE("No surface is render target, not using hardware blit.\n");
2375 return WINED3DERR_INVALIDCALL;
2378 /* No destination color keying supported */
2379 if (flags & (WINED3D_BLT_DST_CKEY | WINED3D_BLT_DST_CKEY_OVERRIDE))
2381 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2382 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2383 return WINED3DERR_INVALIDCALL;
2386 if (dst_swapchain && dst_swapchain == src_swapchain)
2388 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
2389 return WINED3DERR_INVALIDCALL;
2392 if (dst_swapchain && src_swapchain)
2394 FIXME("Implement hardware blit between two different swapchains\n");
2395 return WINED3DERR_INVALIDCALL;
2398 if (dst_swapchain)
2400 /* Handled with regular texture -> swapchain blit */
2401 if (src_surface == rt)
2402 TRACE("Blit from active render target to a swapchain\n");
2404 else if (src_swapchain && dst_surface == rt)
2406 FIXME("Implement blit from a swapchain to the active render target\n");
2407 return WINED3DERR_INVALIDCALL;
2410 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
2412 unsigned int src_width, src_height;
2413 /* Blit from render target to texture */
2414 BOOL stretchx;
2416 /* P8 read back is not implemented */
2417 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT
2418 || dst_texture->resource.format->id == WINED3DFMT_P8_UINT)
2420 TRACE("P8 read back not supported by frame buffer to texture blit\n");
2421 return WINED3DERR_INVALIDCALL;
2424 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_SRC_CKEY_OVERRIDE))
2426 TRACE("Color keying not supported by frame buffer to texture blit\n");
2427 return WINED3DERR_INVALIDCALL;
2428 /* Destination color key is checked above */
2431 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
2432 stretchx = TRUE;
2433 else
2434 stretchx = FALSE;
2436 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2437 * flip the image nor scale it.
2439 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2440 * -> If the app wants an image width an unscaled width, copy it line per line
2441 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
2442 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2443 * back buffer. This is slower than reading line per line, thus not used for flipping
2444 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2445 * pixel by pixel. */
2446 src_width = wined3d_texture_get_level_width(src_texture, src_surface->texture_level);
2447 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
2448 if (!stretchx || dst_rect->right - dst_rect->left > src_width
2449 || dst_rect->bottom - dst_rect->top > src_height)
2451 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
2452 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
2454 else
2456 TRACE("Using hardware stretching to flip / stretch the texture.\n");
2457 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
2460 return WINED3D_OK;
2463 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
2464 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
2465 return WINED3DERR_INVALIDCALL;
2468 /* Context activation is done by the caller. */
2469 static BOOL surface_load_sysmem(struct wined3d_surface *surface,
2470 struct wined3d_context *context, DWORD dst_location)
2472 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2473 const struct wined3d_gl_info *gl_info = context->gl_info;
2474 struct wined3d_texture *texture = surface->container;
2475 struct wined3d_texture_sub_resource *sub_resource;
2477 sub_resource = &texture->sub_resources[sub_resource_idx];
2478 wined3d_texture_prepare_location(texture, sub_resource_idx, context, dst_location);
2480 if (sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
2481 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
2483 /* Download the surface to system memory. */
2484 if (sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
2486 wined3d_texture_bind_and_dirtify(texture, context,
2487 !(sub_resource->locations & WINED3D_LOCATION_TEXTURE_RGB));
2488 surface_download_data(surface, gl_info, dst_location);
2489 ++texture->download_count;
2491 return TRUE;
2494 if (!(texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2495 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
2497 read_from_framebuffer(surface, context, dst_location);
2498 return TRUE;
2501 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
2502 surface, wined3d_debug_location(sub_resource->locations));
2503 return FALSE;
2506 /* Context activation is done by the caller. */
2507 static BOOL surface_load_drawable(struct wined3d_surface *surface,
2508 struct wined3d_context *context)
2510 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2511 struct wined3d_texture *texture = surface->container;
2512 RECT r;
2514 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2516 DWORD current = texture->sub_resources[sub_resource_idx].locations;
2517 FIXME("Unimplemented copy from %s for depth/stencil buffers.\n",
2518 wined3d_debug_location(current));
2519 return FALSE;
2522 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
2523 && wined3d_resource_is_offscreen(&texture->resource))
2525 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
2526 return FALSE;
2529 surface_get_rect(surface, NULL, &r);
2530 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_TEXTURE_RGB);
2531 surface_blt_to_drawable(texture->resource.device, context,
2532 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
2534 return TRUE;
2537 static BOOL surface_load_texture(struct wined3d_surface *surface,
2538 struct wined3d_context *context, BOOL srgb)
2540 unsigned int width, height, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch;
2541 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2542 const struct wined3d_gl_info *gl_info = context->gl_info;
2543 struct wined3d_texture *texture = surface->container;
2544 struct wined3d_device *device = texture->resource.device;
2545 const struct wined3d_color_key_conversion *conversion;
2546 struct wined3d_texture_sub_resource *sub_resource;
2547 struct wined3d_bo_address data;
2548 BYTE *src_mem, *dst_mem = NULL;
2549 struct wined3d_format format;
2550 POINT dst_point = {0, 0};
2551 RECT src_rect;
2552 BOOL depth;
2554 depth = texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL;
2555 sub_resource = surface_get_sub_resource(surface);
2557 if (!depth && wined3d_settings.offscreen_rendering_mode != ORM_FBO
2558 && wined3d_resource_is_offscreen(&texture->resource)
2559 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
2561 surface_load_fb_texture(surface, srgb, context);
2563 return TRUE;
2566 width = wined3d_texture_get_level_width(texture, surface->texture_level);
2567 height = wined3d_texture_get_level_height(texture, surface->texture_level);
2568 SetRect(&src_rect, 0, 0, width, height);
2570 if (!depth && sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
2571 && (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
2572 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
2573 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format,
2574 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format))
2576 if (srgb)
2577 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
2578 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
2579 else
2580 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
2581 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
2583 return TRUE;
2586 if (!depth && sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
2587 && (!srgb || (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
2588 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
2589 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format,
2590 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format))
2592 DWORD src_location = sub_resource->locations & WINED3D_LOCATION_RB_RESOLVED ?
2593 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
2594 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2596 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, src_location,
2597 &src_rect, surface, dst_location, &src_rect);
2599 return TRUE;
2602 /* Upload from system memory */
2604 if (srgb)
2606 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | texture->resource.map_binding))
2607 == WINED3D_LOCATION_TEXTURE_RGB)
2609 FIXME_(d3d_perf)("Downloading RGB surface %p to reload it as sRGB.\n", surface);
2610 wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding);
2613 else
2615 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | texture->resource.map_binding))
2616 == WINED3D_LOCATION_TEXTURE_SRGB)
2618 FIXME_(d3d_perf)("Downloading sRGB surface %p to reload it as RGB.\n", surface);
2619 wined3d_texture_load_location(texture, sub_resource_idx, context, texture->resource.map_binding);
2623 if (!(sub_resource->locations & surface_simple_locations))
2625 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
2626 /* Lets hope we get it from somewhere... */
2627 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM);
2630 wined3d_texture_prepare_texture(texture, context, srgb);
2631 wined3d_texture_bind_and_dirtify(texture, context, srgb);
2632 wined3d_texture_get_pitch(texture, surface->texture_level, &src_row_pitch, &src_slice_pitch);
2634 format = *texture->resource.format;
2635 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
2636 format = *wined3d_get_format(gl_info, conversion->dst_format, texture->resource.usage);
2638 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
2639 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
2640 * getting called. */
2641 if ((format.convert || conversion) && texture->sub_resources[sub_resource_idx].buffer_object)
2643 TRACE("Removing the pbo attached to surface %p.\n", surface);
2645 wined3d_texture_load_location(texture, sub_resource_idx, context, WINED3D_LOCATION_SYSMEM);
2646 wined3d_texture_set_map_binding(texture, WINED3D_LOCATION_SYSMEM);
2649 wined3d_texture_get_memory(texture, sub_resource_idx, &data, sub_resource->locations);
2650 if (format.convert)
2652 /* This code is entered for texture formats which need a fixup. */
2653 format.byte_count = format.conv_byte_count;
2654 wined3d_format_calculate_pitch(&format, 1, width, height, &dst_row_pitch, &dst_slice_pitch);
2656 src_mem = wined3d_texture_map_bo_address(&data, src_slice_pitch,
2657 gl_info, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READONLY);
2658 if (!(dst_mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
2660 ERR("Out of memory (%u).\n", dst_slice_pitch);
2661 context_release(context);
2662 return FALSE;
2664 format.convert(src_mem, dst_mem, src_row_pitch, src_slice_pitch,
2665 dst_row_pitch, dst_slice_pitch, width, height, 1);
2666 src_row_pitch = dst_row_pitch;
2667 wined3d_texture_unmap_bo_address(&data, gl_info, GL_PIXEL_UNPACK_BUFFER);
2669 data.buffer_object = 0;
2670 data.addr = dst_mem;
2672 else if (conversion)
2674 /* This code is only entered for color keying fixups */
2675 struct wined3d_palette *palette = NULL;
2677 wined3d_format_calculate_pitch(&format, device->surface_alignment,
2678 width, height, &dst_row_pitch, &dst_slice_pitch);
2680 src_mem = wined3d_texture_map_bo_address(&data, src_slice_pitch,
2681 gl_info, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READONLY);
2682 if (!(dst_mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
2684 ERR("Out of memory (%u).\n", dst_slice_pitch);
2685 context_release(context);
2686 return FALSE;
2688 if (texture->swapchain && texture->swapchain->palette)
2689 palette = texture->swapchain->palette;
2690 conversion->convert(src_mem, src_row_pitch, dst_mem, dst_row_pitch,
2691 width, height, palette, &texture->async.gl_color_key);
2692 src_row_pitch = dst_row_pitch;
2693 wined3d_texture_unmap_bo_address(&data, gl_info, GL_PIXEL_UNPACK_BUFFER);
2695 data.buffer_object = 0;
2696 data.addr = dst_mem;
2699 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
2700 src_row_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
2702 HeapFree(GetProcessHeap(), 0, dst_mem);
2704 return TRUE;
2707 /* Context activation is done by the caller. */
2708 static BOOL surface_load_renderbuffer(struct wined3d_surface *surface, struct wined3d_context *context,
2709 DWORD dst_location)
2711 struct wined3d_texture *texture = surface->container;
2712 const RECT rect = {0, 0,
2713 wined3d_texture_get_level_width(texture, surface->texture_level),
2714 wined3d_texture_get_level_height(texture, surface->texture_level)};
2715 DWORD locations = surface_get_sub_resource(surface)->locations;
2716 DWORD src_location;
2718 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
2720 FIXME("Unimplemented copy from %s for depth/stencil buffers.\n",
2721 wined3d_debug_location(locations));
2722 return FALSE;
2725 if (locations & WINED3D_LOCATION_RB_MULTISAMPLE)
2726 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
2727 else if (locations & WINED3D_LOCATION_RB_RESOLVED)
2728 src_location = WINED3D_LOCATION_RB_RESOLVED;
2729 else if (locations & WINED3D_LOCATION_TEXTURE_SRGB)
2730 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
2731 else /* surface_blt_fbo will load the source location if necessary. */
2732 src_location = WINED3D_LOCATION_TEXTURE_RGB;
2734 surface_blt_fbo(texture->resource.device, context, WINED3D_TEXF_POINT,
2735 surface, src_location, &rect, surface, dst_location, &rect);
2737 return TRUE;
2740 /* Context activation is done by the caller. Context may be NULL in ddraw-only mode. */
2741 BOOL surface_load_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
2743 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
2745 switch (location)
2747 case WINED3D_LOCATION_USER_MEMORY:
2748 case WINED3D_LOCATION_SYSMEM:
2749 case WINED3D_LOCATION_BUFFER:
2750 return surface_load_sysmem(surface, context, location);
2752 case WINED3D_LOCATION_DRAWABLE:
2753 return surface_load_drawable(surface, context);
2755 case WINED3D_LOCATION_RB_RESOLVED:
2756 case WINED3D_LOCATION_RB_MULTISAMPLE:
2757 return surface_load_renderbuffer(surface, context, location);
2759 case WINED3D_LOCATION_TEXTURE_RGB:
2760 case WINED3D_LOCATION_TEXTURE_SRGB:
2761 return surface_load_texture(surface, context,
2762 location == WINED3D_LOCATION_TEXTURE_SRGB);
2764 default:
2765 ERR("Don't know how to handle location %#x.\n", location);
2766 return FALSE;
2770 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
2771 /* Context activation is done by the caller. */
2772 static void ffp_blit_free(struct wined3d_device *device) { }
2774 /* Context activation is done by the caller. */
2775 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
2776 const struct wined3d_color_key *color_key)
2778 const struct wined3d_gl_info *gl_info = context->gl_info;
2780 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
2781 checkGLcall("glEnable(target)");
2783 return WINED3D_OK;
2786 /* Context activation is done by the caller. */
2787 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
2789 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
2790 checkGLcall("glDisable(GL_TEXTURE_2D)");
2791 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
2793 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
2794 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
2796 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
2798 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
2799 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
2803 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info,
2804 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
2805 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
2806 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
2808 BOOL decompress;
2810 decompress = src_format && (src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
2811 && !(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED);
2812 if (!decompress && (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM))
2814 TRACE("Source or destination is in system memory.\n");
2815 return FALSE;
2818 switch (blit_op)
2820 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
2821 if (d3d_info->shader_color_key)
2823 TRACE("Color keying requires converted textures.\n");
2824 return FALSE;
2826 case WINED3D_BLIT_OP_COLOR_BLIT:
2827 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
2828 if (TRACE_ON(d3d))
2830 TRACE("Checking support for fixup:\n");
2831 dump_color_fixup_desc(src_format->color_fixup);
2834 /* We only support identity conversions. */
2835 if (!is_identity_fixup(src_format->color_fixup)
2836 || !is_identity_fixup(dst_format->color_fixup))
2838 TRACE("Fixups are not supported.\n");
2839 return FALSE;
2842 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
2844 TRACE("Can only blit to render targets.\n");
2845 return FALSE;
2847 return TRUE;
2849 case WINED3D_BLIT_OP_COLOR_FILL:
2850 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
2852 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
2853 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
2854 return FALSE;
2856 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
2858 TRACE("Color fill not supported\n");
2859 return FALSE;
2862 /* FIXME: We should reject color fills on formats with fixups,
2863 * but this would break P8 color fills for example. */
2865 return TRUE;
2867 case WINED3D_BLIT_OP_DEPTH_FILL:
2868 return TRUE;
2870 default:
2871 TRACE("Unsupported blit_op=%d\n", blit_op);
2872 return FALSE;
2876 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
2877 const RECT *rect, const struct wined3d_color *color)
2879 const RECT draw_rect = {0, 0, view->width, view->height};
2880 struct wined3d_fb_state fb = {&view, NULL};
2882 device_clear_render_targets(device, 1, &fb, 1, rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
2884 return WINED3D_OK;
2887 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
2888 struct wined3d_rendertarget_view *view, const RECT *rect, DWORD clear_flags,
2889 float depth, DWORD stencil)
2891 const RECT draw_rect = {0, 0, view->width, view->height};
2892 struct wined3d_fb_state fb = {NULL, view};
2894 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, clear_flags, NULL, depth, stencil);
2896 return WINED3D_OK;
2899 static void ffp_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
2900 struct wined3d_surface *src_surface, const RECT *src_rect,
2901 struct wined3d_surface *dst_surface, const RECT *dst_rect,
2902 const struct wined3d_color_key *color_key)
2904 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
2905 struct wined3d_texture *dst_texture = dst_surface->container;
2906 struct wined3d_texture *src_texture = src_surface->container;
2907 const struct wined3d_gl_info *gl_info;
2908 struct wined3d_context *context;
2910 /* Blit from offscreen surface to render target */
2911 struct wined3d_color_key old_blt_key = src_texture->async.src_blt_color_key;
2912 DWORD old_color_key_flags = src_texture->async.color_key_flags;
2914 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
2916 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT, color_key);
2918 context = context_acquire(device, dst_texture, dst_sub_resource_idx);
2919 gl_info = context->gl_info;
2921 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
2922 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
2924 surface_blt_to_drawable(device, context, filter,
2925 !!color_key, src_surface, src_rect, dst_surface, dst_rect);
2927 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
2928 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2930 context_release(context);
2932 /* Restore the color key parameters */
2933 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT,
2934 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
2936 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, dst_texture->resource.draw_binding);
2937 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~dst_texture->resource.draw_binding);
2940 const struct blit_shader ffp_blit = {
2941 ffp_blit_alloc,
2942 ffp_blit_free,
2943 ffp_blit_set,
2944 ffp_blit_unset,
2945 ffp_blit_supported,
2946 ffp_blit_color_fill,
2947 ffp_blit_depth_fill,
2948 ffp_blit_blit_surface,
2951 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
2953 return WINED3D_OK;
2956 /* Context activation is done by the caller. */
2957 static void cpu_blit_free(struct wined3d_device *device)
2961 /* Context activation is done by the caller. */
2962 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
2963 const struct wined3d_color_key *color_key)
2965 return WINED3D_OK;
2968 /* Context activation is done by the caller. */
2969 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
2973 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info,
2974 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
2975 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
2976 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
2978 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL || blit_op == WINED3D_BLIT_OP_DEPTH_FILL)
2979 return TRUE;
2981 return FALSE;
2984 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
2985 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
2986 const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx)
2988 UINT row_block_count;
2989 const BYTE *src_row;
2990 BYTE *dst_row;
2991 UINT x, y;
2993 src_row = src_data;
2994 dst_row = dst_data;
2996 row_block_count = (update_w + format->block_width - 1) / format->block_width;
2998 if (!flags)
3000 for (y = 0; y < update_h; y += format->block_height)
3002 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
3003 src_row += src_pitch;
3004 dst_row += dst_pitch;
3007 return WINED3D_OK;
3010 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
3012 src_row += (((update_h / format->block_height) - 1) * src_pitch);
3014 switch (format->id)
3016 case WINED3DFMT_DXT1:
3017 for (y = 0; y < update_h; y += format->block_height)
3019 struct block
3021 WORD color[2];
3022 BYTE control_row[4];
3025 const struct block *s = (const struct block *)src_row;
3026 struct block *d = (struct block *)dst_row;
3028 for (x = 0; x < row_block_count; ++x)
3030 d[x].color[0] = s[x].color[0];
3031 d[x].color[1] = s[x].color[1];
3032 d[x].control_row[0] = s[x].control_row[3];
3033 d[x].control_row[1] = s[x].control_row[2];
3034 d[x].control_row[2] = s[x].control_row[1];
3035 d[x].control_row[3] = s[x].control_row[0];
3037 src_row -= src_pitch;
3038 dst_row += dst_pitch;
3040 return WINED3D_OK;
3042 case WINED3DFMT_DXT2:
3043 case WINED3DFMT_DXT3:
3044 for (y = 0; y < update_h; y += format->block_height)
3046 struct block
3048 WORD alpha_row[4];
3049 WORD color[2];
3050 BYTE control_row[4];
3053 const struct block *s = (const struct block *)src_row;
3054 struct block *d = (struct block *)dst_row;
3056 for (x = 0; x < row_block_count; ++x)
3058 d[x].alpha_row[0] = s[x].alpha_row[3];
3059 d[x].alpha_row[1] = s[x].alpha_row[2];
3060 d[x].alpha_row[2] = s[x].alpha_row[1];
3061 d[x].alpha_row[3] = s[x].alpha_row[0];
3062 d[x].color[0] = s[x].color[0];
3063 d[x].color[1] = s[x].color[1];
3064 d[x].control_row[0] = s[x].control_row[3];
3065 d[x].control_row[1] = s[x].control_row[2];
3066 d[x].control_row[2] = s[x].control_row[1];
3067 d[x].control_row[3] = s[x].control_row[0];
3069 src_row -= src_pitch;
3070 dst_row += dst_pitch;
3072 return WINED3D_OK;
3074 default:
3075 FIXME("Compressed flip not implemented for format %s.\n",
3076 debug_d3dformat(format->id));
3077 return E_NOTIMPL;
3081 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
3082 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
3084 return E_NOTIMPL;
3087 static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
3088 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
3089 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
3090 enum wined3d_texture_filter_type filter)
3092 unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count;
3093 const struct wined3d_format *src_format, *dst_format;
3094 struct wined3d_texture *converted_texture = NULL;
3095 unsigned int src_fmt_flags, dst_fmt_flags;
3096 struct wined3d_map_desc dst_map, src_map;
3097 const BYTE *sbase = NULL;
3098 HRESULT hr = WINED3D_OK;
3099 BOOL same_sub_resource;
3100 const BYTE *sbuf;
3101 BYTE *dbuf;
3102 int x, y;
3104 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
3105 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
3106 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture,
3107 src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
3109 if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx)
3111 same_sub_resource = TRUE;
3112 wined3d_resource_map(&dst_texture->resource, dst_sub_resource_idx, &dst_map, NULL, 0);
3113 src_map = dst_map;
3114 src_format = dst_texture->resource.format;
3115 dst_format = src_format;
3116 dst_fmt_flags = dst_texture->resource.format_flags;
3117 src_fmt_flags = dst_fmt_flags;
3119 else
3121 same_sub_resource = FALSE;
3122 dst_format = dst_texture->resource.format;
3123 dst_fmt_flags = dst_texture->resource.format_flags;
3124 if (src_texture)
3126 if (dst_texture->resource.format->id != src_texture->resource.format->id)
3128 if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format)))
3130 FIXME("Cannot convert %s to %s.\n", debug_d3dformat(src_texture->resource.format->id),
3131 debug_d3dformat(dst_texture->resource.format->id));
3132 return WINED3DERR_NOTAVAILABLE;
3134 src_texture = converted_texture;
3135 src_sub_resource_idx = 0;
3137 wined3d_resource_map(&src_texture->resource, src_sub_resource_idx, &src_map, NULL, WINED3D_MAP_READONLY);
3138 src_format = src_texture->resource.format;
3139 src_fmt_flags = src_texture->resource.format_flags;
3141 else
3143 src_format = dst_format;
3144 src_fmt_flags = dst_fmt_flags;
3147 wined3d_resource_map(&dst_texture->resource, dst_sub_resource_idx, &dst_map, dst_box, 0);
3150 bpp = dst_format->byte_count;
3151 src_height = src_box->bottom - src_box->top;
3152 src_width = src_box->right - src_box->left;
3153 dst_height = dst_box->bottom - dst_box->top;
3154 dst_width = dst_box->right - dst_box->left;
3155 row_byte_count = dst_width * bpp;
3157 if (src_texture)
3158 sbase = (BYTE *)src_map.data
3159 + ((src_box->top / src_format->block_height) * src_map.row_pitch)
3160 + ((src_box->left / src_format->block_width) * src_format->block_byte_count);
3161 if (same_sub_resource)
3162 dbuf = (BYTE *)dst_map.data
3163 + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch)
3164 + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count);
3165 else
3166 dbuf = dst_map.data;
3168 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
3170 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
3172 if (same_sub_resource)
3174 FIXME("Only plain blits supported on compressed surfaces.\n");
3175 hr = E_NOTIMPL;
3176 goto release;
3179 if (src_height != dst_height || src_width != dst_width)
3181 WARN("Stretching not supported on compressed surfaces.\n");
3182 hr = WINED3DERR_INVALIDCALL;
3183 goto release;
3186 if (!wined3d_texture_check_block_align(src_texture,
3187 src_sub_resource_idx % src_texture->level_count, src_box))
3189 WARN("Source rectangle not block-aligned.\n");
3190 hr = WINED3DERR_INVALIDCALL;
3191 goto release;
3194 if (!wined3d_texture_check_block_align(dst_texture,
3195 dst_sub_resource_idx % dst_texture->level_count, dst_box))
3197 WARN("Destination rectangle not block-aligned.\n");
3198 hr = WINED3DERR_INVALIDCALL;
3199 goto release;
3202 hr = surface_cpu_blt_compressed(sbase, dbuf,
3203 src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height,
3204 src_format, flags, fx);
3205 goto release;
3208 /* First, all the 'source-less' blits */
3209 if (flags & (WINED3D_BLT_COLOR_FILL | WINED3D_BLT_DEPTH_FILL))
3211 hr = _Blt_ColorFill(dbuf, dst_width, dst_height, bpp, dst_map.row_pitch, fx->fill_color);
3212 flags &= ~(WINED3D_BLT_COLOR_FILL | WINED3D_BLT_DEPTH_FILL);
3215 /* Now the 'with source' blits. */
3216 if (src_texture)
3218 int sx, xinc, sy, yinc;
3220 if (!dst_width || !dst_height) /* Hmm... stupid program? */
3221 goto release;
3223 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
3224 && (src_width != dst_width || src_height != dst_height))
3226 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
3227 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
3230 xinc = (src_width << 16) / dst_width;
3231 yinc = (src_height << 16) / dst_height;
3233 if (!flags)
3235 /* No effects, we can cheat here. */
3236 if (dst_width == src_width)
3238 if (dst_height == src_height)
3240 /* No stretching in either direction. This needs to be as
3241 * fast as possible. */
3242 sbuf = sbase;
3244 /* Check for overlapping surfaces. */
3245 if (!same_sub_resource || dst_box->top < src_box->top
3246 || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
3248 /* No overlap, or dst above src, so copy from top downwards. */
3249 for (y = 0; y < dst_height; ++y)
3251 memcpy(dbuf, sbuf, row_byte_count);
3252 sbuf += src_map.row_pitch;
3253 dbuf += dst_map.row_pitch;
3256 else if (dst_box->top > src_box->top)
3258 /* Copy from bottom upwards. */
3259 sbuf += src_map.row_pitch * dst_height;
3260 dbuf += dst_map.row_pitch * dst_height;
3261 for (y = 0; y < dst_height; ++y)
3263 sbuf -= src_map.row_pitch;
3264 dbuf -= dst_map.row_pitch;
3265 memcpy(dbuf, sbuf, row_byte_count);
3268 else
3270 /* Src and dst overlapping on the same line, use memmove. */
3271 for (y = 0; y < dst_height; ++y)
3273 memmove(dbuf, sbuf, row_byte_count);
3274 sbuf += src_map.row_pitch;
3275 dbuf += dst_map.row_pitch;
3279 else
3281 /* Stretching in y direction only. */
3282 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3284 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3285 memcpy(dbuf, sbuf, row_byte_count);
3286 dbuf += dst_map.row_pitch;
3290 else
3292 /* Stretching in X direction. */
3293 int last_sy = -1;
3294 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3296 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3298 if ((sy >> 16) == (last_sy >> 16))
3300 /* This source row is the same as last source row -
3301 * Copy the already stretched row. */
3302 memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count);
3304 else
3306 #define STRETCH_ROW(type) \
3307 do { \
3308 const type *s = (const type *)sbuf; \
3309 type *d = (type *)dbuf; \
3310 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3311 d[x] = s[sx >> 16]; \
3312 } while(0)
3314 switch(bpp)
3316 case 1:
3317 STRETCH_ROW(BYTE);
3318 break;
3319 case 2:
3320 STRETCH_ROW(WORD);
3321 break;
3322 case 4:
3323 STRETCH_ROW(DWORD);
3324 break;
3325 case 3:
3327 const BYTE *s;
3328 BYTE *d = dbuf;
3329 for (x = sx = 0; x < dst_width; x++, sx+= xinc)
3331 DWORD pixel;
3333 s = sbuf + 3 * (sx >> 16);
3334 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3335 d[0] = (pixel ) & 0xff;
3336 d[1] = (pixel >> 8) & 0xff;
3337 d[2] = (pixel >> 16) & 0xff;
3338 d += 3;
3340 break;
3342 default:
3343 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
3344 hr = WINED3DERR_NOTAVAILABLE;
3345 goto error;
3347 #undef STRETCH_ROW
3349 dbuf += dst_map.row_pitch;
3350 last_sy = sy;
3354 else
3356 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
3357 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
3358 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
3359 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3360 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
3362 /* The color keying flags are checked for correctness in ddraw */
3363 if (flags & WINED3D_BLT_SRC_CKEY)
3365 keylow = src_texture->async.src_blt_color_key.color_space_low_value;
3366 keyhigh = src_texture->async.src_blt_color_key.color_space_high_value;
3368 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3370 keylow = fx->src_color_key.color_space_low_value;
3371 keyhigh = fx->src_color_key.color_space_high_value;
3374 if (flags & WINED3D_BLT_DST_CKEY)
3376 /* Destination color keys are taken from the source surface! */
3377 destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value;
3378 destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value;
3380 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
3382 destkeylow = fx->dst_color_key.color_space_low_value;
3383 destkeyhigh = fx->dst_color_key.color_space_high_value;
3386 if (bpp == 1)
3388 keymask = 0xff;
3390 else
3392 DWORD masks[3];
3393 get_color_masks(src_format, masks);
3394 keymask = masks[0]
3395 | masks[1]
3396 | masks[2];
3398 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3399 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
3402 if (flags & WINED3D_BLT_FX)
3404 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
3405 LONG tmpxy;
3406 dTopLeft = dbuf;
3407 dTopRight = dbuf + ((dst_width - 1) * bpp);
3408 dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
3409 dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
3411 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
3413 /* I don't think we need to do anything about this flag */
3414 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
3416 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
3418 tmp = dTopRight;
3419 dTopRight = dTopLeft;
3420 dTopLeft = tmp;
3421 tmp = dBottomRight;
3422 dBottomRight = dBottomLeft;
3423 dBottomLeft = tmp;
3424 dstxinc = dstxinc * -1;
3426 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
3428 tmp = dTopLeft;
3429 dTopLeft = dBottomLeft;
3430 dBottomLeft = tmp;
3431 tmp = dTopRight;
3432 dTopRight = dBottomRight;
3433 dBottomRight = tmp;
3434 dstyinc = dstyinc * -1;
3436 if (fx->fx & WINEDDBLTFX_NOTEARING)
3438 /* I don't think we need to do anything about this flag */
3439 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
3441 if (fx->fx & WINEDDBLTFX_ROTATE180)
3443 tmp = dBottomRight;
3444 dBottomRight = dTopLeft;
3445 dTopLeft = tmp;
3446 tmp = dBottomLeft;
3447 dBottomLeft = dTopRight;
3448 dTopRight = tmp;
3449 dstxinc = dstxinc * -1;
3450 dstyinc = dstyinc * -1;
3452 if (fx->fx & WINEDDBLTFX_ROTATE270)
3454 tmp = dTopLeft;
3455 dTopLeft = dBottomLeft;
3456 dBottomLeft = dBottomRight;
3457 dBottomRight = dTopRight;
3458 dTopRight = tmp;
3459 tmpxy = dstxinc;
3460 dstxinc = dstyinc;
3461 dstyinc = tmpxy;
3462 dstxinc = dstxinc * -1;
3464 if (fx->fx & WINEDDBLTFX_ROTATE90)
3466 tmp = dTopLeft;
3467 dTopLeft = dTopRight;
3468 dTopRight = dBottomRight;
3469 dBottomRight = dBottomLeft;
3470 dBottomLeft = tmp;
3471 tmpxy = dstxinc;
3472 dstxinc = dstyinc;
3473 dstyinc = tmpxy;
3474 dstyinc = dstyinc * -1;
3476 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
3478 /* I don't think we need to do anything about this flag */
3479 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
3481 dbuf = dTopLeft;
3482 flags &= ~(WINED3D_BLT_FX);
3485 #define COPY_COLORKEY_FX(type) \
3486 do { \
3487 const type *s; \
3488 type *d = (type *)dbuf, *dx, tmp; \
3489 for (y = sy = 0; y < dst_height; ++y, sy += yinc) \
3491 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
3492 dx = d; \
3493 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3495 tmp = s[sx >> 16]; \
3496 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
3497 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
3499 dx[0] = tmp; \
3501 dx = (type *)(((BYTE *)dx) + dstxinc); \
3503 d = (type *)(((BYTE *)d) + dstyinc); \
3505 } while(0)
3507 switch (bpp)
3509 case 1:
3510 COPY_COLORKEY_FX(BYTE);
3511 break;
3512 case 2:
3513 COPY_COLORKEY_FX(WORD);
3514 break;
3515 case 4:
3516 COPY_COLORKEY_FX(DWORD);
3517 break;
3518 case 3:
3520 const BYTE *s;
3521 BYTE *d = dbuf, *dx;
3522 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3524 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3525 dx = d;
3526 for (x = sx = 0; x < dst_width; ++x, sx+= xinc)
3528 DWORD pixel, dpixel = 0;
3529 s = sbuf + 3 * (sx>>16);
3530 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3531 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
3532 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
3533 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
3535 dx[0] = (pixel ) & 0xff;
3536 dx[1] = (pixel >> 8) & 0xff;
3537 dx[2] = (pixel >> 16) & 0xff;
3539 dx += dstxinc;
3541 d += dstyinc;
3543 break;
3545 default:
3546 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
3547 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
3548 hr = WINED3DERR_NOTAVAILABLE;
3549 goto error;
3550 #undef COPY_COLORKEY_FX
3555 error:
3556 if (flags)
3557 FIXME(" Unsupported flags %#x.\n", flags);
3559 release:
3560 wined3d_resource_unmap(&dst_texture->resource, dst_sub_resource_idx);
3561 if (src_texture && !same_sub_resource)
3562 wined3d_resource_unmap(&src_texture->resource, src_sub_resource_idx);
3563 if (converted_texture)
3564 wined3d_texture_decref(converted_texture);
3566 return hr;
3569 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
3570 const RECT *rect, const struct wined3d_color *color)
3572 const struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
3573 static const struct wined3d_box src_box;
3574 struct wined3d_blt_fx fx;
3576 fx.fill_color = wined3d_format_convert_from_float(view->format, color);
3577 return surface_cpu_blt(texture_from_resource(view->resource), view->sub_resource_idx,
3578 &box, NULL, 0, &src_box, WINED3D_BLT_COLOR_FILL, &fx, WINED3D_TEXF_POINT);
3581 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
3582 struct wined3d_rendertarget_view *view, const RECT *rect, DWORD clear_flags,
3583 float depth, DWORD stencil)
3585 const struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
3586 struct wined3d_color color = {depth, 0.0f, 0.0f, 0.0f};
3587 static const struct wined3d_box src_box;
3588 struct wined3d_blt_fx fx;
3590 if (clear_flags != WINED3DCLEAR_ZBUFFER)
3592 FIXME("clear_flags %#x not implemented.\n", clear_flags);
3593 return WINED3DERR_INVALIDCALL;
3596 fx.fill_color = wined3d_format_convert_from_float(view->format, &color);
3597 return surface_cpu_blt(texture_from_resource(view->resource), view->sub_resource_idx,
3598 &box, NULL, 0, &src_box, WINED3D_BLT_DEPTH_FILL, &fx, WINED3D_TEXF_POINT);
3601 static void cpu_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
3602 struct wined3d_surface *src_surface, const RECT *src_rect,
3603 struct wined3d_surface *dst_surface, const RECT *dst_rect,
3604 const struct wined3d_color_key *color_key)
3606 /* FIXME: Remove error returns from surface_blt_cpu. */
3607 ERR("Blit method not implemented by cpu_blit.\n");
3610 const struct blit_shader cpu_blit = {
3611 cpu_blit_alloc,
3612 cpu_blit_free,
3613 cpu_blit_set,
3614 cpu_blit_unset,
3615 cpu_blit_supported,
3616 cpu_blit_color_fill,
3617 cpu_blit_depth_fill,
3618 cpu_blit_blit_surface,
3621 HRESULT wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3622 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
3623 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
3625 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
3626 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
3627 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
3628 struct wined3d_texture *dst_texture = dst_surface->container;
3629 struct wined3d_device *device = dst_texture->resource.device;
3630 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3631 struct wined3d_texture *src_texture = NULL;
3632 unsigned int dst_w, dst_h, src_w, src_h;
3633 unsigned int src_sub_resource_idx = 0;
3634 DWORD src_ds_flags, dst_ds_flags;
3635 BOOL scale, convert;
3637 static const DWORD simple_blit = WINED3D_BLT_ASYNC
3638 | WINED3D_BLT_COLOR_FILL
3639 | WINED3D_BLT_SRC_CKEY
3640 | WINED3D_BLT_SRC_CKEY_OVERRIDE
3641 | WINED3D_BLT_WAIT
3642 | WINED3D_BLT_DEPTH_FILL
3643 | WINED3D_BLT_DO_NOT_WAIT
3644 | WINED3D_BLT_ALPHA_TEST;
3646 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
3647 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3648 flags, fx, debug_d3dtexturefiltertype(filter));
3649 TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage));
3651 if (fx)
3653 TRACE("fx %#x.\n", fx->fx);
3654 TRACE("fill_color 0x%08x.\n", fx->fill_color);
3655 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
3656 fx->dst_color_key.color_space_low_value,
3657 fx->dst_color_key.color_space_high_value);
3658 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
3659 fx->src_color_key.color_space_low_value,
3660 fx->src_color_key.color_space_high_value);
3663 if (src_surface)
3665 src_texture = src_surface->container;
3666 src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
3669 if (dst_texture->sub_resources[dst_sub_resource_idx].map_count
3670 || (src_texture && src_texture->sub_resources[src_sub_resource_idx].map_count))
3672 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
3673 return WINEDDERR_SURFACEBUSY;
3676 dst_w = wined3d_texture_get_level_width(dst_texture, dst_surface->texture_level);
3677 dst_h = wined3d_texture_get_level_height(dst_texture, dst_surface->texture_level);
3678 if (IsRectEmpty(dst_rect) || dst_rect->left > dst_w || dst_rect->left < 0
3679 || dst_rect->top > dst_h || dst_rect->top < 0
3680 || dst_rect->right > dst_w || dst_rect->right < 0
3681 || dst_rect->bottom > dst_h || dst_rect->bottom < 0)
3683 WARN("The application gave us a bad destination rectangle.\n");
3684 return WINEDDERR_INVALIDRECT;
3687 if (src_texture)
3689 src_w = wined3d_texture_get_level_width(src_texture, src_surface->texture_level);
3690 src_h = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
3691 if (IsRectEmpty(src_rect) || src_rect->left > src_w || src_rect->left < 0
3692 || src_rect->top > src_h || src_rect->top < 0
3693 || src_rect->right > src_w || src_rect->right < 0
3694 || src_rect->bottom > src_h || src_rect->bottom < 0)
3696 WARN("The application gave us a bad source rectangle.\n");
3697 return WINEDDERR_INVALIDRECT;
3701 if (!fx || !(fx->fx))
3702 flags &= ~WINED3D_BLT_FX;
3704 if (flags & WINED3D_BLT_WAIT)
3705 flags &= ~WINED3D_BLT_WAIT;
3707 if (flags & WINED3D_BLT_ASYNC)
3709 static unsigned int once;
3711 if (!once++)
3712 FIXME("Can't handle WINED3D_BLT_ASYNC flag.\n");
3713 flags &= ~WINED3D_BLT_ASYNC;
3716 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
3717 if (flags & WINED3D_BLT_DO_NOT_WAIT)
3719 static unsigned int once;
3721 if (!once++)
3722 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
3723 flags &= ~WINED3D_BLT_DO_NOT_WAIT;
3726 if (!device->d3d_initialized)
3728 WARN("D3D not initialized, using fallback.\n");
3729 goto cpu;
3732 /* We want to avoid invalidating the sysmem location for converted
3733 * surfaces, since otherwise we'd have to convert the data back when
3734 * locking them. */
3735 if (dst_texture->flags & WINED3D_TEXTURE_CONVERTED || dst_texture->resource.format->convert
3736 || wined3d_format_get_color_key_conversion(dst_texture, TRUE))
3738 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
3739 goto cpu;
3742 if (flags & ~simple_blit)
3744 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
3745 goto fallback;
3748 if (src_texture)
3749 src_swapchain = src_texture->swapchain;
3750 else
3751 src_swapchain = NULL;
3753 dst_swapchain = dst_texture->swapchain;
3755 /* This isn't strictly needed. FBO blits for example could deal with
3756 * cross-swapchain blits by first downloading the source to a texture
3757 * before switching to the destination context. We just have this here to
3758 * not have to deal with the issue, since cross-swapchain blits should be
3759 * rare. */
3760 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
3762 FIXME("Using fallback for cross-swapchain blit.\n");
3763 goto fallback;
3766 scale = src_texture
3767 && (src_rect->right - src_rect->left != dst_rect->right - dst_rect->left
3768 || src_rect->bottom - src_rect->top != dst_rect->bottom - dst_rect->top);
3769 convert = src_texture && src_texture->resource.format->id != dst_texture->resource.format->id;
3771 dst_ds_flags = dst_texture->resource.format_flags
3772 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
3773 if (src_texture)
3774 src_ds_flags = src_texture->resource.format_flags
3775 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
3776 else
3777 src_ds_flags = 0;
3779 if (src_ds_flags || dst_ds_flags)
3781 if (flags & WINED3D_BLT_DEPTH_FILL)
3783 struct wined3d_color color;
3785 TRACE("Depth fill.\n");
3787 if (!wined3d_format_convert_color_to_float(dst_texture->resource.format, NULL, fx->fill_color, &color))
3788 return WINED3DERR_INVALIDCALL;
3790 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, dst_rect, color.r)))
3791 return WINED3D_OK;
3793 else
3795 if (src_ds_flags != dst_ds_flags)
3797 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
3798 return WINED3DERR_INVALIDCALL;
3801 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_texture->resource.draw_binding,
3802 src_rect, dst_surface, dst_texture->resource.draw_binding, dst_rect)))
3803 return WINED3D_OK;
3806 else
3808 struct wined3d_texture_sub_resource *src_sub_resource, *dst_sub_resource;
3809 const struct blit_shader *blitter;
3811 dst_sub_resource = surface_get_sub_resource(dst_surface);
3812 src_sub_resource = src_texture ? &src_texture->sub_resources[src_sub_resource_idx] : NULL;
3814 /* In principle this would apply to depth blits as well, but we don't
3815 * implement those in the CPU blitter at the moment. */
3816 if ((dst_sub_resource->locations & dst_texture->resource.map_binding)
3817 && (!src_texture || (src_sub_resource->locations & src_texture->resource.map_binding)))
3819 if (scale)
3820 TRACE("Not doing sysmem blit because of scaling.\n");
3821 else if (convert)
3822 TRACE("Not doing sysmem blit because of format conversion.\n");
3823 else
3824 goto cpu;
3827 if (flags & WINED3D_BLT_COLOR_FILL)
3829 struct wined3d_color color;
3830 const struct wined3d_palette *palette = dst_swapchain ? dst_swapchain->palette : NULL;
3832 TRACE("Color fill.\n");
3834 if (!wined3d_format_convert_color_to_float(dst_texture->resource.format,
3835 palette, fx->fill_color, &color))
3836 goto fallback;
3838 if (SUCCEEDED(surface_color_fill(dst_surface, dst_rect, &color)))
3839 return WINED3D_OK;
3841 else
3843 enum wined3d_blit_op blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
3844 const struct wined3d_color_key *color_key = NULL;
3846 TRACE("Color blit.\n");
3847 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3849 color_key = &fx->src_color_key;
3850 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
3852 else if (flags & WINED3D_BLT_SRC_CKEY)
3854 color_key = &src_texture->async.src_blt_color_key;
3855 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
3857 else if (flags & WINED3D_BLT_ALPHA_TEST)
3859 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
3861 else if ((src_sub_resource->locations & WINED3D_LOCATION_SYSMEM)
3862 && !(dst_sub_resource->locations & WINED3D_LOCATION_SYSMEM))
3864 /* Upload */
3865 if (scale)
3866 TRACE("Not doing upload because of scaling.\n");
3867 else if (convert)
3868 TRACE("Not doing upload because of format conversion.\n");
3869 else
3871 POINT dst_point = {dst_rect->left, dst_rect->top};
3873 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, src_rect)))
3875 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
3877 struct wined3d_context *context = context_acquire(device,
3878 dst_texture, dst_sub_resource_idx);
3879 wined3d_texture_load_location(dst_texture, dst_sub_resource_idx,
3880 context, dst_texture->resource.draw_binding);
3881 context_release(context);
3883 return WINED3D_OK;
3887 else if (dst_swapchain && dst_swapchain->back_buffers
3888 && dst_texture == dst_swapchain->front_buffer
3889 && src_texture == dst_swapchain->back_buffers[0])
3891 /* Use present for back -> front blits. The idea behind this is
3892 * that present is potentially faster than a blit, in particular
3893 * when FBO blits aren't available. Some ddraw applications like
3894 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
3895 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
3896 * applications can't blit directly to the frontbuffer. */
3897 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
3899 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
3901 /* Set the swap effect to COPY, we don't want the backbuffer
3902 * to become undefined. */
3903 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
3904 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, 0);
3905 dst_swapchain->desc.swap_effect = swap_effect;
3907 return WINED3D_OK;
3910 if (fbo_blit_supported(&device->adapter->gl_info, blit_op,
3911 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
3912 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format))
3914 struct wined3d_context *context;
3915 TRACE("Using FBO blit.\n");
3917 context = context_acquire(device, NULL, 0);
3918 surface_blt_fbo(device, context, filter,
3919 src_surface, src_texture->resource.draw_binding, src_rect,
3920 dst_surface, dst_texture->resource.draw_binding, dst_rect);
3921 context_release(context);
3923 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx,
3924 dst_texture->resource.draw_binding);
3925 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx,
3926 ~dst_texture->resource.draw_binding);
3928 return WINED3D_OK;
3931 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, blit_op,
3932 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
3933 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format);
3934 if (blitter)
3936 blitter->blit_surface(device, blit_op, filter, src_surface,
3937 src_rect, dst_surface, dst_rect, color_key);
3938 return WINED3D_OK;
3943 fallback:
3944 /* Special cases for render targets. */
3945 if (SUCCEEDED(surface_blt_special(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter)))
3946 return WINED3D_OK;
3948 cpu:
3949 return surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
3950 src_texture, src_sub_resource_idx, &src_box, flags, fx, filter);