wined3d: Use wined3d_texture_prepare_location() in surface_blt_fbo().
[wine.git] / dlls / wined3d / surface.c
blob88187e8464744ceb0fafeed5b670ce0cb9758459
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 void surface_get_drawable_size(const struct wined3d_surface *surface, const struct wined3d_context *context,
40 unsigned int *width, unsigned int *height)
42 if (surface->container->swapchain)
44 /* The drawable size of an onscreen drawable is the surface size.
45 * (Actually: The window size, but the surface is created in window
46 * size.) */
47 *width = context->current_rt.texture->resource.width;
48 *height = context->current_rt.texture->resource.height;
50 else if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER)
52 const struct wined3d_swapchain *swapchain = context->swapchain;
54 /* The drawable size of a backbuffer / aux buffer offscreen target is
55 * the size of the current context's drawable, which is the size of
56 * the back buffer of the swapchain the active context belongs to. */
57 *width = swapchain->desc.backbuffer_width;
58 *height = swapchain->desc.backbuffer_height;
60 else
62 struct wined3d_surface *rt;
64 /* The drawable size of an FBO target is the OpenGL texture size,
65 * which is the power of two size. */
66 rt = context->current_rt.texture->sub_resources[context->current_rt.sub_resource_idx].u.surface;
67 *width = wined3d_texture_get_level_pow2_width(rt->container, rt->texture_level);
68 *height = wined3d_texture_get_level_pow2_height(rt->container, rt->texture_level);
72 struct blt_info
74 GLenum binding;
75 GLenum bind_target;
76 enum wined3d_gl_resource_type tex_type;
77 GLfloat coords[4][3];
80 struct float_rect
82 float l;
83 float t;
84 float r;
85 float b;
88 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
90 f->l = ((r->left * 2.0f) / w) - 1.0f;
91 f->t = ((r->top * 2.0f) / h) - 1.0f;
92 f->r = ((r->right * 2.0f) / w) - 1.0f;
93 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
96 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
98 GLfloat (*coords)[3] = info->coords;
99 struct float_rect f;
101 switch (target)
103 default:
104 FIXME("Unsupported texture target %#x\n", target);
105 /* Fall back to GL_TEXTURE_2D */
106 case GL_TEXTURE_2D:
107 info->binding = GL_TEXTURE_BINDING_2D;
108 info->bind_target = GL_TEXTURE_2D;
109 info->tex_type = WINED3D_GL_RES_TYPE_TEX_2D;
110 coords[0][0] = (float)rect->left / w;
111 coords[0][1] = (float)rect->top / h;
112 coords[0][2] = 0.0f;
114 coords[1][0] = (float)rect->right / w;
115 coords[1][1] = (float)rect->top / h;
116 coords[1][2] = 0.0f;
118 coords[2][0] = (float)rect->left / w;
119 coords[2][1] = (float)rect->bottom / h;
120 coords[2][2] = 0.0f;
122 coords[3][0] = (float)rect->right / w;
123 coords[3][1] = (float)rect->bottom / h;
124 coords[3][2] = 0.0f;
125 break;
127 case GL_TEXTURE_RECTANGLE_ARB:
128 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
129 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
130 info->tex_type = WINED3D_GL_RES_TYPE_TEX_RECT;
131 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
132 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
133 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
134 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
135 break;
137 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
138 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
139 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
140 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
141 cube_coords_float(rect, w, h, &f);
143 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
144 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
145 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
146 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
147 break;
149 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
150 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
151 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
152 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
153 cube_coords_float(rect, w, h, &f);
155 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
156 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
157 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
158 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
159 break;
161 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
162 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
163 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
164 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
165 cube_coords_float(rect, w, h, &f);
167 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
168 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
169 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
170 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
171 break;
173 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
174 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
175 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
176 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
177 cube_coords_float(rect, w, h, &f);
179 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
180 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
181 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
182 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
183 break;
185 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
186 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
187 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
188 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
189 cube_coords_float(rect, w, h, &f);
191 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
192 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
193 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
194 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
195 break;
197 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
198 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
199 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
200 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
201 cube_coords_float(rect, w, h, &f);
203 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
204 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
205 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
206 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
207 break;
211 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
213 if (rect_in)
214 *rect_out = *rect_in;
215 else
217 const struct wined3d_texture *texture = surface->container;
219 rect_out->left = 0;
220 rect_out->top = 0;
221 rect_out->right = wined3d_texture_get_level_width(texture, surface->texture_level);
222 rect_out->bottom = wined3d_texture_get_level_height(texture, surface->texture_level);
226 /* Context activation is done by the caller. */
227 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
228 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
230 const struct wined3d_gl_info *gl_info = context->gl_info;
231 struct wined3d_texture *texture = src_surface->container;
232 struct blt_info info;
234 surface_get_blt_info(src_surface->texture_target, src_rect,
235 wined3d_texture_get_level_pow2_width(texture, src_surface->texture_level),
236 wined3d_texture_get_level_pow2_height(texture, src_surface->texture_level), &info);
238 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
239 checkGLcall("glEnable(bind_target)");
241 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
243 /* Filtering for StretchRect */
244 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
245 checkGLcall("glTexParameteri");
246 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
247 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
248 checkGLcall("glTexParameteri");
249 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
250 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
251 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
252 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
253 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
254 checkGLcall("glTexEnvi");
256 /* Draw a quad */
257 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
258 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
259 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
261 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
262 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
264 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
265 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
267 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
268 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
269 gl_info->gl_ops.gl.p_glEnd();
271 /* Unbind the texture */
272 context_bind_texture(context, info.bind_target, 0);
274 /* We changed the filtering settings on the texture. Inform the
275 * container about this to get the filters reset properly next draw. */
276 texture->texture_rgb.sampler_desc.mag_filter = WINED3D_TEXF_POINT;
277 texture->texture_rgb.sampler_desc.min_filter = WINED3D_TEXF_POINT;
278 texture->texture_rgb.sampler_desc.mip_filter = WINED3D_TEXF_NONE;
279 texture->texture_rgb.sampler_desc.srgb_decode = FALSE;
282 /* Works correctly only for <= 4 bpp formats. */
283 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
285 masks[0] = ((1u << format->red_size) - 1) << format->red_offset;
286 masks[1] = ((1u << format->green_size) - 1) << format->green_offset;
287 masks[2] = ((1u << format->blue_size) - 1) << format->blue_offset;
290 void wined3d_surface_destroy_dc(struct wined3d_surface *surface)
292 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
293 struct wined3d_texture *texture = surface->container;
294 struct wined3d_device *device = texture->resource.device;
295 const struct wined3d_gl_info *gl_info = NULL;
296 D3DKMT_DESTROYDCFROMMEMORY destroy_desc;
297 struct wined3d_context *context = NULL;
298 struct wined3d_bo_address data;
299 NTSTATUS status;
301 if (!surface->dc)
303 ERR("Surface %p has no DC.\n", surface);
304 return;
307 TRACE("dc %p, bitmap %p.\n", surface->dc, surface->bitmap);
309 destroy_desc.hDc = surface->dc;
310 destroy_desc.hBitmap = surface->bitmap;
311 if ((status = D3DKMTDestroyDCFromMemory(&destroy_desc)))
312 ERR("Failed to destroy dc, status %#x.\n", status);
313 surface->dc = NULL;
314 surface->bitmap = NULL;
316 if (device->d3d_initialized)
318 context = context_acquire(device, NULL);
319 gl_info = context->gl_info;
322 wined3d_texture_get_memory(texture, sub_resource_idx, &data, texture->resource.map_binding);
323 wined3d_texture_unmap_bo_address(&data, gl_info, GL_PIXEL_UNPACK_BUFFER);
325 if (context)
326 context_release(context);
329 HRESULT wined3d_surface_create_dc(struct wined3d_surface *surface)
331 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
332 struct wined3d_texture *texture = surface->container;
333 const struct wined3d_format *format = texture->resource.format;
334 struct wined3d_device *device = texture->resource.device;
335 const struct wined3d_gl_info *gl_info = NULL;
336 struct wined3d_context *context = NULL;
337 unsigned int row_pitch, slice_pitch;
338 struct wined3d_bo_address data;
339 D3DKMT_CREATEDCFROMMEMORY desc;
340 NTSTATUS status;
342 TRACE("surface %p.\n", surface);
344 if (!format->ddi_format)
346 WARN("Cannot create a DC for format %s.\n", debug_d3dformat(format->id));
347 return WINED3DERR_INVALIDCALL;
350 wined3d_texture_get_pitch(texture, surface->texture_level, &row_pitch, &slice_pitch);
352 if (device->d3d_initialized)
354 context = context_acquire(device, NULL);
355 gl_info = context->gl_info;
358 wined3d_texture_get_memory(texture, sub_resource_idx, &data, texture->resource.map_binding);
359 desc.pMemory = wined3d_texture_map_bo_address(&data, texture->sub_resources[sub_resource_idx].size,
360 gl_info, GL_PIXEL_UNPACK_BUFFER, 0);
362 if (context)
363 context_release(context);
365 desc.Format = format->ddi_format;
366 desc.Width = wined3d_texture_get_level_width(texture, surface->texture_level);
367 desc.Height = wined3d_texture_get_level_height(texture, surface->texture_level);
368 desc.Pitch = row_pitch;
369 desc.hDeviceDc = CreateCompatibleDC(NULL);
370 desc.pColorTable = NULL;
372 status = D3DKMTCreateDCFromMemory(&desc);
373 DeleteDC(desc.hDeviceDc);
374 if (status)
376 WARN("Failed to create DC, status %#x.\n", status);
377 return WINED3DERR_INVALIDCALL;
380 surface->dc = desc.hDc;
381 surface->bitmap = desc.hBitmap;
383 TRACE("Created DC %p, bitmap %p for surface %p.\n", surface->dc, surface->bitmap, surface);
385 return WINED3D_OK;
388 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
390 unsigned int t;
392 t = wined3d_texture_get_level_width(surface->container, surface->texture_level);
393 if ((r->left && r->right) || abs(r->right - r->left) != t)
394 return FALSE;
395 t = wined3d_texture_get_level_height(surface->container, surface->texture_level);
396 if ((r->top && r->bottom) || abs(r->bottom - r->top) != t)
397 return FALSE;
398 return TRUE;
401 static void surface_depth_blt_fbo(const struct wined3d_device *device,
402 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
403 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
405 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
406 struct wined3d_texture *dst_texture = dst_surface->container;
407 struct wined3d_texture *src_texture = src_surface->container;
408 const struct wined3d_gl_info *gl_info;
409 struct wined3d_context *context;
410 DWORD src_mask, dst_mask;
411 GLbitfield gl_mask;
413 TRACE("device %p\n", device);
414 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
415 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
416 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
417 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
419 src_mask = src_texture->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
420 dst_mask = dst_texture->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
422 if (src_mask != dst_mask)
424 ERR("Incompatible formats %s and %s.\n",
425 debug_d3dformat(src_texture->resource.format->id),
426 debug_d3dformat(dst_texture->resource.format->id));
427 return;
430 if (!src_mask)
432 ERR("Not a depth / stencil format: %s.\n",
433 debug_d3dformat(src_texture->resource.format->id));
434 return;
437 gl_mask = 0;
438 if (src_mask & WINED3DFMT_FLAG_DEPTH)
439 gl_mask |= GL_DEPTH_BUFFER_BIT;
440 if (src_mask & WINED3DFMT_FLAG_STENCIL)
441 gl_mask |= GL_STENCIL_BUFFER_BIT;
443 context = context_acquire(device, NULL);
444 if (!context->valid)
446 context_release(context);
447 WARN("Invalid context, skipping blit.\n");
448 return;
451 /* Make sure the locations are up-to-date. Loading the destination
452 * surface isn't required if the entire surface is overwritten. */
453 surface_load_location(src_surface, context, src_location);
454 if (!surface_is_full_rect(dst_surface, dst_rect))
455 surface_load_location(dst_surface, context, dst_location);
456 else
457 wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, context, dst_location);
459 gl_info = context->gl_info;
461 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
462 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
464 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
465 context_set_draw_buffer(context, GL_NONE);
466 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
467 context_invalidate_state(context, STATE_FRAMEBUFFER);
469 if (gl_mask & GL_DEPTH_BUFFER_BIT)
471 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
472 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
474 if (gl_mask & GL_STENCIL_BUFFER_BIT)
476 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
478 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
479 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
481 gl_info->gl_ops.gl.p_glStencilMask(~0U);
482 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
485 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
486 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
488 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
489 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
490 checkGLcall("glBlitFramebuffer()");
492 if (wined3d_settings.strict_draw_ordering)
493 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
495 context_release(context);
498 /* Blit between surface locations. Onscreen on different swapchains is not supported.
499 * Depth / stencil is not supported. Context activation is done by the caller. */
500 static void surface_blt_fbo(const struct wined3d_device *device,
501 struct wined3d_context *old_ctx, enum wined3d_texture_filter_type filter,
502 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
503 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
505 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
506 struct wined3d_texture *dst_texture = dst_surface->container;
507 struct wined3d_texture *src_texture = src_surface->container;
508 const struct wined3d_gl_info *gl_info;
509 struct wined3d_context *context = old_ctx;
510 struct wined3d_surface *required_rt, *restore_rt = NULL;
511 RECT src_rect, dst_rect;
512 GLenum gl_filter;
513 GLenum buffer;
515 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
516 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
517 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
518 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
519 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
521 src_rect = *src_rect_in;
522 dst_rect = *dst_rect_in;
524 switch (filter)
526 case WINED3D_TEXF_LINEAR:
527 gl_filter = GL_LINEAR;
528 break;
530 default:
531 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
532 case WINED3D_TEXF_NONE:
533 case WINED3D_TEXF_POINT:
534 gl_filter = GL_NEAREST;
535 break;
538 /* Resolve the source surface first if needed. */
539 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
540 && (src_texture->resource.format->id != dst_texture->resource.format->id
541 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
542 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
543 src_location = WINED3D_LOCATION_RB_RESOLVED;
545 /* Make sure the locations are up-to-date. Loading the destination
546 * surface isn't required if the entire surface is overwritten. (And is
547 * in fact harmful if we're being called by surface_load_location() with
548 * the purpose of loading the destination surface.) */
549 surface_load_location(src_surface, old_ctx, src_location);
550 if (!surface_is_full_rect(dst_surface, &dst_rect))
551 surface_load_location(dst_surface, old_ctx, dst_location);
552 else
553 wined3d_texture_prepare_location(dst_texture, dst_sub_resource_idx, old_ctx, dst_location);
556 if (src_location == WINED3D_LOCATION_DRAWABLE) required_rt = src_surface;
557 else if (dst_location == WINED3D_LOCATION_DRAWABLE) required_rt = dst_surface;
558 else required_rt = NULL;
560 restore_rt = context_get_rt_surface(old_ctx);
561 if (restore_rt != required_rt)
562 context = context_acquire(device, required_rt);
563 else
564 restore_rt = NULL;
566 if (!context->valid)
568 context_release(context);
569 WARN("Invalid context, skipping blit.\n");
570 return;
573 gl_info = context->gl_info;
575 if (src_location == WINED3D_LOCATION_DRAWABLE)
577 TRACE("Source surface %p is onscreen.\n", src_surface);
578 buffer = wined3d_texture_get_gl_buffer(src_texture);
579 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
581 else
583 TRACE("Source surface %p is offscreen.\n", src_surface);
584 buffer = GL_COLOR_ATTACHMENT0;
587 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
588 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
589 checkGLcall("glReadBuffer()");
590 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
592 if (dst_location == WINED3D_LOCATION_DRAWABLE)
594 TRACE("Destination surface %p is onscreen.\n", dst_surface);
595 buffer = wined3d_texture_get_gl_buffer(dst_texture);
596 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
598 else
600 TRACE("Destination surface %p is offscreen.\n", dst_surface);
601 buffer = GL_COLOR_ATTACHMENT0;
604 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
605 context_set_draw_buffer(context, buffer);
606 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
607 context_invalidate_state(context, STATE_FRAMEBUFFER);
609 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
610 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
611 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
612 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
613 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
615 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
616 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
618 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
619 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
620 checkGLcall("glBlitFramebuffer()");
622 if (wined3d_settings.strict_draw_ordering || (dst_location == WINED3D_LOCATION_DRAWABLE
623 && dst_texture->swapchain->front_buffer == dst_texture))
624 gl_info->gl_ops.gl.p_glFlush();
626 if (restore_rt)
627 context_restore(context, restore_rt);
630 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
631 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
632 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
634 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
635 return FALSE;
637 /* Source and/or destination need to be on the GL side */
638 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
639 return FALSE;
641 switch (blit_op)
643 case WINED3D_BLIT_OP_COLOR_BLIT:
644 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
645 || (src_usage & WINED3DUSAGE_RENDERTARGET)))
646 return FALSE;
647 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
648 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
649 return FALSE;
650 if (!(src_format->id == dst_format->id
651 || (is_identity_fixup(src_format->color_fixup)
652 && is_identity_fixup(dst_format->color_fixup))))
653 return FALSE;
654 break;
656 case WINED3D_BLIT_OP_DEPTH_BLIT:
657 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
658 return FALSE;
659 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
660 return FALSE;
661 /* Accept pure swizzle fixups for depth formats. In general we
662 * ignore the stencil component (if present) at the moment and the
663 * swizzle is not relevant with just the depth component. */
664 if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup)
665 || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup))
666 return FALSE;
667 break;
669 default:
670 return FALSE;
673 return TRUE;
676 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
678 const struct wined3d_format *format = surface->container->resource.format;
680 switch (format->id)
682 case WINED3DFMT_S1_UINT_D15_UNORM:
683 *float_depth = depth / (float)0x00007fff;
684 break;
686 case WINED3DFMT_D16_UNORM:
687 *float_depth = depth / (float)0x0000ffff;
688 break;
690 case WINED3DFMT_D24_UNORM_S8_UINT:
691 case WINED3DFMT_X8D24_UNORM:
692 *float_depth = depth / (float)0x00ffffff;
693 break;
695 case WINED3DFMT_D32_UNORM:
696 *float_depth = depth / (float)0xffffffff;
697 break;
699 default:
700 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
701 return FALSE;
704 return TRUE;
707 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
709 struct wined3d_resource *resource = &surface->container->resource;
710 struct wined3d_device *device = resource->device;
711 struct wined3d_rendertarget_view_desc view_desc;
712 struct wined3d_rendertarget_view *view;
713 const struct blit_shader *blitter;
714 HRESULT hr;
716 if (!(blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info,
717 WINED3D_BLIT_OP_DEPTH_FILL, NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format)))
719 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
720 return WINED3DERR_INVALIDCALL;
723 view_desc.format_id = resource->format->id;
724 view_desc.u.texture.level_idx = surface->texture_level;
725 view_desc.u.texture.layer_idx = surface->texture_layer;
726 view_desc.u.texture.layer_count = 1;
727 if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc,
728 resource, NULL, &wined3d_null_parent_ops, &view)))
730 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
731 return hr;
734 hr = blitter->depth_fill(device, view, rect, WINED3DCLEAR_ZBUFFER, depth, 0);
735 wined3d_rendertarget_view_decref(view);
737 return hr;
740 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
741 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
743 struct wined3d_texture *src_texture = src_surface->container;
744 struct wined3d_texture *dst_texture = dst_surface->container;
745 struct wined3d_device *device = src_texture->resource.device;
747 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
748 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
749 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format))
750 return WINED3DERR_INVALIDCALL;
752 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
754 surface_modify_ds_location(dst_surface, dst_location,
755 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
757 return WINED3D_OK;
760 /* This call just downloads data, the caller is responsible for binding the
761 * correct texture. */
762 /* Context activation is done by the caller. */
763 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
764 DWORD dst_location)
766 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
767 struct wined3d_texture *texture = surface->container;
768 const struct wined3d_format *format = texture->resource.format;
769 struct wined3d_texture_sub_resource *sub_resource;
770 unsigned int dst_row_pitch, dst_slice_pitch;
771 unsigned int src_row_pitch, src_slice_pitch;
772 struct wined3d_bo_address data;
773 BYTE *temporary_mem = NULL;
774 void *mem;
776 /* Only support read back of converted P8 surfaces. */
777 if (texture->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
779 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
780 return;
783 sub_resource = &texture->sub_resources[sub_resource_idx];
785 if (surface->texture_target == GL_TEXTURE_2D_ARRAY)
787 /* We don't expect to ever need to emulate NP2 textures when we have EXT_texture_array. */
788 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
790 FIXME("Cannot download surface %p, level %u, layer %u.\n",
791 surface, surface->texture_level, surface->texture_layer);
792 return;
795 WARN_(d3d_perf)("Downloading all miplevel layers to get the surface data for a single sub-resource.\n");
797 if (!(temporary_mem = HeapAlloc(GetProcessHeap(), 0, texture->layer_count * sub_resource->size)))
799 ERR("Out of memory.\n");
800 return;
804 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
806 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
808 wined3d_texture_get_pitch(texture, surface->texture_level, &dst_row_pitch, &dst_slice_pitch);
809 wined3d_format_calculate_pitch(format, texture->resource.device->surface_alignment,
810 wined3d_texture_get_level_pow2_width(texture, surface->texture_level),
811 wined3d_texture_get_level_pow2_height(texture, surface->texture_level),
812 &src_row_pitch, &src_slice_pitch);
813 if (!(temporary_mem = HeapAlloc(GetProcessHeap(), 0, src_slice_pitch)))
815 ERR("Out of memory.\n");
816 return;
819 if (data.buffer_object)
820 ERR("NP2 emulated texture uses PBO unexpectedly.\n");
821 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
822 ERR("Unexpected compressed format for NP2 emulated texture.\n");
825 if (temporary_mem)
827 mem = temporary_mem;
829 else if (data.buffer_object)
831 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
832 checkGLcall("glBindBuffer");
833 mem = data.addr;
835 else
837 mem = data.addr;
840 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
842 TRACE("Downloading compressed surface %p, level %u, format %#x, type %#x, data %p.\n",
843 surface, surface->texture_level, format->glFormat, format->glType, mem);
845 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target, surface->texture_level, mem));
846 checkGLcall("glGetCompressedTexImage");
848 else
850 TRACE("Downloading surface %p, level %u, format %#x, type %#x, data %p.\n",
851 surface, surface->texture_level, format->glFormat, format->glType, mem);
853 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
854 format->glFormat, format->glType, mem);
855 checkGLcall("glGetTexImage");
858 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
860 const BYTE *src_data;
861 unsigned int h, y;
862 BYTE *dst_data;
864 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
865 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
866 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
868 * We're doing this...
870 * instead of boxing the texture :
871 * |<-texture width ->| -->pow2width| /\
872 * |111111111111111111| | |
873 * |222 Texture 222222| boxed empty | texture height
874 * |3333 Data 33333333| | |
875 * |444444444444444444| | \/
876 * ----------------------------------- |
877 * | boxed empty | boxed empty | pow2height
878 * | | | \/
879 * -----------------------------------
882 * we're repacking the data to the expected texture width
884 * |<-texture width ->| -->pow2width| /\
885 * |111111111111111111222222222222222| |
886 * |222333333333333333333444444444444| texture height
887 * |444444 | |
888 * | | \/
889 * | | |
890 * | empty | pow2height
891 * | | \/
892 * -----------------------------------
894 * == is the same as
896 * |<-texture width ->| /\
897 * |111111111111111111|
898 * |222222222222222222|texture height
899 * |333333333333333333|
900 * |444444444444444444| \/
901 * --------------------
903 * This also means that any references to surface memory should work with the data as if it were a
904 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
906 * internally the texture is still stored in a boxed format so any references to textureName will
907 * get a boxed texture with width pow2width and not a texture of width resource.width. */
908 src_data = mem;
909 dst_data = data.addr;
910 TRACE("Repacking the surface data from pitch %u to pitch %u.\n", src_row_pitch, dst_row_pitch);
911 h = wined3d_texture_get_level_height(texture, surface->texture_level);
912 for (y = 0; y < h; ++y)
914 memcpy(dst_data, src_data, dst_row_pitch);
915 src_data += src_row_pitch;
916 dst_data += dst_row_pitch;
919 else if (temporary_mem)
921 void *src_data = temporary_mem + surface->texture_layer * sub_resource->size;
922 if (data.buffer_object)
924 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
925 checkGLcall("glBindBuffer");
926 GL_EXTCALL(glBufferSubData(GL_PIXEL_PACK_BUFFER, 0, sub_resource->size, src_data));
927 checkGLcall("glBufferSubData");
929 else
931 memcpy(data.addr, src_data, sub_resource->size);
935 if (data.buffer_object)
937 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
938 checkGLcall("glBindBuffer");
941 HeapFree(GetProcessHeap(), 0, temporary_mem);
944 /* This call just uploads data, the caller is responsible for binding the
945 * correct texture. */
946 /* Context activation is done by the caller. */
947 void wined3d_surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
948 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
949 BOOL srgb, const struct wined3d_const_bo_address *data)
951 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
952 struct wined3d_texture *texture = surface->container;
953 UINT update_w = src_rect->right - src_rect->left;
954 UINT update_h = src_rect->bottom - src_rect->top;
956 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
957 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
958 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
960 if (texture->sub_resources[sub_resource_idx].map_count)
962 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
963 texture->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
966 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE)
968 update_h *= format->height_scale.numerator;
969 update_h /= format->height_scale.denominator;
972 if (data->buffer_object)
974 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
975 checkGLcall("glBindBuffer");
978 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
980 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
981 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
982 const BYTE *addr = data->addr;
983 GLenum internal;
985 addr += (src_rect->top / format->block_height) * src_pitch;
986 addr += (src_rect->left / format->block_width) * format->block_byte_count;
988 if (srgb)
989 internal = format->glGammaInternal;
990 else if (texture->resource.usage & WINED3DUSAGE_RENDERTARGET
991 && wined3d_resource_is_offscreen(&texture->resource))
992 internal = format->rtInternal;
993 else
994 internal = format->glInternal;
996 TRACE("glCompressedTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, "
997 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
998 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1000 if (row_length == src_pitch)
1002 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1003 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1005 else
1007 UINT row, y;
1009 /* glCompressedTexSubImage2D() ignores pixel store state, so we
1010 * can't use the unpack row length like for glTexSubImage2D. */
1011 for (row = 0, y = dst_point->y; row < row_count; ++row)
1013 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1014 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1015 y += format->block_height;
1016 addr += src_pitch;
1019 checkGLcall("glCompressedTexSubImage2D");
1021 else
1023 const BYTE *addr = data->addr;
1025 addr += src_rect->top * src_pitch;
1026 addr += src_rect->left * format->byte_count;
1028 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1029 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1030 update_w, update_h, format->glFormat, format->glType, addr);
1032 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1033 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1034 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1035 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1036 checkGLcall("glTexSubImage2D");
1039 if (data->buffer_object)
1041 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
1042 checkGLcall("glBindBuffer");
1045 if (wined3d_settings.strict_draw_ordering)
1046 gl_info->gl_ops.gl.p_glFlush();
1048 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1050 struct wined3d_device *device = texture->resource.device;
1051 unsigned int i;
1053 for (i = 0; i < device->context_count; ++i)
1055 context_surface_update(device->contexts[i], surface);
1060 static BOOL surface_check_block_align_rect(struct wined3d_surface *surface, const RECT *rect)
1062 struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
1064 return wined3d_texture_check_block_align(surface->container, surface->texture_level, &box);
1067 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1068 struct wined3d_surface *src_surface, const RECT *src_rect)
1070 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
1071 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1072 struct wined3d_texture *src_texture = src_surface->container;
1073 struct wined3d_texture *dst_texture = dst_surface->container;
1074 unsigned int src_row_pitch, src_slice_pitch;
1075 const struct wined3d_format *src_format;
1076 const struct wined3d_format *dst_format;
1077 unsigned int src_fmt_flags, dst_fmt_flags;
1078 const struct wined3d_gl_info *gl_info;
1079 struct wined3d_context *context;
1080 struct wined3d_bo_address data;
1081 UINT update_w, update_h;
1082 UINT dst_w, dst_h;
1083 RECT r, dst_rect;
1084 POINT p;
1086 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1087 dst_surface, wine_dbgstr_point(dst_point),
1088 src_surface, wine_dbgstr_rect(src_rect));
1090 src_format = src_texture->resource.format;
1091 dst_format = dst_texture->resource.format;
1092 src_fmt_flags = src_texture->resource.format_flags;
1093 dst_fmt_flags = dst_texture->resource.format_flags;
1095 if (src_format->id != dst_format->id)
1097 WARN("Source and destination surfaces should have the same format.\n");
1098 return WINED3DERR_INVALIDCALL;
1101 if (!dst_point)
1103 p.x = 0;
1104 p.y = 0;
1105 dst_point = &p;
1107 else if (dst_point->x < 0 || dst_point->y < 0)
1109 WARN("Invalid destination point.\n");
1110 return WINED3DERR_INVALIDCALL;
1113 if (!src_rect)
1115 SetRect(&r, 0, 0, wined3d_texture_get_level_width(src_texture, src_surface->texture_level),
1116 wined3d_texture_get_level_height(src_texture, src_surface->texture_level));
1117 src_rect = &r;
1119 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1120 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1122 WARN("Invalid source rectangle.\n");
1123 return WINED3DERR_INVALIDCALL;
1126 dst_w = wined3d_texture_get_level_width(dst_texture, dst_surface->texture_level);
1127 dst_h = wined3d_texture_get_level_height(dst_texture, dst_surface->texture_level);
1129 update_w = src_rect->right - src_rect->left;
1130 update_h = src_rect->bottom - src_rect->top;
1132 if (update_w > dst_w || dst_point->x > dst_w - update_w
1133 || update_h > dst_h || dst_point->y > dst_h - update_h)
1135 WARN("Destination out of bounds.\n");
1136 return WINED3DERR_INVALIDCALL;
1139 if ((src_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(src_surface, src_rect))
1141 WARN("Source rectangle not block-aligned.\n");
1142 return WINED3DERR_INVALIDCALL;
1145 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1146 if ((dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(dst_surface, &dst_rect))
1148 WARN("Destination rectangle not block-aligned.\n");
1149 return WINED3DERR_INVALIDCALL;
1152 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1153 if (dst_format->convert || wined3d_format_get_color_key_conversion(dst_texture, FALSE))
1154 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1156 context = context_acquire(dst_texture->resource.device, NULL);
1157 gl_info = context->gl_info;
1159 /* Only load the surface for partial updates. For newly allocated texture
1160 * the texture wouldn't be the current location, and we'd upload zeroes
1161 * just to overwrite them again. */
1162 if (update_w == dst_w && update_h == dst_h)
1163 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
1164 else
1165 surface_load_location(dst_surface, context, WINED3D_LOCATION_TEXTURE_RGB);
1166 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
1168 wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &data,
1169 src_texture->sub_resources[src_sub_resource_idx].locations);
1170 wined3d_texture_get_pitch(src_texture, src_surface->texture_level, &src_row_pitch, &src_slice_pitch);
1172 wined3d_surface_upload_data(dst_surface, gl_info, src_format, src_rect,
1173 src_row_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
1175 context_release(context);
1177 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1178 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1180 return WINED3D_OK;
1183 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1184 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1185 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1186 /* Context activation is done by the caller. */
1187 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1189 const struct wined3d_gl_info *gl_info = &surface->container->resource.device->adapter->gl_info;
1190 struct wined3d_renderbuffer_entry *entry;
1191 GLuint renderbuffer = 0;
1192 unsigned int src_width, src_height;
1193 unsigned int width, height;
1195 if (rt && rt->container->resource.format->id != WINED3DFMT_NULL)
1197 width = wined3d_texture_get_level_pow2_width(rt->container, rt->texture_level);
1198 height = wined3d_texture_get_level_pow2_height(rt->container, rt->texture_level);
1200 else
1202 width = wined3d_texture_get_level_pow2_width(surface->container, surface->texture_level);
1203 height = wined3d_texture_get_level_pow2_height(surface->container, surface->texture_level);
1206 src_width = wined3d_texture_get_level_pow2_width(surface->container, surface->texture_level);
1207 src_height = wined3d_texture_get_level_pow2_height(surface->container, surface->texture_level);
1209 /* A depth stencil smaller than the render target is not valid */
1210 if (width > src_width || height > src_height) return;
1212 /* Remove any renderbuffer set if the sizes match */
1213 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1214 || (width == src_width && height == src_height))
1216 surface->current_renderbuffer = NULL;
1217 return;
1220 /* Look if we've already got a renderbuffer of the correct dimensions */
1221 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1223 if (entry->width == width && entry->height == height)
1225 renderbuffer = entry->id;
1226 surface->current_renderbuffer = entry;
1227 break;
1231 if (!renderbuffer)
1233 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1234 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1235 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1236 surface->container->resource.format->glInternal, width, height);
1238 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1239 entry->width = width;
1240 entry->height = height;
1241 entry->id = renderbuffer;
1242 list_add_head(&surface->renderbuffers, &entry->entry);
1244 surface->current_renderbuffer = entry;
1247 checkGLcall("set_compatible_renderbuffer");
1250 /* See also float_16_to_32() in wined3d_private.h */
1251 static inline unsigned short float_32_to_16(const float *in)
1253 int exp = 0;
1254 float tmp = fabsf(*in);
1255 unsigned int mantissa;
1256 unsigned short ret;
1258 /* Deal with special numbers */
1259 if (*in == 0.0f)
1260 return 0x0000;
1261 if (isnan(*in))
1262 return 0x7c01;
1263 if (isinf(*in))
1264 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1266 if (tmp < (float)(1u << 10))
1270 tmp = tmp * 2.0f;
1271 exp--;
1272 } while (tmp < (float)(1u << 10));
1274 else if (tmp >= (float)(1u << 11))
1278 tmp /= 2.0f;
1279 exp++;
1280 } while (tmp >= (float)(1u << 11));
1283 mantissa = (unsigned int)tmp;
1284 if (tmp - mantissa >= 0.5f)
1285 ++mantissa; /* Round to nearest, away from zero. */
1287 exp += 10; /* Normalize the mantissa. */
1288 exp += 15; /* Exponent is encoded with excess 15. */
1290 if (exp > 30) /* too big */
1292 ret = 0x7c00; /* INF */
1294 else if (exp <= 0)
1296 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1297 while (exp <= 0)
1299 mantissa = mantissa >> 1;
1300 ++exp;
1302 ret = mantissa & 0x3ff;
1304 else
1306 ret = (exp << 10) | (mantissa & 0x3ff);
1309 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1310 return ret;
1313 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
1314 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1316 unsigned short *dst_s;
1317 const float *src_f;
1318 unsigned int x, y;
1320 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1322 for (y = 0; y < h; ++y)
1324 src_f = (const float *)(src + y * pitch_in);
1325 dst_s = (unsigned short *) (dst + y * pitch_out);
1326 for (x = 0; x < w; ++x)
1328 dst_s[x] = float_32_to_16(src_f + x);
1333 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
1334 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1336 static const unsigned char convert_5to8[] =
1338 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
1339 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
1340 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
1341 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
1343 static const unsigned char convert_6to8[] =
1345 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
1346 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
1347 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
1348 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
1349 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
1350 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
1351 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
1352 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
1354 unsigned int x, y;
1356 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1358 for (y = 0; y < h; ++y)
1360 const WORD *src_line = (const WORD *)(src + y * pitch_in);
1361 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1362 for (x = 0; x < w; ++x)
1364 WORD pixel = src_line[x];
1365 dst_line[x] = 0xff000000u
1366 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
1367 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
1368 | convert_5to8[(pixel & 0x001fu)];
1373 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
1374 * in both cases we're just setting the X / Alpha channel to 0xff. */
1375 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
1376 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1378 unsigned int x, y;
1380 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1382 for (y = 0; y < h; ++y)
1384 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
1385 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1387 for (x = 0; x < w; ++x)
1389 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
1394 static inline BYTE cliptobyte(int x)
1396 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
1399 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
1400 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1402 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1403 unsigned int x, y;
1405 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1407 for (y = 0; y < h; ++y)
1409 const BYTE *src_line = src + y * pitch_in;
1410 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1411 for (x = 0; x < w; ++x)
1413 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1414 * C = Y - 16; D = U - 128; E = V - 128;
1415 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1416 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1417 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1418 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1419 * U and V are shared between the pixels. */
1420 if (!(x & 1)) /* For every even pixel, read new U and V. */
1422 d = (int) src_line[1] - 128;
1423 e = (int) src_line[3] - 128;
1424 r2 = 409 * e + 128;
1425 g2 = - 100 * d - 208 * e + 128;
1426 b2 = 516 * d + 128;
1428 c2 = 298 * ((int) src_line[0] - 16);
1429 dst_line[x] = 0xff000000
1430 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
1431 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
1432 | cliptobyte((c2 + b2) >> 8); /* blue */
1433 /* Scale RGB values to 0..255 range,
1434 * then clip them if still not in range (may be negative),
1435 * then shift them within DWORD if necessary. */
1436 src_line += 2;
1441 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
1442 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1444 unsigned int x, y;
1445 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1447 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
1449 for (y = 0; y < h; ++y)
1451 const BYTE *src_line = src + y * pitch_in;
1452 WORD *dst_line = (WORD *)(dst + y * pitch_out);
1453 for (x = 0; x < w; ++x)
1455 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1456 * C = Y - 16; D = U - 128; E = V - 128;
1457 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1458 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1459 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1460 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1461 * U and V are shared between the pixels. */
1462 if (!(x & 1)) /* For every even pixel, read new U and V. */
1464 d = (int) src_line[1] - 128;
1465 e = (int) src_line[3] - 128;
1466 r2 = 409 * e + 128;
1467 g2 = - 100 * d - 208 * e + 128;
1468 b2 = 516 * d + 128;
1470 c2 = 298 * ((int) src_line[0] - 16);
1471 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
1472 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
1473 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
1474 /* Scale RGB values to 0..255 range,
1475 * then clip them if still not in range (may be negative),
1476 * then shift them within DWORD if necessary. */
1477 src_line += 2;
1482 struct d3dfmt_converter_desc
1484 enum wined3d_format_id from, to;
1485 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
1488 static const struct d3dfmt_converter_desc converters[] =
1490 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
1491 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
1492 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1493 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1494 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
1495 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
1498 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
1499 enum wined3d_format_id to)
1501 unsigned int i;
1503 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
1505 if (converters[i].from == from && converters[i].to == to)
1506 return &converters[i];
1509 return NULL;
1512 static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture,
1513 unsigned int sub_resource_idx, const struct wined3d_format *dst_format)
1515 unsigned int texture_level = sub_resource_idx % src_texture->level_count;
1516 const struct wined3d_format *src_format = src_texture->resource.format;
1517 struct wined3d_device *device = src_texture->resource.device;
1518 const struct d3dfmt_converter_desc *conv = NULL;
1519 struct wined3d_texture *dst_texture;
1520 struct wined3d_resource_desc desc;
1521 struct wined3d_map_desc src_map;
1523 if (!(conv = find_converter(src_format->id, dst_format->id)) && (!device->d3d_initialized
1524 || !is_identity_fixup(src_format->color_fixup) || src_format->convert
1525 || !is_identity_fixup(dst_format->color_fixup) || dst_format->convert))
1527 FIXME("Cannot find a conversion function from format %s to %s.\n",
1528 debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
1529 return NULL;
1532 /* FIXME: Multisampled conversion? */
1533 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
1534 desc.format = dst_format->id;
1535 desc.multisample_type = WINED3D_MULTISAMPLE_NONE;
1536 desc.multisample_quality = 0;
1537 desc.usage = 0;
1538 desc.pool = WINED3D_POOL_SCRATCH;
1539 desc.width = wined3d_texture_get_level_width(src_texture, texture_level);
1540 desc.height = wined3d_texture_get_level_height(src_texture, texture_level);
1541 desc.depth = 1;
1542 desc.size = 0;
1543 if (FAILED(wined3d_texture_create(device, &desc, 1,
1544 WINED3D_TEXTURE_CREATE_MAPPABLE | WINED3D_TEXTURE_CREATE_DISCARD,
1545 NULL, NULL, &wined3d_null_parent_ops, &dst_texture)))
1547 ERR("Failed to create a destination texture for conversion.\n");
1548 return NULL;
1551 memset(&src_map, 0, sizeof(src_map));
1552 if (FAILED(wined3d_resource_map(&src_texture->resource, sub_resource_idx,
1553 &src_map, NULL, WINED3D_MAP_READONLY)))
1555 ERR("Failed to map the source texture.\n");
1556 wined3d_texture_decref(dst_texture);
1557 return NULL;
1559 if (conv)
1561 struct wined3d_map_desc dst_map;
1563 memset(&dst_map, 0, sizeof(dst_map));
1564 if (FAILED(wined3d_resource_map(&dst_texture->resource, 0, &dst_map, NULL, 0)))
1566 ERR("Failed to map the destination texture.\n");
1567 wined3d_resource_unmap(&src_texture->resource, sub_resource_idx);
1568 wined3d_texture_decref(dst_texture);
1569 return NULL;
1572 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch, desc.width, desc.height);
1574 wined3d_resource_unmap(&dst_texture->resource, 0);
1576 else
1578 struct wined3d_bo_address data = {0, src_map.data};
1579 RECT src_rect = {0, 0, desc.width, desc.height};
1580 const struct wined3d_gl_info *gl_info;
1581 struct wined3d_context *context;
1582 POINT dst_point = {0, 0};
1584 TRACE("Using upload conversion.\n");
1585 context = context_acquire(device, NULL);
1586 gl_info = context->gl_info;
1588 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
1589 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
1590 wined3d_surface_upload_data(dst_texture->sub_resources[0].u.surface, gl_info, src_format,
1591 &src_rect, src_map.row_pitch, &dst_point, FALSE, wined3d_const_bo_address(&data));
1593 context_release(context);
1595 wined3d_texture_validate_location(dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB);
1596 wined3d_texture_invalidate_location(dst_texture, 0, ~WINED3D_LOCATION_TEXTURE_RGB);
1598 wined3d_resource_unmap(&src_texture->resource, sub_resource_idx);
1600 return dst_texture;
1603 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
1604 unsigned int bpp, UINT pitch, DWORD color)
1606 BYTE *first;
1607 unsigned int x, y;
1609 /* Do first row */
1611 #define COLORFILL_ROW(type) \
1612 do { \
1613 type *d = (type *)buf; \
1614 for (x = 0; x < width; ++x) \
1615 d[x] = (type)color; \
1616 } while(0)
1618 switch (bpp)
1620 case 1:
1621 COLORFILL_ROW(BYTE);
1622 break;
1624 case 2:
1625 COLORFILL_ROW(WORD);
1626 break;
1628 case 3:
1630 BYTE *d = buf;
1631 for (x = 0; x < width; ++x, d += 3)
1633 d[0] = (color ) & 0xff;
1634 d[1] = (color >> 8) & 0xff;
1635 d[2] = (color >> 16) & 0xff;
1637 break;
1639 case 4:
1640 COLORFILL_ROW(DWORD);
1641 break;
1643 default:
1644 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
1645 return WINED3DERR_NOTAVAILABLE;
1648 #undef COLORFILL_ROW
1650 /* Now copy first row. */
1651 first = buf;
1652 for (y = 1; y < height; ++y)
1654 buf += pitch;
1655 memcpy(buf, first, width * bpp);
1658 return WINED3D_OK;
1661 static void read_from_framebuffer(struct wined3d_surface *surface,
1662 struct wined3d_context *old_ctx, DWORD dst_location)
1664 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
1665 struct wined3d_texture *texture = surface->container;
1666 struct wined3d_device *device = texture->resource.device;
1667 const struct wined3d_gl_info *gl_info;
1668 struct wined3d_context *context = old_ctx;
1669 struct wined3d_surface *restore_rt = NULL;
1670 unsigned int row_pitch, slice_pitch;
1671 unsigned int width, height;
1672 BYTE *mem;
1673 BYTE *row, *top, *bottom;
1674 int i;
1675 BOOL srcIsUpsideDown;
1676 struct wined3d_bo_address data;
1678 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
1680 restore_rt = context_get_rt_surface(old_ctx);
1681 if (restore_rt != surface)
1682 context = context_acquire(device, surface);
1683 else
1684 restore_rt = NULL;
1686 context_apply_blit_state(context, device);
1687 gl_info = context->gl_info;
1689 /* Select the correct read buffer, and give some debug output.
1690 * There is no need to keep track of the current read buffer or reset it, every part of the code
1691 * that reads sets the read buffer as desired.
1693 if (wined3d_resource_is_offscreen(&texture->resource))
1695 /* Mapping the primary render target which is not on a swapchain.
1696 * Read from the back buffer. */
1697 TRACE("Mapping offscreen render target.\n");
1698 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1699 srcIsUpsideDown = TRUE;
1701 else
1703 /* Onscreen surfaces are always part of a swapchain */
1704 GLenum buffer = wined3d_texture_get_gl_buffer(texture);
1705 TRACE("Mapping %#x buffer.\n", buffer);
1706 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
1707 checkGLcall("glReadBuffer");
1708 srcIsUpsideDown = FALSE;
1711 if (data.buffer_object)
1713 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1714 checkGLcall("glBindBuffer");
1717 wined3d_texture_get_pitch(texture, surface->texture_level, &row_pitch, &slice_pitch);
1719 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1720 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / texture->resource.format->byte_count);
1721 checkGLcall("glPixelStorei");
1723 width = wined3d_texture_get_level_width(texture, surface->texture_level);
1724 height = wined3d_texture_get_level_height(texture, surface->texture_level);
1725 gl_info->gl_ops.gl.p_glReadPixels(0, 0, width, height,
1726 texture->resource.format->glFormat,
1727 texture->resource.format->glType, data.addr);
1728 checkGLcall("glReadPixels");
1730 /* Reset previous pixel store pack state */
1731 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
1732 checkGLcall("glPixelStorei");
1734 if (!srcIsUpsideDown)
1736 /* glReadPixels returns the image upside down, and there is no way to
1737 * prevent this. Flip the lines in software. */
1739 if (!(row = HeapAlloc(GetProcessHeap(), 0, row_pitch)))
1740 goto error;
1742 if (data.buffer_object)
1744 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
1745 checkGLcall("glMapBuffer");
1747 else
1748 mem = data.addr;
1750 top = mem;
1751 bottom = mem + row_pitch * (height - 1);
1752 for (i = 0; i < height / 2; i++)
1754 memcpy(row, top, row_pitch);
1755 memcpy(top, bottom, row_pitch);
1756 memcpy(bottom, row, row_pitch);
1757 top += row_pitch;
1758 bottom -= row_pitch;
1760 HeapFree(GetProcessHeap(), 0, row);
1762 if (data.buffer_object)
1763 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
1766 error:
1767 if (data.buffer_object)
1769 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1770 checkGLcall("glBindBuffer");
1773 if (restore_rt)
1774 context_restore(context, restore_rt);
1777 /* Read the framebuffer contents into a texture. Note that this function
1778 * doesn't do any kind of flipping. Using this on an onscreen surface will
1779 * result in a flipped D3D texture.
1781 * Context activation is done by the caller. This function may temporarily
1782 * switch to a different context and restore the original one before return. */
1783 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb, struct wined3d_context *old_ctx)
1785 struct wined3d_texture *texture = surface->container;
1786 struct wined3d_device *device = texture->resource.device;
1787 const struct wined3d_gl_info *gl_info;
1788 struct wined3d_context *context = old_ctx;
1789 struct wined3d_surface *restore_rt = NULL;
1791 restore_rt = context_get_rt_surface(old_ctx);
1792 if (restore_rt != surface)
1793 context = context_acquire(device, surface);
1794 else
1795 restore_rt = NULL;
1797 gl_info = context->gl_info;
1798 device_invalidate_state(device, STATE_FRAMEBUFFER);
1800 wined3d_texture_prepare_texture(texture, context, srgb);
1801 wined3d_texture_bind_and_dirtify(texture, context, srgb);
1803 TRACE("Reading back offscreen render target %p.\n", surface);
1805 if (wined3d_resource_is_offscreen(&texture->resource))
1806 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1807 else
1808 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(texture));
1809 checkGLcall("glReadBuffer");
1811 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
1812 0, 0, 0, 0, wined3d_texture_get_level_width(texture, surface->texture_level),
1813 wined3d_texture_get_level_height(texture, surface->texture_level));
1814 checkGLcall("glCopyTexSubImage2D");
1816 if (restore_rt)
1817 context_restore(context, restore_rt);
1820 /* Does a direct frame buffer -> texture copy. Stretching is done with single
1821 * pixel copy calls. */
1822 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
1823 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
1825 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1826 struct wined3d_texture *src_texture = src_surface->container;
1827 struct wined3d_texture *dst_texture = dst_surface->container;
1828 struct wined3d_device *device = dst_texture->resource.device;
1829 const struct wined3d_gl_info *gl_info;
1830 float xrel, yrel;
1831 struct wined3d_context *context;
1832 BOOL upsidedown = FALSE;
1833 RECT dst_rect = *dst_rect_in;
1834 unsigned int src_height;
1836 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1837 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1839 if(dst_rect.top > dst_rect.bottom) {
1840 UINT tmp = dst_rect.bottom;
1841 dst_rect.bottom = dst_rect.top;
1842 dst_rect.top = tmp;
1843 upsidedown = TRUE;
1846 context = context_acquire(device, src_surface);
1847 gl_info = context->gl_info;
1848 context_apply_blit_state(context, device);
1849 wined3d_texture_load(dst_texture, context, FALSE);
1851 /* Bind the target texture */
1852 context_bind_texture(context, dst_texture->target, dst_texture->texture_rgb.name);
1853 if (wined3d_resource_is_offscreen(&src_texture->resource))
1855 TRACE("Reading from an offscreen target\n");
1856 upsidedown = !upsidedown;
1857 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1859 else
1861 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
1863 checkGLcall("glReadBuffer");
1865 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
1866 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
1868 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1870 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
1872 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1873 ERR("Texture filtering not supported in direct blit.\n");
1875 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1876 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1878 ERR("Texture filtering not supported in direct blit\n");
1881 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
1882 if (upsidedown
1883 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1884 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1886 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
1887 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1888 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
1889 src_rect->left, src_height - src_rect->bottom,
1890 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1892 else
1894 LONG row;
1895 UINT yoffset = src_height - src_rect->top + dst_rect.top - 1;
1896 /* I have to process this row by row to swap the image,
1897 * otherwise it would be upside down, so stretching in y direction
1898 * doesn't cost extra time
1900 * However, stretching in x direction can be avoided if not necessary
1902 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
1903 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1905 /* Well, that stuff works, but it's very slow.
1906 * find a better way instead
1908 LONG col;
1910 for (col = dst_rect.left; col < dst_rect.right; ++col)
1912 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1913 dst_rect.left + col /* x offset */, row /* y offset */,
1914 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
1917 else
1919 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1920 dst_rect.left /* x offset */, row /* y offset */,
1921 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
1925 checkGLcall("glCopyTexSubImage2D");
1927 context_release(context);
1929 /* The texture is now most up to date - If the surface is a render target
1930 * and has a drawable, this path is never entered. */
1931 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1932 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1935 /* Uses the hardware to stretch and flip the image */
1936 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
1937 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
1939 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1940 unsigned int src_width, src_height, src_pow2_width, src_pow2_height;
1941 struct wined3d_texture *src_texture = src_surface->container;
1942 struct wined3d_texture *dst_texture = dst_surface->container;
1943 struct wined3d_device *device = dst_texture->resource.device;
1944 GLuint src, backup = 0;
1945 float left, right, top, bottom; /* Texture coordinates */
1946 const struct wined3d_gl_info *gl_info;
1947 struct wined3d_context *context;
1948 GLenum drawBuffer = GL_BACK;
1949 GLenum offscreen_buffer;
1950 GLenum texture_target;
1951 BOOL noBackBufferBackup;
1952 BOOL src_offscreen;
1953 BOOL upsidedown = FALSE;
1954 RECT dst_rect = *dst_rect_in;
1956 TRACE("Using hwstretch blit\n");
1957 /* Activate the Proper context for reading from the source surface, set it up for blitting */
1958 context = context_acquire(device, src_surface);
1959 gl_info = context->gl_info;
1960 context_apply_blit_state(context, device);
1961 wined3d_texture_load(dst_texture, context, FALSE);
1963 offscreen_buffer = context_get_offscreen_gl_buffer(context);
1964 src_width = wined3d_texture_get_level_width(src_texture, src_surface->texture_level);
1965 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
1966 src_pow2_width = wined3d_texture_get_level_pow2_width(src_texture, src_surface->texture_level);
1967 src_pow2_height = wined3d_texture_get_level_pow2_height(src_texture, src_surface->texture_level);
1969 src_offscreen = wined3d_resource_is_offscreen(&src_texture->resource);
1970 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
1971 if (!noBackBufferBackup && !src_texture->texture_rgb.name)
1973 /* Get it a description */
1974 wined3d_texture_load(src_texture, context, FALSE);
1977 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
1978 * This way we don't have to wait for the 2nd readback to finish to leave this function.
1980 if (context->aux_buffers >= 2)
1982 /* Got more than one aux buffer? Use the 2nd aux buffer */
1983 drawBuffer = GL_AUX1;
1985 else if ((!src_offscreen || offscreen_buffer == GL_BACK) && context->aux_buffers >= 1)
1987 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
1988 drawBuffer = GL_AUX0;
1991 if (noBackBufferBackup)
1993 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
1994 checkGLcall("glGenTextures");
1995 context_bind_texture(context, GL_TEXTURE_2D, backup);
1996 texture_target = GL_TEXTURE_2D;
1998 else
2000 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2001 * we are reading from the back buffer, the backup can be used as source texture
2003 texture_target = src_surface->texture_target;
2004 context_bind_texture(context, texture_target, src_texture->texture_rgb.name);
2005 gl_info->gl_ops.gl.p_glEnable(texture_target);
2006 checkGLcall("glEnable(texture_target)");
2008 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2009 surface_get_sub_resource(src_surface)->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
2012 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2013 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2015 if(dst_rect.top > dst_rect.bottom) {
2016 UINT tmp = dst_rect.bottom;
2017 dst_rect.bottom = dst_rect.top;
2018 dst_rect.top = tmp;
2019 upsidedown = TRUE;
2022 if (src_offscreen)
2024 TRACE("Reading from an offscreen target\n");
2025 upsidedown = !upsidedown;
2026 gl_info->gl_ops.gl.p_glReadBuffer(offscreen_buffer);
2028 else
2030 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
2033 /* TODO: Only back up the part that will be overwritten */
2034 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, src_width, src_height);
2036 checkGLcall("glCopyTexSubImage2D");
2038 /* No issue with overriding these - the sampler is dirty due to blit usage */
2039 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
2040 checkGLcall("glTexParameteri");
2041 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2042 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
2043 checkGLcall("glTexParameteri");
2045 if (!src_texture->swapchain || src_texture == src_texture->swapchain->back_buffers[0])
2047 src = backup ? backup : src_texture->texture_rgb.name;
2049 else
2051 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
2052 checkGLcall("glReadBuffer(GL_FRONT)");
2054 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
2055 checkGLcall("glGenTextures(1, &src)");
2056 context_bind_texture(context, GL_TEXTURE_2D, src);
2058 /* TODO: Only copy the part that will be read. Use src_rect->left,
2059 * src_rect->bottom as origin, but with the width watch out for power
2060 * of 2 sizes. */
2061 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_pow2_width,
2062 src_pow2_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2063 checkGLcall("glTexImage2D");
2064 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, src_width, src_height);
2066 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2067 checkGLcall("glTexParameteri");
2068 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2069 checkGLcall("glTexParameteri");
2071 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
2072 checkGLcall("glReadBuffer(GL_BACK)");
2074 if (texture_target != GL_TEXTURE_2D)
2076 gl_info->gl_ops.gl.p_glDisable(texture_target);
2077 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
2078 texture_target = GL_TEXTURE_2D;
2081 checkGLcall("glEnd and previous");
2083 left = src_rect->left;
2084 right = src_rect->right;
2086 if (!upsidedown)
2088 top = src_height - src_rect->top;
2089 bottom = src_height - src_rect->bottom;
2091 else
2093 top = src_height - src_rect->bottom;
2094 bottom = src_height - src_rect->top;
2097 if (src_texture->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
2099 left /= src_pow2_width;
2100 right /= src_pow2_width;
2101 top /= src_pow2_height;
2102 bottom /= src_pow2_height;
2105 /* draw the source texture stretched and upside down. The correct surface is bound already */
2106 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2107 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2109 context_set_draw_buffer(context, drawBuffer);
2110 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
2112 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
2113 /* bottom left */
2114 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
2115 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
2117 /* top left */
2118 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
2119 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
2121 /* top right */
2122 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
2123 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2125 /* bottom right */
2126 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
2127 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
2128 gl_info->gl_ops.gl.p_glEnd();
2129 checkGLcall("glEnd and previous");
2131 if (texture_target != dst_surface->texture_target)
2133 gl_info->gl_ops.gl.p_glDisable(texture_target);
2134 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
2135 texture_target = dst_surface->texture_target;
2138 /* Now read the stretched and upside down image into the destination texture */
2139 context_bind_texture(context, texture_target, dst_texture->texture_rgb.name);
2140 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
2142 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
2143 0, 0, /* We blitted the image to the origin */
2144 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2145 checkGLcall("glCopyTexSubImage2D");
2147 if (drawBuffer == GL_BACK)
2149 /* Write the back buffer backup back. */
2150 if (backup)
2152 if (texture_target != GL_TEXTURE_2D)
2154 gl_info->gl_ops.gl.p_glDisable(texture_target);
2155 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
2156 texture_target = GL_TEXTURE_2D;
2158 context_bind_texture(context, GL_TEXTURE_2D, backup);
2160 else
2162 if (texture_target != src_surface->texture_target)
2164 gl_info->gl_ops.gl.p_glDisable(texture_target);
2165 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
2166 texture_target = src_surface->texture_target;
2168 context_bind_texture(context, src_surface->texture_target, src_texture->texture_rgb.name);
2171 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
2172 /* top left */
2173 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
2174 gl_info->gl_ops.gl.p_glVertex2i(0, src_height);
2176 /* bottom left */
2177 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)src_height / (float)src_pow2_height);
2178 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
2180 /* bottom right */
2181 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width,
2182 (float)src_height / (float)src_pow2_height);
2183 gl_info->gl_ops.gl.p_glVertex2i(src_width, 0);
2185 /* top right */
2186 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width, 0.0f);
2187 gl_info->gl_ops.gl.p_glVertex2i(src_width, src_height);
2188 gl_info->gl_ops.gl.p_glEnd();
2190 gl_info->gl_ops.gl.p_glDisable(texture_target);
2191 checkGLcall("glDisable(texture_target)");
2193 /* Cleanup */
2194 if (src != src_texture->texture_rgb.name && src != backup)
2196 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
2197 checkGLcall("glDeleteTextures(1, &src)");
2199 if (backup)
2201 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
2202 checkGLcall("glDeleteTextures(1, &backup)");
2205 if (wined3d_settings.strict_draw_ordering)
2206 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2208 context_release(context);
2210 /* The texture is now most up to date - If the surface is a render target
2211 * and has a drawable, this path is never entered. */
2212 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
2213 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
2216 /* Front buffer coordinates are always full screen coordinates, but our GL
2217 * drawable is limited to the window's client area. The sysmem and texture
2218 * copies do have the full screen size. Note that GL has a bottom-left
2219 * origin, while D3D has a top-left origin. */
2220 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
2222 struct wined3d_texture *texture = surface->container;
2223 UINT drawable_height;
2225 if (texture->swapchain && texture == texture->swapchain->front_buffer)
2227 POINT offset = {0, 0};
2228 RECT windowsize;
2230 ScreenToClient(window, &offset);
2231 OffsetRect(rect, offset.x, offset.y);
2233 GetClientRect(window, &windowsize);
2234 drawable_height = windowsize.bottom - windowsize.top;
2236 else
2238 drawable_height = wined3d_texture_get_level_height(texture, surface->texture_level);
2241 rect->top = drawable_height - rect->top;
2242 rect->bottom = drawable_height - rect->bottom;
2245 /* Context activation is done by the caller. */
2246 static void surface_blt_to_drawable(const struct wined3d_device *device,
2247 struct wined3d_context *old_ctx,
2248 enum wined3d_texture_filter_type filter, BOOL alpha_test,
2249 struct wined3d_surface *src_surface, const RECT *src_rect_in,
2250 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
2252 struct wined3d_texture *src_texture = src_surface->container;
2253 struct wined3d_texture *dst_texture = dst_surface->container;
2254 const struct wined3d_gl_info *gl_info;
2255 struct wined3d_context *context = old_ctx;
2256 struct wined3d_surface *restore_rt = NULL;
2257 RECT src_rect, dst_rect;
2259 src_rect = *src_rect_in;
2260 dst_rect = *dst_rect_in;
2262 restore_rt = context_get_rt_surface(old_ctx);
2263 if (restore_rt != dst_surface)
2264 context = context_acquire(device, dst_surface);
2265 else
2266 restore_rt = NULL;
2268 gl_info = context->gl_info;
2270 /* Make sure the surface is up-to-date. This should probably use
2271 * surface_load_location() and worry about the destination surface too,
2272 * unless we're overwriting it completely. */
2273 wined3d_texture_load(src_texture, context, FALSE);
2275 /* Activate the destination context, set it up for blitting */
2276 context_apply_blit_state(context, device);
2278 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
2279 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
2281 device->blitter->set_shader(device->blit_priv, context, src_surface, NULL);
2283 if (alpha_test)
2285 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
2286 checkGLcall("glEnable(GL_ALPHA_TEST)");
2288 /* For P8 surfaces, the alpha component contains the palette index.
2289 * Which means that the colorkey is one of the palette entries. In
2290 * other cases pixels that should be masked away have alpha set to 0. */
2291 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT)
2292 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
2293 (float)src_texture->async.src_blt_color_key.color_space_low_value / 255.0f);
2294 else
2295 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
2296 checkGLcall("glAlphaFunc");
2298 else
2300 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2301 checkGLcall("glDisable(GL_ALPHA_TEST)");
2304 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
2306 if (alpha_test)
2308 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2309 checkGLcall("glDisable(GL_ALPHA_TEST)");
2312 /* Leave the opengl state valid for blitting */
2313 device->blitter->unset_shader(context->gl_info);
2315 if (wined3d_settings.strict_draw_ordering
2316 || (dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture))
2317 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2319 if (restore_rt)
2320 context_restore(context, restore_rt);
2323 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
2325 struct wined3d_resource *resource = &s->container->resource;
2326 struct wined3d_device *device = resource->device;
2327 struct wined3d_rendertarget_view_desc view_desc;
2328 struct wined3d_rendertarget_view *view;
2329 const struct blit_shader *blitter;
2330 HRESULT hr;
2332 if (!(blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info,
2333 WINED3D_BLIT_OP_COLOR_FILL, NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format)))
2335 FIXME("No blitter is capable of performing the requested color fill operation.\n");
2336 return WINED3DERR_INVALIDCALL;
2339 view_desc.format_id = resource->format->id;
2340 view_desc.u.texture.level_idx = s->texture_level;
2341 view_desc.u.texture.layer_idx = s->texture_layer;
2342 view_desc.u.texture.layer_count = 1;
2343 if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc,
2344 resource, NULL, &wined3d_null_parent_ops, &view)))
2346 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
2347 return hr;
2350 hr = blitter->color_fill(device, view, rect, color);
2351 wined3d_rendertarget_view_decref(view);
2353 return hr;
2356 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
2357 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
2358 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
2360 struct wined3d_texture *dst_texture = dst_surface->container;
2361 struct wined3d_device *device = dst_texture->resource.device;
2362 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
2363 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
2364 struct wined3d_texture *src_texture;
2366 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
2367 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
2368 flags, fx, debug_d3dtexturefiltertype(filter));
2370 /* Get the swapchain. One of the surfaces has to be a primary surface */
2371 if (dst_texture->resource.pool == WINED3D_POOL_SYSTEM_MEM)
2373 WARN("Destination is in sysmem, rejecting gl blt\n");
2374 return WINED3DERR_INVALIDCALL;
2377 dst_swapchain = dst_texture->swapchain;
2379 if (src_surface)
2381 src_texture = src_surface->container;
2382 if (src_texture->resource.pool == WINED3D_POOL_SYSTEM_MEM)
2384 WARN("Src is in sysmem, rejecting gl blt\n");
2385 return WINED3DERR_INVALIDCALL;
2388 src_swapchain = src_texture->swapchain;
2390 else
2392 src_texture = NULL;
2393 src_swapchain = NULL;
2396 /* Early sort out of cases where no render target is used */
2397 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
2399 TRACE("No surface is render target, not using hardware blit.\n");
2400 return WINED3DERR_INVALIDCALL;
2403 /* No destination color keying supported */
2404 if (flags & (WINED3D_BLT_DST_CKEY | WINED3D_BLT_DST_CKEY_OVERRIDE))
2406 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2407 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2408 return WINED3DERR_INVALIDCALL;
2411 if (dst_swapchain && dst_swapchain == src_swapchain)
2413 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
2414 return WINED3DERR_INVALIDCALL;
2417 if (dst_swapchain && src_swapchain)
2419 FIXME("Implement hardware blit between two different swapchains\n");
2420 return WINED3DERR_INVALIDCALL;
2423 if (dst_swapchain)
2425 /* Handled with regular texture -> swapchain blit */
2426 if (src_surface == rt)
2427 TRACE("Blit from active render target to a swapchain\n");
2429 else if (src_swapchain && dst_surface == rt)
2431 FIXME("Implement blit from a swapchain to the active render target\n");
2432 return WINED3DERR_INVALIDCALL;
2435 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
2437 unsigned int src_width, src_height;
2438 /* Blit from render target to texture */
2439 BOOL stretchx;
2441 /* P8 read back is not implemented */
2442 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT
2443 || dst_texture->resource.format->id == WINED3DFMT_P8_UINT)
2445 TRACE("P8 read back not supported by frame buffer to texture blit\n");
2446 return WINED3DERR_INVALIDCALL;
2449 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_SRC_CKEY_OVERRIDE))
2451 TRACE("Color keying not supported by frame buffer to texture blit\n");
2452 return WINED3DERR_INVALIDCALL;
2453 /* Destination color key is checked above */
2456 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
2457 stretchx = TRUE;
2458 else
2459 stretchx = FALSE;
2461 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2462 * flip the image nor scale it.
2464 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2465 * -> If the app wants an image width an unscaled width, copy it line per line
2466 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
2467 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2468 * back buffer. This is slower than reading line per line, thus not used for flipping
2469 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2470 * pixel by pixel. */
2471 src_width = wined3d_texture_get_level_width(src_texture, src_surface->texture_level);
2472 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
2473 if (!stretchx || dst_rect->right - dst_rect->left > src_width
2474 || dst_rect->bottom - dst_rect->top > src_height)
2476 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
2477 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
2479 else
2481 TRACE("Using hardware stretching to flip / stretch the texture.\n");
2482 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
2485 return WINED3D_OK;
2488 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
2489 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
2490 return WINED3DERR_INVALIDCALL;
2493 /* Context activation is done by the caller. */
2494 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
2495 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
2497 struct wined3d_device *device = surface->container->resource.device;
2498 const struct wined3d_gl_info *gl_info = context->gl_info;
2499 GLint compare_mode = GL_NONE;
2500 struct blt_info info;
2501 GLint old_binding = 0;
2502 RECT rect;
2504 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
2506 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
2507 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
2508 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2509 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
2510 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
2511 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
2512 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
2513 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
2514 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
2515 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
2516 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
2518 SetRect(&rect, 0, h, w, 0);
2519 surface_get_blt_info(target, &rect,
2520 wined3d_texture_get_level_pow2_width(surface->container, surface->texture_level),
2521 wined3d_texture_get_level_pow2_height(surface->container, surface->texture_level), &info);
2522 context_active_texture(context, context->gl_info, 0);
2523 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
2524 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
2525 if (gl_info->supported[ARB_SHADOW])
2527 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
2528 if (compare_mode != GL_NONE)
2529 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
2532 device->shader_backend->shader_select_depth_blt(device->shader_priv,
2533 gl_info, info.tex_type, &surface->ds_current_size);
2535 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
2536 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
2537 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
2538 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
2539 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
2540 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
2541 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
2542 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
2543 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
2544 gl_info->gl_ops.gl.p_glEnd();
2546 if (compare_mode != GL_NONE)
2547 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
2548 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
2550 gl_info->gl_ops.gl.p_glPopAttrib();
2552 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
2555 void surface_modify_ds_location(struct wined3d_surface *surface,
2556 DWORD location, UINT w, UINT h)
2558 struct wined3d_texture *texture = surface->container;
2559 unsigned int sub_resource_idx;
2561 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
2563 sub_resource_idx = surface_get_sub_resource_idx(surface);
2564 surface->ds_current_size.cx = w;
2565 surface->ds_current_size.cy = h;
2566 wined3d_texture_validate_location(texture, sub_resource_idx, location);
2567 wined3d_texture_invalidate_location(texture, sub_resource_idx, ~location);
2570 /* Context activation is done by the caller. */
2571 static void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
2573 struct wined3d_texture *texture = surface->container;
2574 struct wined3d_device *device = texture->resource.device;
2575 const struct wined3d_gl_info *gl_info = context->gl_info;
2576 GLsizei w, h;
2578 TRACE("surface %p, context %p, new location %#x.\n", surface, context, location);
2580 /* TODO: Make this work for modes other than FBO */
2581 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
2583 if (!(surface_get_sub_resource(surface)->locations & location))
2585 w = surface->ds_current_size.cx;
2586 h = surface->ds_current_size.cy;
2587 surface->ds_current_size.cx = 0;
2588 surface->ds_current_size.cy = 0;
2590 else
2592 w = wined3d_texture_get_level_width(texture, surface->texture_level);
2593 h = wined3d_texture_get_level_height(texture, surface->texture_level);
2596 if (surface->current_renderbuffer)
2598 FIXME("Not supported with fixed up depth stencil.\n");
2599 return;
2602 wined3d_surface_prepare(surface, context, location);
2604 if (location == WINED3D_LOCATION_TEXTURE_RGB)
2606 GLint old_binding = 0;
2607 GLenum bind_target;
2609 /* The render target is allowed to be smaller than the depth/stencil
2610 * buffer, so the onscreen depth/stencil buffer is potentially smaller
2611 * than the offscreen surface. Don't overwrite the offscreen surface
2612 * with undefined data. */
2613 w = min(w, context->swapchain->desc.backbuffer_width);
2614 h = min(h, context->swapchain->desc.backbuffer_height);
2616 TRACE("Copying onscreen depth buffer to depth texture.\n");
2618 if (!device->depth_blt_texture)
2619 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
2621 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
2622 * directly on the FBO texture. That's because we need to flip. */
2623 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
2624 context->swapchain->front_buffer->sub_resources[0].u.surface,
2625 NULL, WINED3D_LOCATION_DRAWABLE);
2626 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2628 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
2629 bind_target = GL_TEXTURE_RECTANGLE_ARB;
2631 else
2633 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
2634 bind_target = GL_TEXTURE_2D;
2636 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
2637 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
2638 * internal format, because the internal format might include stencil
2639 * data. In principle we should copy stencil data as well, but unless
2640 * the driver supports stencil export it's hard to do, and doesn't
2641 * seem to be needed in practice. If the hardware doesn't support
2642 * writing stencil data, the glCopyTexImage2D() call might trigger
2643 * software fallbacks. */
2644 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
2645 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2646 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2647 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2648 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2649 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2650 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
2652 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
2653 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
2654 context_set_draw_buffer(context, GL_NONE);
2656 /* Do the actual blit */
2657 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
2658 checkGLcall("depth_blt");
2660 context_invalidate_state(context, STATE_FRAMEBUFFER);
2662 if (wined3d_settings.strict_draw_ordering)
2663 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2665 else if (location == WINED3D_LOCATION_DRAWABLE)
2667 TRACE("Copying depth texture to onscreen depth buffer.\n");
2669 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
2670 context->swapchain->front_buffer->sub_resources[0].u.surface,
2671 NULL, WINED3D_LOCATION_DRAWABLE);
2672 surface_depth_blt(surface, context, texture->texture_rgb.name, 0,
2673 wined3d_texture_get_level_pow2_height(texture, surface->texture_level) - h,
2674 w, h, surface->texture_target);
2675 checkGLcall("depth_blt");
2677 context_invalidate_state(context, STATE_FRAMEBUFFER);
2679 if (wined3d_settings.strict_draw_ordering)
2680 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2682 else
2684 ERR("Invalid location (%#x) specified.\n", location);
2688 static DWORD resource_access_from_location(DWORD location)
2690 switch (location)
2692 case WINED3D_LOCATION_SYSMEM:
2693 case WINED3D_LOCATION_USER_MEMORY:
2694 case WINED3D_LOCATION_BUFFER:
2695 return WINED3D_RESOURCE_ACCESS_CPU;
2697 case WINED3D_LOCATION_DRAWABLE:
2698 case WINED3D_LOCATION_TEXTURE_SRGB:
2699 case WINED3D_LOCATION_TEXTURE_RGB:
2700 case WINED3D_LOCATION_RB_MULTISAMPLE:
2701 case WINED3D_LOCATION_RB_RESOLVED:
2702 return WINED3D_RESOURCE_ACCESS_GPU;
2704 default:
2705 FIXME("Unhandled location %#x.\n", location);
2706 return 0;
2710 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
2712 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2713 struct wined3d_texture *texture = surface->container;
2714 struct wined3d_device *device = texture->resource.device;
2715 struct wined3d_texture_sub_resource *sub_resource;
2716 struct wined3d_context *context;
2717 const struct wined3d_gl_info *gl_info;
2718 struct wined3d_bo_address dst, src;
2720 sub_resource = &texture->sub_resources[sub_resource_idx];
2721 wined3d_texture_get_memory(texture, sub_resource_idx, &dst, location);
2722 wined3d_texture_get_memory(texture, sub_resource_idx, &src, sub_resource->locations);
2724 if (dst.buffer_object)
2726 context = context_acquire(device, NULL);
2727 gl_info = context->gl_info;
2728 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, dst.buffer_object));
2729 GL_EXTCALL(glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, sub_resource->size, src.addr));
2730 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
2731 checkGLcall("Upload PBO");
2732 context_release(context);
2733 return;
2735 if (src.buffer_object)
2737 context = context_acquire(device, NULL);
2738 gl_info = context->gl_info;
2739 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, src.buffer_object));
2740 GL_EXTCALL(glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, sub_resource->size, dst.addr));
2741 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
2742 checkGLcall("Download PBO");
2743 context_release(context);
2744 return;
2746 memcpy(dst.addr, src.addr, sub_resource->size);
2749 /* Context activation is done by the caller. */
2750 static void surface_load_sysmem(struct wined3d_surface *surface,
2751 struct wined3d_context *context, DWORD dst_location)
2753 const struct wined3d_gl_info *gl_info = context->gl_info;
2754 struct wined3d_texture_sub_resource *sub_resource;
2756 wined3d_surface_prepare(surface, context, dst_location);
2758 sub_resource = surface_get_sub_resource(surface);
2759 if (sub_resource->locations & surface_simple_locations)
2761 surface_copy_simple_location(surface, dst_location);
2762 return;
2765 if (sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
2766 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
2768 /* Download the surface to system memory. */
2769 if (sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
2771 struct wined3d_texture *texture = surface->container;
2773 wined3d_texture_bind_and_dirtify(texture, context,
2774 !(sub_resource->locations & WINED3D_LOCATION_TEXTURE_RGB));
2775 surface_download_data(surface, gl_info, dst_location);
2776 ++texture->download_count;
2778 return;
2781 if (sub_resource->locations & WINED3D_LOCATION_DRAWABLE)
2783 read_from_framebuffer(surface, context, dst_location);
2784 return;
2787 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
2788 surface, wined3d_debug_location(sub_resource->locations));
2791 /* Context activation is done by the caller. */
2792 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
2793 struct wined3d_context *context)
2795 struct wined3d_texture *texture = surface->container;
2796 RECT r;
2798 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
2799 && wined3d_resource_is_offscreen(&texture->resource))
2801 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
2802 return WINED3DERR_INVALIDCALL;
2805 surface_get_rect(surface, NULL, &r);
2806 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
2807 surface_blt_to_drawable(texture->resource.device, context,
2808 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
2810 return WINED3D_OK;
2813 static HRESULT surface_load_texture(struct wined3d_surface *surface,
2814 struct wined3d_context *context, BOOL srgb)
2816 unsigned int width, height, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch;
2817 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2818 const struct wined3d_gl_info *gl_info = context->gl_info;
2819 struct wined3d_texture *texture = surface->container;
2820 struct wined3d_device *device = texture->resource.device;
2821 const struct wined3d_color_key_conversion *conversion;
2822 struct wined3d_texture_sub_resource *sub_resource;
2823 struct wined3d_bo_address data;
2824 BYTE *src_mem, *dst_mem = NULL;
2825 struct wined3d_format format;
2826 POINT dst_point = {0, 0};
2827 RECT src_rect;
2829 sub_resource = surface_get_sub_resource(surface);
2830 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
2831 && wined3d_resource_is_offscreen(&texture->resource)
2832 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
2834 surface_load_fb_texture(surface, srgb, context);
2836 return WINED3D_OK;
2839 width = wined3d_texture_get_level_width(texture, surface->texture_level);
2840 height = wined3d_texture_get_level_height(texture, surface->texture_level);
2841 SetRect(&src_rect, 0, 0, width, height);
2843 if (sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
2844 && (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
2845 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
2846 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format,
2847 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format))
2849 if (srgb)
2850 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
2851 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
2852 else
2853 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
2854 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
2856 return WINED3D_OK;
2859 if (sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
2860 && (!srgb || (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
2861 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
2862 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format,
2863 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format))
2865 DWORD src_location = sub_resource->locations & WINED3D_LOCATION_RB_RESOLVED ?
2866 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
2867 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2869 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, src_location,
2870 &src_rect, surface, dst_location, &src_rect);
2872 return WINED3D_OK;
2875 /* Upload from system memory */
2877 if (srgb)
2879 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | texture->resource.map_binding))
2880 == WINED3D_LOCATION_TEXTURE_RGB)
2882 /* Performance warning... */
2883 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
2884 surface_load_location(surface, context, texture->resource.map_binding);
2887 else
2889 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | texture->resource.map_binding))
2890 == WINED3D_LOCATION_TEXTURE_SRGB)
2892 /* Performance warning... */
2893 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
2894 surface_load_location(surface, context, texture->resource.map_binding);
2898 if (!(sub_resource->locations & surface_simple_locations))
2900 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
2901 /* Lets hope we get it from somewhere... */
2902 surface_load_location(surface, context, WINED3D_LOCATION_SYSMEM);
2905 wined3d_texture_prepare_texture(texture, context, srgb);
2906 wined3d_texture_bind_and_dirtify(texture, context, srgb);
2907 wined3d_texture_get_pitch(texture, surface->texture_level, &src_row_pitch, &src_slice_pitch);
2909 format = *texture->resource.format;
2910 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
2911 format = *wined3d_get_format(gl_info, conversion->dst_format);
2913 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
2914 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
2915 * getting called. */
2916 if ((format.convert || conversion) && texture->sub_resources[sub_resource_idx].buffer_object)
2918 TRACE("Removing the pbo attached to surface %p.\n", surface);
2920 surface_load_location(surface, context, WINED3D_LOCATION_SYSMEM);
2921 wined3d_texture_set_map_binding(texture, WINED3D_LOCATION_SYSMEM);
2924 wined3d_texture_get_memory(texture, sub_resource_idx, &data, sub_resource->locations);
2925 if (format.convert)
2927 /* This code is entered for texture formats which need a fixup. */
2928 format.byte_count = format.conv_byte_count;
2929 wined3d_format_calculate_pitch(&format, 1, width, height, &dst_row_pitch, &dst_slice_pitch);
2931 src_mem = wined3d_texture_map_bo_address(&data, src_slice_pitch,
2932 gl_info, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READONLY);
2933 if (!(dst_mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
2935 ERR("Out of memory (%u).\n", dst_slice_pitch);
2936 context_release(context);
2937 return E_OUTOFMEMORY;
2939 format.convert(src_mem, dst_mem, src_row_pitch, src_slice_pitch,
2940 dst_row_pitch, dst_slice_pitch, width, height, 1);
2941 src_row_pitch = dst_row_pitch;
2942 wined3d_texture_unmap_bo_address(&data, gl_info, GL_PIXEL_UNPACK_BUFFER);
2944 data.buffer_object = 0;
2945 data.addr = dst_mem;
2947 else if (conversion)
2949 /* This code is only entered for color keying fixups */
2950 struct wined3d_palette *palette = NULL;
2952 wined3d_format_calculate_pitch(&format, device->surface_alignment,
2953 width, height, &dst_row_pitch, &dst_slice_pitch);
2955 src_mem = wined3d_texture_map_bo_address(&data, src_slice_pitch,
2956 gl_info, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READONLY);
2957 if (!(dst_mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
2959 ERR("Out of memory (%u).\n", dst_slice_pitch);
2960 context_release(context);
2961 return E_OUTOFMEMORY;
2963 if (texture->swapchain && texture->swapchain->palette)
2964 palette = texture->swapchain->palette;
2965 conversion->convert(src_mem, src_row_pitch, dst_mem, dst_row_pitch,
2966 width, height, palette, &texture->async.gl_color_key);
2967 src_row_pitch = dst_row_pitch;
2968 wined3d_texture_unmap_bo_address(&data, gl_info, GL_PIXEL_UNPACK_BUFFER);
2970 data.buffer_object = 0;
2971 data.addr = dst_mem;
2974 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
2975 src_row_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
2977 HeapFree(GetProcessHeap(), 0, dst_mem);
2979 return WINED3D_OK;
2982 /* Context activation is done by the caller. */
2983 static void surface_load_renderbuffer(struct wined3d_surface *surface, struct wined3d_context *context,
2984 DWORD dst_location)
2986 struct wined3d_texture *texture = surface->container;
2987 const RECT rect = {0, 0,
2988 wined3d_texture_get_level_width(texture, surface->texture_level),
2989 wined3d_texture_get_level_height(texture, surface->texture_level)};
2990 DWORD locations = surface_get_sub_resource(surface)->locations;
2991 DWORD src_location;
2993 if (locations & WINED3D_LOCATION_RB_MULTISAMPLE)
2994 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
2995 else if (locations & WINED3D_LOCATION_RB_RESOLVED)
2996 src_location = WINED3D_LOCATION_RB_RESOLVED;
2997 else if (locations & WINED3D_LOCATION_TEXTURE_SRGB)
2998 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
2999 else /* surface_blt_fbo will load the source location if necessary. */
3000 src_location = WINED3D_LOCATION_TEXTURE_RGB;
3002 surface_blt_fbo(texture->resource.device, context, WINED3D_TEXF_POINT,
3003 surface, src_location, &rect, surface, dst_location, &rect);
3006 /* Context activation is done by the caller. Context may be NULL in ddraw-only mode. */
3007 HRESULT surface_load_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3009 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
3010 struct wined3d_texture *texture = surface->container;
3011 struct wined3d_texture_sub_resource *sub_resource;
3012 unsigned int surface_w, surface_h;
3013 HRESULT hr;
3015 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3017 surface_w = wined3d_texture_get_level_width(texture, surface->texture_level);
3018 surface_h = wined3d_texture_get_level_height(texture, surface->texture_level);
3020 sub_resource = &texture->sub_resources[sub_resource_idx];
3021 if (sub_resource->locations & location && (!(texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
3022 || (surface->ds_current_size.cx == surface_w && surface->ds_current_size.cy == surface_h)))
3024 TRACE("Location (%#x) is already up to date.\n", location);
3025 return WINED3D_OK;
3028 if (WARN_ON(d3d))
3030 DWORD required_access = resource_access_from_location(location);
3031 if ((texture->resource.access_flags & required_access) != required_access)
3032 WARN("Operation requires %#x access, but surface only has %#x.\n",
3033 required_access, texture->resource.access_flags);
3036 if (sub_resource->locations & WINED3D_LOCATION_DISCARDED)
3038 TRACE("Surface previously discarded, nothing to do.\n");
3039 wined3d_surface_prepare(surface, context, location);
3040 wined3d_texture_validate_location(texture, sub_resource_idx, location);
3041 wined3d_texture_invalidate_location(texture, sub_resource_idx, WINED3D_LOCATION_DISCARDED);
3042 goto done;
3045 if (!sub_resource->locations)
3047 ERR("Surface %p does not have any up to date location.\n", surface);
3048 wined3d_texture_validate_location(texture, sub_resource_idx, WINED3D_LOCATION_DISCARDED);
3049 return surface_load_location(surface, context, location);
3052 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
3054 if ((location == WINED3D_LOCATION_TEXTURE_RGB && sub_resource->locations & WINED3D_LOCATION_DRAWABLE)
3055 || (location == WINED3D_LOCATION_DRAWABLE && sub_resource->locations & WINED3D_LOCATION_TEXTURE_RGB))
3057 surface_load_ds_location(surface, context, location);
3058 goto done;
3061 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
3062 wined3d_debug_location(sub_resource->locations), wined3d_debug_location(location));
3063 return WINED3DERR_INVALIDCALL;
3066 switch (location)
3068 case WINED3D_LOCATION_USER_MEMORY:
3069 case WINED3D_LOCATION_SYSMEM:
3070 case WINED3D_LOCATION_BUFFER:
3071 surface_load_sysmem(surface, context, location);
3072 break;
3074 case WINED3D_LOCATION_DRAWABLE:
3075 if (FAILED(hr = surface_load_drawable(surface, context)))
3076 return hr;
3077 break;
3079 case WINED3D_LOCATION_RB_RESOLVED:
3080 case WINED3D_LOCATION_RB_MULTISAMPLE:
3081 surface_load_renderbuffer(surface, context, location);
3082 break;
3084 case WINED3D_LOCATION_TEXTURE_RGB:
3085 case WINED3D_LOCATION_TEXTURE_SRGB:
3086 if (FAILED(hr = surface_load_texture(surface, context,
3087 location == WINED3D_LOCATION_TEXTURE_SRGB)))
3088 return hr;
3089 break;
3091 default:
3092 ERR("Don't know how to handle location %#x.\n", location);
3093 break;
3096 done:
3097 wined3d_texture_validate_location(texture, sub_resource_idx, location);
3099 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
3101 surface->ds_current_size.cx = surface_w;
3102 surface->ds_current_size.cy = surface_h;
3105 return WINED3D_OK;
3108 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
3109 /* Context activation is done by the caller. */
3110 static void ffp_blit_free(struct wined3d_device *device) { }
3112 /* Context activation is done by the caller. */
3113 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
3114 const struct wined3d_color_key *color_key)
3116 const struct wined3d_gl_info *gl_info = context->gl_info;
3118 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
3119 checkGLcall("glEnable(target)");
3121 return WINED3D_OK;
3124 /* Context activation is done by the caller. */
3125 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
3127 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
3128 checkGLcall("glDisable(GL_TEXTURE_2D)");
3129 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
3131 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3132 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3134 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
3136 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
3137 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
3141 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info,
3142 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
3143 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
3144 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
3146 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
3148 TRACE("Source or destination is in system memory.\n");
3149 return FALSE;
3152 switch (blit_op)
3154 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
3155 if (d3d_info->shader_color_key)
3157 TRACE("Color keying requires converted textures.\n");
3158 return FALSE;
3160 case WINED3D_BLIT_OP_COLOR_BLIT:
3161 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
3162 if (TRACE_ON(d3d))
3164 TRACE("Checking support for fixup:\n");
3165 dump_color_fixup_desc(src_format->color_fixup);
3168 /* We only support identity conversions. */
3169 if (!is_identity_fixup(src_format->color_fixup)
3170 || !is_identity_fixup(dst_format->color_fixup))
3172 TRACE("Fixups are not supported.\n");
3173 return FALSE;
3176 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
3178 TRACE("Can only blit to render targets.\n");
3179 return FALSE;
3181 return TRUE;
3183 case WINED3D_BLIT_OP_COLOR_FILL:
3184 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
3186 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
3187 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
3188 return FALSE;
3190 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
3192 TRACE("Color fill not supported\n");
3193 return FALSE;
3196 /* FIXME: We should reject color fills on formats with fixups,
3197 * but this would break P8 color fills for example. */
3199 return TRUE;
3201 case WINED3D_BLIT_OP_DEPTH_FILL:
3202 return TRUE;
3204 default:
3205 TRACE("Unsupported blit_op=%d\n", blit_op);
3206 return FALSE;
3210 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
3211 const RECT *rect, const struct wined3d_color *color)
3213 const RECT draw_rect = {0, 0, view->width, view->height};
3214 struct wined3d_fb_state fb = {&view, NULL};
3216 device_clear_render_targets(device, 1, &fb, 1, rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
3218 return WINED3D_OK;
3221 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
3222 struct wined3d_rendertarget_view *view, const RECT *rect, DWORD clear_flags,
3223 float depth, DWORD stencil)
3225 const RECT draw_rect = {0, 0, view->width, view->height};
3226 struct wined3d_fb_state fb = {NULL, view};
3228 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, clear_flags, NULL, depth, stencil);
3230 return WINED3D_OK;
3233 static void ffp_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
3234 struct wined3d_surface *src_surface, const RECT *src_rect,
3235 struct wined3d_surface *dst_surface, const RECT *dst_rect,
3236 const struct wined3d_color_key *color_key)
3238 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
3239 struct wined3d_texture *dst_texture = dst_surface->container;
3240 struct wined3d_texture *src_texture = src_surface->container;
3241 struct wined3d_context *context;
3243 /* Blit from offscreen surface to render target */
3244 struct wined3d_color_key old_blt_key = src_texture->async.src_blt_color_key;
3245 DWORD old_color_key_flags = src_texture->async.color_key_flags;
3247 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
3249 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT, color_key);
3251 context = context_acquire(device, dst_surface);
3253 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
3254 glEnable(GL_ALPHA_TEST);
3256 surface_blt_to_drawable(device, context, filter,
3257 !!color_key, src_surface, src_rect, dst_surface, dst_rect);
3259 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
3260 glDisable(GL_ALPHA_TEST);
3262 context_release(context);
3264 /* Restore the color key parameters */
3265 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT,
3266 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
3268 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, dst_texture->resource.draw_binding);
3269 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~dst_texture->resource.draw_binding);
3272 const struct blit_shader ffp_blit = {
3273 ffp_blit_alloc,
3274 ffp_blit_free,
3275 ffp_blit_set,
3276 ffp_blit_unset,
3277 ffp_blit_supported,
3278 ffp_blit_color_fill,
3279 ffp_blit_depth_fill,
3280 ffp_blit_blit_surface,
3283 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
3285 return WINED3D_OK;
3288 /* Context activation is done by the caller. */
3289 static void cpu_blit_free(struct wined3d_device *device)
3293 /* Context activation is done by the caller. */
3294 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
3295 const struct wined3d_color_key *color_key)
3297 return WINED3D_OK;
3300 /* Context activation is done by the caller. */
3301 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
3305 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info,
3306 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
3307 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
3308 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
3310 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
3312 return TRUE;
3315 return FALSE;
3318 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
3319 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
3320 const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx)
3322 UINT row_block_count;
3323 const BYTE *src_row;
3324 BYTE *dst_row;
3325 UINT x, y;
3327 src_row = src_data;
3328 dst_row = dst_data;
3330 row_block_count = (update_w + format->block_width - 1) / format->block_width;
3332 if (!flags)
3334 for (y = 0; y < update_h; y += format->block_height)
3336 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
3337 src_row += src_pitch;
3338 dst_row += dst_pitch;
3341 return WINED3D_OK;
3344 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
3346 src_row += (((update_h / format->block_height) - 1) * src_pitch);
3348 switch (format->id)
3350 case WINED3DFMT_DXT1:
3351 for (y = 0; y < update_h; y += format->block_height)
3353 struct block
3355 WORD color[2];
3356 BYTE control_row[4];
3359 const struct block *s = (const struct block *)src_row;
3360 struct block *d = (struct block *)dst_row;
3362 for (x = 0; x < row_block_count; ++x)
3364 d[x].color[0] = s[x].color[0];
3365 d[x].color[1] = s[x].color[1];
3366 d[x].control_row[0] = s[x].control_row[3];
3367 d[x].control_row[1] = s[x].control_row[2];
3368 d[x].control_row[2] = s[x].control_row[1];
3369 d[x].control_row[3] = s[x].control_row[0];
3371 src_row -= src_pitch;
3372 dst_row += dst_pitch;
3374 return WINED3D_OK;
3376 case WINED3DFMT_DXT2:
3377 case WINED3DFMT_DXT3:
3378 for (y = 0; y < update_h; y += format->block_height)
3380 struct block
3382 WORD alpha_row[4];
3383 WORD color[2];
3384 BYTE control_row[4];
3387 const struct block *s = (const struct block *)src_row;
3388 struct block *d = (struct block *)dst_row;
3390 for (x = 0; x < row_block_count; ++x)
3392 d[x].alpha_row[0] = s[x].alpha_row[3];
3393 d[x].alpha_row[1] = s[x].alpha_row[2];
3394 d[x].alpha_row[2] = s[x].alpha_row[1];
3395 d[x].alpha_row[3] = s[x].alpha_row[0];
3396 d[x].color[0] = s[x].color[0];
3397 d[x].color[1] = s[x].color[1];
3398 d[x].control_row[0] = s[x].control_row[3];
3399 d[x].control_row[1] = s[x].control_row[2];
3400 d[x].control_row[2] = s[x].control_row[1];
3401 d[x].control_row[3] = s[x].control_row[0];
3403 src_row -= src_pitch;
3404 dst_row += dst_pitch;
3406 return WINED3D_OK;
3408 default:
3409 FIXME("Compressed flip not implemented for format %s.\n",
3410 debug_d3dformat(format->id));
3411 return E_NOTIMPL;
3415 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
3416 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
3418 return E_NOTIMPL;
3421 static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
3422 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
3423 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
3424 enum wined3d_texture_filter_type filter)
3426 unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count;
3427 const struct wined3d_format *src_format, *dst_format;
3428 struct wined3d_texture *converted_texture = NULL;
3429 unsigned int src_fmt_flags, dst_fmt_flags;
3430 struct wined3d_map_desc dst_map, src_map;
3431 const BYTE *sbase = NULL;
3432 HRESULT hr = WINED3D_OK;
3433 BOOL same_sub_resource;
3434 const BYTE *sbuf;
3435 BYTE *dbuf;
3436 int x, y;
3438 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
3439 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
3440 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture,
3441 src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
3443 if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx)
3445 same_sub_resource = TRUE;
3446 wined3d_resource_map(&dst_texture->resource, dst_sub_resource_idx, &dst_map, NULL, 0);
3447 src_map = dst_map;
3448 src_format = dst_texture->resource.format;
3449 dst_format = src_format;
3450 dst_fmt_flags = dst_texture->resource.format_flags;
3451 src_fmt_flags = dst_fmt_flags;
3453 else
3455 same_sub_resource = FALSE;
3456 dst_format = dst_texture->resource.format;
3457 dst_fmt_flags = dst_texture->resource.format_flags;
3458 if (src_texture)
3460 if (dst_texture->resource.format->id != src_texture->resource.format->id)
3462 if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format)))
3464 FIXME("Cannot convert %s to %s.\n", debug_d3dformat(src_texture->resource.format->id),
3465 debug_d3dformat(dst_texture->resource.format->id));
3466 return WINED3DERR_NOTAVAILABLE;
3468 src_texture = converted_texture;
3469 src_sub_resource_idx = 0;
3471 wined3d_resource_map(&src_texture->resource, src_sub_resource_idx, &src_map, NULL, WINED3D_MAP_READONLY);
3472 src_format = src_texture->resource.format;
3473 src_fmt_flags = src_texture->resource.format_flags;
3475 else
3477 src_format = dst_format;
3478 src_fmt_flags = dst_fmt_flags;
3481 wined3d_resource_map(&dst_texture->resource, dst_sub_resource_idx, &dst_map, dst_box, 0);
3484 bpp = dst_format->byte_count;
3485 src_height = src_box->bottom - src_box->top;
3486 src_width = src_box->right - src_box->left;
3487 dst_height = dst_box->bottom - dst_box->top;
3488 dst_width = dst_box->right - dst_box->left;
3489 row_byte_count = dst_width * bpp;
3491 if (src_texture)
3492 sbase = (BYTE *)src_map.data
3493 + ((src_box->top / src_format->block_height) * src_map.row_pitch)
3494 + ((src_box->left / src_format->block_width) * src_format->block_byte_count);
3495 if (same_sub_resource)
3496 dbuf = (BYTE *)dst_map.data
3497 + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch)
3498 + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count);
3499 else
3500 dbuf = dst_map.data;
3502 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
3504 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
3506 if (same_sub_resource)
3508 FIXME("Only plain blits supported on compressed surfaces.\n");
3509 hr = E_NOTIMPL;
3510 goto release;
3513 if (src_height != dst_height || src_width != dst_width)
3515 WARN("Stretching not supported on compressed surfaces.\n");
3516 hr = WINED3DERR_INVALIDCALL;
3517 goto release;
3520 if (!wined3d_texture_check_block_align(src_texture,
3521 src_sub_resource_idx % src_texture->level_count, src_box))
3523 WARN("Source rectangle not block-aligned.\n");
3524 hr = WINED3DERR_INVALIDCALL;
3525 goto release;
3528 if (!wined3d_texture_check_block_align(dst_texture,
3529 dst_sub_resource_idx % dst_texture->level_count, dst_box))
3531 WARN("Destination rectangle not block-aligned.\n");
3532 hr = WINED3DERR_INVALIDCALL;
3533 goto release;
3536 hr = surface_cpu_blt_compressed(sbase, dbuf,
3537 src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height,
3538 src_format, flags, fx);
3539 goto release;
3542 /* First, all the 'source-less' blits */
3543 if (flags & WINED3D_BLT_COLOR_FILL)
3545 hr = _Blt_ColorFill(dbuf, dst_width, dst_height, bpp, dst_map.row_pitch, fx->fill_color);
3546 flags &= ~WINED3D_BLT_COLOR_FILL;
3549 if (flags & WINED3D_BLT_DEPTH_FILL)
3550 FIXME("WINED3D_BLT_DEPTH_FILL needs to be implemented!\n");
3552 /* Now the 'with source' blits. */
3553 if (src_texture)
3555 int sx, xinc, sy, yinc;
3557 if (!dst_width || !dst_height) /* Hmm... stupid program? */
3558 goto release;
3560 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
3561 && (src_width != dst_width || src_height != dst_height))
3563 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
3564 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
3567 xinc = (src_width << 16) / dst_width;
3568 yinc = (src_height << 16) / dst_height;
3570 if (!flags)
3572 /* No effects, we can cheat here. */
3573 if (dst_width == src_width)
3575 if (dst_height == src_height)
3577 /* No stretching in either direction. This needs to be as
3578 * fast as possible. */
3579 sbuf = sbase;
3581 /* Check for overlapping surfaces. */
3582 if (!same_sub_resource || dst_box->top < src_box->top
3583 || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
3585 /* No overlap, or dst above src, so copy from top downwards. */
3586 for (y = 0; y < dst_height; ++y)
3588 memcpy(dbuf, sbuf, row_byte_count);
3589 sbuf += src_map.row_pitch;
3590 dbuf += dst_map.row_pitch;
3593 else if (dst_box->top > src_box->top)
3595 /* Copy from bottom upwards. */
3596 sbuf += src_map.row_pitch * dst_height;
3597 dbuf += dst_map.row_pitch * dst_height;
3598 for (y = 0; y < dst_height; ++y)
3600 sbuf -= src_map.row_pitch;
3601 dbuf -= dst_map.row_pitch;
3602 memcpy(dbuf, sbuf, row_byte_count);
3605 else
3607 /* Src and dst overlapping on the same line, use memmove. */
3608 for (y = 0; y < dst_height; ++y)
3610 memmove(dbuf, sbuf, row_byte_count);
3611 sbuf += src_map.row_pitch;
3612 dbuf += dst_map.row_pitch;
3616 else
3618 /* Stretching in y direction only. */
3619 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3621 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3622 memcpy(dbuf, sbuf, row_byte_count);
3623 dbuf += dst_map.row_pitch;
3627 else
3629 /* Stretching in X direction. */
3630 int last_sy = -1;
3631 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3633 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3635 if ((sy >> 16) == (last_sy >> 16))
3637 /* This source row is the same as last source row -
3638 * Copy the already stretched row. */
3639 memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count);
3641 else
3643 #define STRETCH_ROW(type) \
3644 do { \
3645 const type *s = (const type *)sbuf; \
3646 type *d = (type *)dbuf; \
3647 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3648 d[x] = s[sx >> 16]; \
3649 } while(0)
3651 switch(bpp)
3653 case 1:
3654 STRETCH_ROW(BYTE);
3655 break;
3656 case 2:
3657 STRETCH_ROW(WORD);
3658 break;
3659 case 4:
3660 STRETCH_ROW(DWORD);
3661 break;
3662 case 3:
3664 const BYTE *s;
3665 BYTE *d = dbuf;
3666 for (x = sx = 0; x < dst_width; x++, sx+= xinc)
3668 DWORD pixel;
3670 s = sbuf + 3 * (sx >> 16);
3671 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3672 d[0] = (pixel ) & 0xff;
3673 d[1] = (pixel >> 8) & 0xff;
3674 d[2] = (pixel >> 16) & 0xff;
3675 d += 3;
3677 break;
3679 default:
3680 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
3681 hr = WINED3DERR_NOTAVAILABLE;
3682 goto error;
3684 #undef STRETCH_ROW
3686 dbuf += dst_map.row_pitch;
3687 last_sy = sy;
3691 else
3693 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
3694 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
3695 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
3696 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3697 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
3699 /* The color keying flags are checked for correctness in ddraw */
3700 if (flags & WINED3D_BLT_SRC_CKEY)
3702 keylow = src_texture->async.src_blt_color_key.color_space_low_value;
3703 keyhigh = src_texture->async.src_blt_color_key.color_space_high_value;
3705 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3707 keylow = fx->src_color_key.color_space_low_value;
3708 keyhigh = fx->src_color_key.color_space_high_value;
3711 if (flags & WINED3D_BLT_DST_CKEY)
3713 /* Destination color keys are taken from the source surface! */
3714 destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value;
3715 destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value;
3717 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
3719 destkeylow = fx->dst_color_key.color_space_low_value;
3720 destkeyhigh = fx->dst_color_key.color_space_high_value;
3723 if (bpp == 1)
3725 keymask = 0xff;
3727 else
3729 DWORD masks[3];
3730 get_color_masks(src_format, masks);
3731 keymask = masks[0]
3732 | masks[1]
3733 | masks[2];
3735 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3736 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
3739 if (flags & WINED3D_BLT_FX)
3741 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
3742 LONG tmpxy;
3743 dTopLeft = dbuf;
3744 dTopRight = dbuf + ((dst_width - 1) * bpp);
3745 dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
3746 dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
3748 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
3750 /* I don't think we need to do anything about this flag */
3751 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
3753 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
3755 tmp = dTopRight;
3756 dTopRight = dTopLeft;
3757 dTopLeft = tmp;
3758 tmp = dBottomRight;
3759 dBottomRight = dBottomLeft;
3760 dBottomLeft = tmp;
3761 dstxinc = dstxinc * -1;
3763 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
3765 tmp = dTopLeft;
3766 dTopLeft = dBottomLeft;
3767 dBottomLeft = tmp;
3768 tmp = dTopRight;
3769 dTopRight = dBottomRight;
3770 dBottomRight = tmp;
3771 dstyinc = dstyinc * -1;
3773 if (fx->fx & WINEDDBLTFX_NOTEARING)
3775 /* I don't think we need to do anything about this flag */
3776 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
3778 if (fx->fx & WINEDDBLTFX_ROTATE180)
3780 tmp = dBottomRight;
3781 dBottomRight = dTopLeft;
3782 dTopLeft = tmp;
3783 tmp = dBottomLeft;
3784 dBottomLeft = dTopRight;
3785 dTopRight = tmp;
3786 dstxinc = dstxinc * -1;
3787 dstyinc = dstyinc * -1;
3789 if (fx->fx & WINEDDBLTFX_ROTATE270)
3791 tmp = dTopLeft;
3792 dTopLeft = dBottomLeft;
3793 dBottomLeft = dBottomRight;
3794 dBottomRight = dTopRight;
3795 dTopRight = tmp;
3796 tmpxy = dstxinc;
3797 dstxinc = dstyinc;
3798 dstyinc = tmpxy;
3799 dstxinc = dstxinc * -1;
3801 if (fx->fx & WINEDDBLTFX_ROTATE90)
3803 tmp = dTopLeft;
3804 dTopLeft = dTopRight;
3805 dTopRight = dBottomRight;
3806 dBottomRight = dBottomLeft;
3807 dBottomLeft = tmp;
3808 tmpxy = dstxinc;
3809 dstxinc = dstyinc;
3810 dstyinc = tmpxy;
3811 dstyinc = dstyinc * -1;
3813 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
3815 /* I don't think we need to do anything about this flag */
3816 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
3818 dbuf = dTopLeft;
3819 flags &= ~(WINED3D_BLT_FX);
3822 #define COPY_COLORKEY_FX(type) \
3823 do { \
3824 const type *s; \
3825 type *d = (type *)dbuf, *dx, tmp; \
3826 for (y = sy = 0; y < dst_height; ++y, sy += yinc) \
3828 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
3829 dx = d; \
3830 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3832 tmp = s[sx >> 16]; \
3833 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
3834 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
3836 dx[0] = tmp; \
3838 dx = (type *)(((BYTE *)dx) + dstxinc); \
3840 d = (type *)(((BYTE *)d) + dstyinc); \
3842 } while(0)
3844 switch (bpp)
3846 case 1:
3847 COPY_COLORKEY_FX(BYTE);
3848 break;
3849 case 2:
3850 COPY_COLORKEY_FX(WORD);
3851 break;
3852 case 4:
3853 COPY_COLORKEY_FX(DWORD);
3854 break;
3855 case 3:
3857 const BYTE *s;
3858 BYTE *d = dbuf, *dx;
3859 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3861 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3862 dx = d;
3863 for (x = sx = 0; x < dst_width; ++x, sx+= xinc)
3865 DWORD pixel, dpixel = 0;
3866 s = sbuf + 3 * (sx>>16);
3867 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3868 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
3869 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
3870 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
3872 dx[0] = (pixel ) & 0xff;
3873 dx[1] = (pixel >> 8) & 0xff;
3874 dx[2] = (pixel >> 16) & 0xff;
3876 dx += dstxinc;
3878 d += dstyinc;
3880 break;
3882 default:
3883 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
3884 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
3885 hr = WINED3DERR_NOTAVAILABLE;
3886 goto error;
3887 #undef COPY_COLORKEY_FX
3892 error:
3893 if (flags)
3894 FIXME(" Unsupported flags %#x.\n", flags);
3896 release:
3897 wined3d_resource_unmap(&dst_texture->resource, dst_sub_resource_idx);
3898 if (src_texture && !same_sub_resource)
3899 wined3d_resource_unmap(&src_texture->resource, src_sub_resource_idx);
3900 if (converted_texture)
3901 wined3d_texture_decref(converted_texture);
3903 return hr;
3906 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
3907 const RECT *rect, const struct wined3d_color *color)
3909 const struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
3910 static const struct wined3d_box src_box;
3911 struct wined3d_blt_fx fx;
3913 fx.fill_color = wined3d_format_convert_from_float(view->format, color);
3914 return surface_cpu_blt(texture_from_resource(view->resource), view->sub_resource_idx,
3915 &box, NULL, 0, &src_box, WINED3D_BLT_COLOR_FILL, &fx, WINED3D_TEXF_POINT);
3918 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
3919 struct wined3d_rendertarget_view *view, const RECT *rect, DWORD clear_flags,
3920 float depth, DWORD stencil)
3922 FIXME("Depth/stencil filling not implemented by cpu_blit.\n");
3923 return WINED3DERR_INVALIDCALL;
3926 static void cpu_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
3927 struct wined3d_surface *src_surface, const RECT *src_rect,
3928 struct wined3d_surface *dst_surface, const RECT *dst_rect,
3929 const struct wined3d_color_key *color_key)
3931 /* FIXME: Remove error returns from surface_blt_cpu. */
3932 ERR("Blit method not implemented by cpu_blit.\n");
3935 const struct blit_shader cpu_blit = {
3936 cpu_blit_alloc,
3937 cpu_blit_free,
3938 cpu_blit_set,
3939 cpu_blit_unset,
3940 cpu_blit_supported,
3941 cpu_blit_color_fill,
3942 cpu_blit_depth_fill,
3943 cpu_blit_blit_surface,
3946 HRESULT wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3947 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
3948 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
3950 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
3951 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
3952 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
3953 struct wined3d_texture *dst_texture = dst_surface->container;
3954 struct wined3d_device *device = dst_texture->resource.device;
3955 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3956 struct wined3d_texture *src_texture = NULL;
3957 unsigned int dst_w, dst_h, src_w, src_h;
3958 unsigned int src_sub_resource_idx = 0;
3959 DWORD src_ds_flags, dst_ds_flags;
3960 BOOL scale, convert;
3962 static const DWORD simple_blit = WINED3D_BLT_ASYNC
3963 | WINED3D_BLT_COLOR_FILL
3964 | WINED3D_BLT_SRC_CKEY
3965 | WINED3D_BLT_SRC_CKEY_OVERRIDE
3966 | WINED3D_BLT_WAIT
3967 | WINED3D_BLT_DEPTH_FILL
3968 | WINED3D_BLT_DO_NOT_WAIT
3969 | WINED3D_BLT_ALPHA_TEST;
3971 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
3972 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3973 flags, fx, debug_d3dtexturefiltertype(filter));
3974 TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage));
3976 if (fx)
3978 TRACE("fx %#x.\n", fx->fx);
3979 TRACE("fill_color 0x%08x.\n", fx->fill_color);
3980 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
3981 fx->dst_color_key.color_space_low_value,
3982 fx->dst_color_key.color_space_high_value);
3983 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
3984 fx->src_color_key.color_space_low_value,
3985 fx->src_color_key.color_space_high_value);
3988 if (src_surface)
3990 src_texture = src_surface->container;
3991 src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
3994 if (dst_texture->sub_resources[dst_sub_resource_idx].map_count
3995 || (src_texture && src_texture->sub_resources[src_sub_resource_idx].map_count))
3997 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
3998 return WINEDDERR_SURFACEBUSY;
4001 dst_w = wined3d_texture_get_level_width(dst_texture, dst_surface->texture_level);
4002 dst_h = wined3d_texture_get_level_height(dst_texture, dst_surface->texture_level);
4003 if (dst_rect->left >= dst_rect->right || dst_rect->top >= dst_rect->bottom
4004 || dst_rect->left > dst_w || dst_rect->left < 0
4005 || dst_rect->top > dst_h || dst_rect->top < 0
4006 || dst_rect->right > dst_w || dst_rect->right < 0
4007 || dst_rect->bottom > dst_h || dst_rect->bottom < 0)
4009 WARN("The application gave us a bad destination rectangle.\n");
4010 return WINEDDERR_INVALIDRECT;
4013 if (src_texture)
4015 src_w = wined3d_texture_get_level_width(src_texture, src_surface->texture_level);
4016 src_h = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
4017 if (src_rect->left >= src_rect->right || src_rect->top >= src_rect->bottom
4018 || src_rect->left > src_w || src_rect->left < 0
4019 || src_rect->top > src_h || src_rect->top < 0
4020 || src_rect->right > src_w || src_rect->right < 0
4021 || src_rect->bottom > src_h || src_rect->bottom < 0)
4023 WARN("The application gave us a bad source rectangle.\n");
4024 return WINEDDERR_INVALIDRECT;
4028 if (!fx || !(fx->fx))
4029 flags &= ~WINED3D_BLT_FX;
4031 if (flags & WINED3D_BLT_WAIT)
4032 flags &= ~WINED3D_BLT_WAIT;
4034 if (flags & WINED3D_BLT_ASYNC)
4036 static unsigned int once;
4038 if (!once++)
4039 FIXME("Can't handle WINED3D_BLT_ASYNC flag.\n");
4040 flags &= ~WINED3D_BLT_ASYNC;
4043 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
4044 if (flags & WINED3D_BLT_DO_NOT_WAIT)
4046 static unsigned int once;
4048 if (!once++)
4049 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
4050 flags &= ~WINED3D_BLT_DO_NOT_WAIT;
4053 if (!device->d3d_initialized)
4055 WARN("D3D not initialized, using fallback.\n");
4056 goto cpu;
4059 /* We want to avoid invalidating the sysmem location for converted
4060 * surfaces, since otherwise we'd have to convert the data back when
4061 * locking them. */
4062 if (dst_texture->flags & WINED3D_TEXTURE_CONVERTED || dst_texture->resource.format->convert
4063 || wined3d_format_get_color_key_conversion(dst_texture, TRUE))
4065 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
4066 goto cpu;
4069 if (flags & ~simple_blit)
4071 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
4072 goto fallback;
4075 if (src_texture)
4076 src_swapchain = src_texture->swapchain;
4077 else
4078 src_swapchain = NULL;
4080 dst_swapchain = dst_texture->swapchain;
4082 /* This isn't strictly needed. FBO blits for example could deal with
4083 * cross-swapchain blits by first downloading the source to a texture
4084 * before switching to the destination context. We just have this here to
4085 * not have to deal with the issue, since cross-swapchain blits should be
4086 * rare. */
4087 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
4089 FIXME("Using fallback for cross-swapchain blit.\n");
4090 goto fallback;
4093 scale = src_texture
4094 && (src_rect->right - src_rect->left != dst_rect->right - dst_rect->left
4095 || src_rect->bottom - src_rect->top != dst_rect->bottom - dst_rect->top);
4096 convert = src_texture && src_texture->resource.format->id != dst_texture->resource.format->id;
4098 dst_ds_flags = dst_texture->resource.format_flags
4099 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
4100 if (src_texture)
4101 src_ds_flags = src_texture->resource.format_flags
4102 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
4103 else
4104 src_ds_flags = 0;
4106 if (src_ds_flags || dst_ds_flags)
4108 if (flags & WINED3D_BLT_DEPTH_FILL)
4110 float depth;
4112 TRACE("Depth fill.\n");
4114 if (!surface_convert_depth_to_float(dst_surface, fx->fill_color, &depth))
4115 return WINED3DERR_INVALIDCALL;
4117 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, dst_rect, depth)))
4118 return WINED3D_OK;
4120 else
4122 if (src_ds_flags != dst_ds_flags)
4124 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
4125 return WINED3DERR_INVALIDCALL;
4128 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_texture->resource.draw_binding,
4129 src_rect, dst_surface, dst_texture->resource.draw_binding, dst_rect)))
4130 return WINED3D_OK;
4133 else
4135 struct wined3d_texture_sub_resource *src_sub_resource, *dst_sub_resource;
4136 const struct blit_shader *blitter;
4138 dst_sub_resource = surface_get_sub_resource(dst_surface);
4139 src_sub_resource = src_texture ? &src_texture->sub_resources[src_sub_resource_idx] : NULL;
4141 /* In principle this would apply to depth blits as well, but we don't
4142 * implement those in the CPU blitter at the moment. */
4143 if ((dst_sub_resource->locations & dst_texture->resource.map_binding)
4144 && (!src_texture || (src_sub_resource->locations & src_texture->resource.map_binding)))
4146 if (scale)
4147 TRACE("Not doing sysmem blit because of scaling.\n");
4148 else if (convert)
4149 TRACE("Not doing sysmem blit because of format conversion.\n");
4150 else
4151 goto cpu;
4154 if (flags & WINED3D_BLT_COLOR_FILL)
4156 struct wined3d_color color;
4157 const struct wined3d_palette *palette = dst_swapchain ? dst_swapchain->palette : NULL;
4159 TRACE("Color fill.\n");
4161 if (!wined3d_format_convert_color_to_float(dst_texture->resource.format,
4162 palette, fx->fill_color, &color))
4163 goto fallback;
4165 if (SUCCEEDED(surface_color_fill(dst_surface, dst_rect, &color)))
4166 return WINED3D_OK;
4168 else
4170 enum wined3d_blit_op blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
4171 const struct wined3d_color_key *color_key = NULL;
4173 TRACE("Color blit.\n");
4174 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
4176 color_key = &fx->src_color_key;
4177 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
4179 else if (flags & WINED3D_BLT_SRC_CKEY)
4181 color_key = &src_texture->async.src_blt_color_key;
4182 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
4184 else if (flags & WINED3D_BLT_ALPHA_TEST)
4186 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
4188 else if ((src_sub_resource->locations & WINED3D_LOCATION_SYSMEM)
4189 && !(dst_sub_resource->locations & WINED3D_LOCATION_SYSMEM))
4191 /* Upload */
4192 if (scale)
4193 TRACE("Not doing upload because of scaling.\n");
4194 else if (convert)
4195 TRACE("Not doing upload because of format conversion.\n");
4196 else
4198 POINT dst_point = {dst_rect->left, dst_rect->top};
4200 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, src_rect)))
4202 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
4204 struct wined3d_context *context = context_acquire(device, dst_surface);
4205 surface_load_location(dst_surface, context, dst_texture->resource.draw_binding);
4206 context_release(context);
4208 return WINED3D_OK;
4212 else if (dst_swapchain && dst_swapchain->back_buffers
4213 && dst_texture == dst_swapchain->front_buffer
4214 && src_texture == dst_swapchain->back_buffers[0])
4216 /* Use present for back -> front blits. The idea behind this is
4217 * that present is potentially faster than a blit, in particular
4218 * when FBO blits aren't available. Some ddraw applications like
4219 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
4220 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
4221 * applications can't blit directly to the frontbuffer. */
4222 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
4224 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
4226 /* Set the swap effect to COPY, we don't want the backbuffer
4227 * to become undefined. */
4228 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
4229 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, 0);
4230 dst_swapchain->desc.swap_effect = swap_effect;
4232 return WINED3D_OK;
4235 if (fbo_blit_supported(&device->adapter->gl_info, blit_op,
4236 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
4237 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format))
4239 struct wined3d_context *context;
4240 TRACE("Using FBO blit.\n");
4242 context = context_acquire(device, NULL);
4243 surface_blt_fbo(device, context, filter,
4244 src_surface, src_texture->resource.draw_binding, src_rect,
4245 dst_surface, dst_texture->resource.draw_binding, dst_rect);
4246 context_release(context);
4248 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx,
4249 dst_texture->resource.draw_binding);
4250 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx,
4251 ~dst_texture->resource.draw_binding);
4253 return WINED3D_OK;
4256 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, blit_op,
4257 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
4258 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format);
4259 if (blitter)
4261 blitter->blit_surface(device, blit_op, filter, src_surface,
4262 src_rect, dst_surface, dst_rect, color_key);
4263 return WINED3D_OK;
4268 fallback:
4269 /* Special cases for render targets. */
4270 if (SUCCEEDED(surface_blt_special(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter)))
4271 return WINED3D_OK;
4273 cpu:
4274 return surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
4275 src_texture, src_sub_resource_idx, &src_box, flags, fx, filter);
4278 /* Context activation is done by the caller. Context may be NULL in
4279 * WINED3D_NO3D mode. */
4280 void wined3d_surface_prepare(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
4282 wined3d_texture_prepare_location(surface->container,
4283 surface_get_sub_resource_idx(surface), context, location);