wined3d: Don't enumerate sub-resources in wined3d_device_reset().
[wine.git] / dlls / wined3d / surface.c
blob493c835b314544deae02929a5dc6360b5eb9c91d
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 wined3d_surface_cleanup(struct wined3d_surface *surface)
41 struct wined3d_surface *overlay, *cur;
43 TRACE("surface %p.\n", surface);
45 if (!list_empty(&surface->renderbuffers))
47 struct wined3d_device *device = surface->container->resource.device;
48 struct wined3d_renderbuffer_entry *entry, *entry2;
49 const struct wined3d_gl_info *gl_info;
50 struct wined3d_context *context;
52 context = context_acquire(device, NULL);
53 gl_info = context->gl_info;
55 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
57 TRACE("Deleting renderbuffer %u.\n", entry->id);
58 context_gl_resource_released(device, entry->id, TRUE);
59 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
60 HeapFree(GetProcessHeap(), 0, entry);
63 context_release(context);
66 if (surface->dc)
67 wined3d_surface_destroy_dc(surface);
69 if (surface->overlay_dest)
70 list_remove(&surface->overlay_entry);
72 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
74 list_remove(&overlay->overlay_entry);
75 overlay->overlay_dest = NULL;
78 resource_cleanup(&surface->resource);
81 void surface_get_drawable_size(const struct wined3d_surface *surface, const struct wined3d_context *context,
82 unsigned int *width, unsigned int *height)
84 if (surface->container->swapchain)
86 /* The drawable size of an onscreen drawable is the surface size.
87 * (Actually: The window size, but the surface is created in window
88 * size.) */
89 *width = context->current_rt.texture->resource.width;
90 *height = context->current_rt.texture->resource.height;
92 else if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER)
94 const struct wined3d_swapchain *swapchain = context->swapchain;
96 /* The drawable size of a backbuffer / aux buffer offscreen target is
97 * the size of the current context's drawable, which is the size of
98 * the back buffer of the swapchain the active context belongs to. */
99 *width = swapchain->desc.backbuffer_width;
100 *height = swapchain->desc.backbuffer_height;
102 else
104 struct wined3d_surface *rt;
106 /* The drawable size of an FBO target is the OpenGL texture size,
107 * which is the power of two size. */
108 rt = context->current_rt.texture->sub_resources[context->current_rt.sub_resource_idx].u.surface;
109 *width = wined3d_texture_get_level_pow2_width(rt->container, rt->texture_level);
110 *height = wined3d_texture_get_level_pow2_height(rt->container, rt->texture_level);
114 struct blt_info
116 GLenum binding;
117 GLenum bind_target;
118 enum wined3d_gl_resource_type tex_type;
119 GLfloat coords[4][3];
122 struct float_rect
124 float l;
125 float t;
126 float r;
127 float b;
130 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
132 f->l = ((r->left * 2.0f) / w) - 1.0f;
133 f->t = ((r->top * 2.0f) / h) - 1.0f;
134 f->r = ((r->right * 2.0f) / w) - 1.0f;
135 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
138 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
140 GLfloat (*coords)[3] = info->coords;
141 struct float_rect f;
143 switch (target)
145 default:
146 FIXME("Unsupported texture target %#x\n", target);
147 /* Fall back to GL_TEXTURE_2D */
148 case GL_TEXTURE_2D:
149 info->binding = GL_TEXTURE_BINDING_2D;
150 info->bind_target = GL_TEXTURE_2D;
151 info->tex_type = WINED3D_GL_RES_TYPE_TEX_2D;
152 coords[0][0] = (float)rect->left / w;
153 coords[0][1] = (float)rect->top / h;
154 coords[0][2] = 0.0f;
156 coords[1][0] = (float)rect->right / w;
157 coords[1][1] = (float)rect->top / h;
158 coords[1][2] = 0.0f;
160 coords[2][0] = (float)rect->left / w;
161 coords[2][1] = (float)rect->bottom / h;
162 coords[2][2] = 0.0f;
164 coords[3][0] = (float)rect->right / w;
165 coords[3][1] = (float)rect->bottom / h;
166 coords[3][2] = 0.0f;
167 break;
169 case GL_TEXTURE_RECTANGLE_ARB:
170 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
171 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
172 info->tex_type = WINED3D_GL_RES_TYPE_TEX_RECT;
173 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
174 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
175 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
176 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
177 break;
179 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
180 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
181 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
182 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
183 cube_coords_float(rect, w, h, &f);
185 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
186 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
187 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
188 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
189 break;
191 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
192 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
193 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
194 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
195 cube_coords_float(rect, w, h, &f);
197 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
198 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
199 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
200 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
201 break;
203 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
204 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
205 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
206 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
207 cube_coords_float(rect, w, h, &f);
209 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
210 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
211 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
212 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
213 break;
215 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
216 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
217 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
218 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
219 cube_coords_float(rect, w, h, &f);
221 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
222 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
223 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
224 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
225 break;
227 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
228 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
229 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
230 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
231 cube_coords_float(rect, w, h, &f);
233 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
234 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
235 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
236 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
237 break;
239 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
240 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
241 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
242 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
243 cube_coords_float(rect, w, h, &f);
245 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
246 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
247 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
248 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
249 break;
253 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
255 if (rect_in)
256 *rect_out = *rect_in;
257 else
259 const struct wined3d_texture *texture = surface->container;
261 rect_out->left = 0;
262 rect_out->top = 0;
263 rect_out->right = wined3d_texture_get_level_width(texture, surface->texture_level);
264 rect_out->bottom = wined3d_texture_get_level_height(texture, surface->texture_level);
268 /* Context activation is done by the caller. */
269 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
270 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
272 const struct wined3d_gl_info *gl_info = context->gl_info;
273 struct wined3d_texture *texture = src_surface->container;
274 struct blt_info info;
276 surface_get_blt_info(src_surface->texture_target, src_rect,
277 wined3d_texture_get_level_pow2_width(texture, src_surface->texture_level),
278 wined3d_texture_get_level_pow2_height(texture, src_surface->texture_level), &info);
280 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
281 checkGLcall("glEnable(bind_target)");
283 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
285 /* Filtering for StretchRect */
286 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
287 checkGLcall("glTexParameteri");
288 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
289 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
290 checkGLcall("glTexParameteri");
291 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
292 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
293 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
294 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
295 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
296 checkGLcall("glTexEnvi");
298 /* Draw a quad */
299 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
300 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
301 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
303 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
304 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
306 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
307 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
309 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
310 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
311 gl_info->gl_ops.gl.p_glEnd();
313 /* Unbind the texture */
314 context_bind_texture(context, info.bind_target, 0);
316 /* We changed the filtering settings on the texture. Inform the
317 * container about this to get the filters reset properly next draw. */
318 texture->texture_rgb.sampler_desc.mag_filter = WINED3D_TEXF_POINT;
319 texture->texture_rgb.sampler_desc.min_filter = WINED3D_TEXF_POINT;
320 texture->texture_rgb.sampler_desc.mip_filter = WINED3D_TEXF_NONE;
321 texture->texture_rgb.sampler_desc.srgb_decode = FALSE;
324 /* Works correctly only for <= 4 bpp formats. */
325 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
327 masks[0] = ((1u << format->red_size) - 1) << format->red_offset;
328 masks[1] = ((1u << format->green_size) - 1) << format->green_offset;
329 masks[2] = ((1u << format->blue_size) - 1) << format->blue_offset;
332 void wined3d_surface_destroy_dc(struct wined3d_surface *surface)
334 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
335 struct wined3d_texture *texture = surface->container;
336 struct wined3d_device *device = texture->resource.device;
337 const struct wined3d_gl_info *gl_info = NULL;
338 D3DKMT_DESTROYDCFROMMEMORY destroy_desc;
339 struct wined3d_context *context = NULL;
340 struct wined3d_bo_address data;
341 NTSTATUS status;
343 if (!surface->dc)
345 ERR("Surface %p has no DC.\n", surface);
346 return;
349 TRACE("dc %p, bitmap %p.\n", surface->dc, surface->bitmap);
351 destroy_desc.hDc = surface->dc;
352 destroy_desc.hBitmap = surface->bitmap;
353 if ((status = D3DKMTDestroyDCFromMemory(&destroy_desc)))
354 ERR("Failed to destroy dc, status %#x.\n", status);
355 surface->dc = NULL;
356 surface->bitmap = NULL;
358 if (device->d3d_initialized)
360 context = context_acquire(device, NULL);
361 gl_info = context->gl_info;
364 wined3d_texture_get_memory(texture, sub_resource_idx, &data, texture->resource.map_binding);
365 wined3d_texture_unmap_bo_address(&data, gl_info, GL_PIXEL_UNPACK_BUFFER);
367 if (context)
368 context_release(context);
371 HRESULT wined3d_surface_create_dc(struct wined3d_surface *surface)
373 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
374 struct wined3d_texture *texture = surface->container;
375 const struct wined3d_format *format = texture->resource.format;
376 struct wined3d_device *device = texture->resource.device;
377 const struct wined3d_gl_info *gl_info = NULL;
378 struct wined3d_context *context = NULL;
379 unsigned int row_pitch, slice_pitch;
380 struct wined3d_bo_address data;
381 D3DKMT_CREATEDCFROMMEMORY desc;
382 NTSTATUS status;
384 TRACE("surface %p.\n", surface);
386 if (!format->ddi_format)
388 WARN("Cannot create a DC for format %s.\n", debug_d3dformat(format->id));
389 return WINED3DERR_INVALIDCALL;
392 wined3d_texture_get_pitch(texture, surface->texture_level, &row_pitch, &slice_pitch);
394 if (device->d3d_initialized)
396 context = context_acquire(device, NULL);
397 gl_info = context->gl_info;
400 wined3d_texture_get_memory(texture, sub_resource_idx, &data, texture->resource.map_binding);
401 desc.pMemory = wined3d_texture_map_bo_address(&data, texture->sub_resources[sub_resource_idx].size,
402 gl_info, GL_PIXEL_UNPACK_BUFFER, 0);
404 if (context)
405 context_release(context);
407 desc.Format = format->ddi_format;
408 desc.Width = wined3d_texture_get_level_width(texture, surface->texture_level);
409 desc.Height = wined3d_texture_get_level_height(texture, surface->texture_level);
410 desc.Pitch = row_pitch;
411 desc.hDeviceDc = CreateCompatibleDC(NULL);
412 desc.pColorTable = NULL;
414 status = D3DKMTCreateDCFromMemory(&desc);
415 DeleteDC(desc.hDeviceDc);
416 if (status)
418 WARN("Failed to create DC, status %#x.\n", status);
419 return WINED3DERR_INVALIDCALL;
422 surface->dc = desc.hDc;
423 surface->bitmap = desc.hBitmap;
425 TRACE("Created DC %p, bitmap %p for surface %p.\n", surface->dc, surface->bitmap, surface);
427 return WINED3D_OK;
430 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
432 unsigned int t;
434 t = wined3d_texture_get_level_width(surface->container, surface->texture_level);
435 if ((r->left && r->right) || abs(r->right - r->left) != t)
436 return FALSE;
437 t = wined3d_texture_get_level_height(surface->container, surface->texture_level);
438 if ((r->top && r->bottom) || abs(r->bottom - r->top) != t)
439 return FALSE;
440 return TRUE;
443 static void surface_depth_blt_fbo(const struct wined3d_device *device,
444 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
445 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
447 const struct wined3d_gl_info *gl_info;
448 struct wined3d_context *context;
449 DWORD src_mask, dst_mask;
450 GLbitfield gl_mask;
452 TRACE("device %p\n", device);
453 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
454 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
455 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
456 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
458 src_mask = src_surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
459 dst_mask = dst_surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
461 if (src_mask != dst_mask)
463 ERR("Incompatible formats %s and %s.\n",
464 debug_d3dformat(src_surface->container->resource.format->id),
465 debug_d3dformat(dst_surface->container->resource.format->id));
466 return;
469 if (!src_mask)
471 ERR("Not a depth / stencil format: %s.\n",
472 debug_d3dformat(src_surface->container->resource.format->id));
473 return;
476 gl_mask = 0;
477 if (src_mask & WINED3DFMT_FLAG_DEPTH)
478 gl_mask |= GL_DEPTH_BUFFER_BIT;
479 if (src_mask & WINED3DFMT_FLAG_STENCIL)
480 gl_mask |= GL_STENCIL_BUFFER_BIT;
482 context = context_acquire(device, NULL);
483 if (!context->valid)
485 context_release(context);
486 WARN("Invalid context, skipping blit.\n");
487 return;
490 /* Make sure the locations are up-to-date. Loading the destination
491 * surface isn't required if the entire surface is overwritten. */
492 surface_load_location(src_surface, context, src_location);
493 if (!surface_is_full_rect(dst_surface, dst_rect))
494 surface_load_location(dst_surface, context, dst_location);
495 else
496 wined3d_surface_prepare(dst_surface, context, dst_location);
498 gl_info = context->gl_info;
500 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
501 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
503 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
504 context_set_draw_buffer(context, GL_NONE);
505 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
506 context_invalidate_state(context, STATE_FRAMEBUFFER);
508 if (gl_mask & GL_DEPTH_BUFFER_BIT)
510 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
511 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
513 if (gl_mask & GL_STENCIL_BUFFER_BIT)
515 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
517 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
518 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
520 gl_info->gl_ops.gl.p_glStencilMask(~0U);
521 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
524 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
525 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
527 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
528 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
529 checkGLcall("glBlitFramebuffer()");
531 if (wined3d_settings.strict_draw_ordering)
532 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
534 context_release(context);
537 /* Blit between surface locations. Onscreen on different swapchains is not supported.
538 * Depth / stencil is not supported. Context activation is done by the caller. */
539 static void surface_blt_fbo(const struct wined3d_device *device,
540 struct wined3d_context *old_ctx, enum wined3d_texture_filter_type filter,
541 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
542 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
544 const struct wined3d_gl_info *gl_info;
545 struct wined3d_context *context = old_ctx;
546 struct wined3d_surface *required_rt, *restore_rt = NULL;
547 RECT src_rect, dst_rect;
548 GLenum gl_filter;
549 GLenum buffer;
551 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
552 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
553 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
554 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
555 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
557 src_rect = *src_rect_in;
558 dst_rect = *dst_rect_in;
560 switch (filter)
562 case WINED3D_TEXF_LINEAR:
563 gl_filter = GL_LINEAR;
564 break;
566 default:
567 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
568 case WINED3D_TEXF_NONE:
569 case WINED3D_TEXF_POINT:
570 gl_filter = GL_NEAREST;
571 break;
574 /* Resolve the source surface first if needed. */
575 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
576 && (src_surface->container->resource.format->id != dst_surface->container->resource.format->id
577 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
578 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
579 src_location = WINED3D_LOCATION_RB_RESOLVED;
581 /* Make sure the locations are up-to-date. Loading the destination
582 * surface isn't required if the entire surface is overwritten. (And is
583 * in fact harmful if we're being called by surface_load_location() with
584 * the purpose of loading the destination surface.) */
585 surface_load_location(src_surface, old_ctx, src_location);
586 if (!surface_is_full_rect(dst_surface, &dst_rect))
587 surface_load_location(dst_surface, old_ctx, dst_location);
588 else
589 wined3d_surface_prepare(dst_surface, old_ctx, dst_location);
592 if (src_location == WINED3D_LOCATION_DRAWABLE) required_rt = src_surface;
593 else if (dst_location == WINED3D_LOCATION_DRAWABLE) required_rt = dst_surface;
594 else required_rt = NULL;
596 restore_rt = context_get_rt_surface(old_ctx);
597 if (restore_rt != required_rt)
598 context = context_acquire(device, required_rt);
599 else
600 restore_rt = NULL;
602 if (!context->valid)
604 context_release(context);
605 WARN("Invalid context, skipping blit.\n");
606 return;
609 gl_info = context->gl_info;
611 if (src_location == WINED3D_LOCATION_DRAWABLE)
613 TRACE("Source surface %p is onscreen.\n", src_surface);
614 buffer = wined3d_texture_get_gl_buffer(src_surface->container);
615 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
617 else
619 TRACE("Source surface %p is offscreen.\n", src_surface);
620 buffer = GL_COLOR_ATTACHMENT0;
623 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
624 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
625 checkGLcall("glReadBuffer()");
626 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
628 if (dst_location == WINED3D_LOCATION_DRAWABLE)
630 TRACE("Destination surface %p is onscreen.\n", dst_surface);
631 buffer = wined3d_texture_get_gl_buffer(dst_surface->container);
632 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
634 else
636 TRACE("Destination surface %p is offscreen.\n", dst_surface);
637 buffer = GL_COLOR_ATTACHMENT0;
640 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
641 context_set_draw_buffer(context, buffer);
642 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
643 context_invalidate_state(context, STATE_FRAMEBUFFER);
645 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
646 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
647 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
648 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
649 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
651 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
652 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
654 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
655 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
656 checkGLcall("glBlitFramebuffer()");
658 if (wined3d_settings.strict_draw_ordering
659 || (dst_location == WINED3D_LOCATION_DRAWABLE
660 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
661 gl_info->gl_ops.gl.p_glFlush();
663 if (restore_rt)
664 context_restore(context, restore_rt);
667 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
668 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
669 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
671 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
672 return FALSE;
674 /* Source and/or destination need to be on the GL side */
675 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
676 return FALSE;
678 switch (blit_op)
680 case WINED3D_BLIT_OP_COLOR_BLIT:
681 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
682 || (src_usage & WINED3DUSAGE_RENDERTARGET)))
683 return FALSE;
684 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
685 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
686 return FALSE;
687 if (!(src_format->id == dst_format->id
688 || (is_identity_fixup(src_format->color_fixup)
689 && is_identity_fixup(dst_format->color_fixup))))
690 return FALSE;
691 break;
693 case WINED3D_BLIT_OP_DEPTH_BLIT:
694 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
695 return FALSE;
696 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
697 return FALSE;
698 /* Accept pure swizzle fixups for depth formats. In general we
699 * ignore the stencil component (if present) at the moment and the
700 * swizzle is not relevant with just the depth component. */
701 if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup)
702 || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup))
703 return FALSE;
704 break;
706 default:
707 return FALSE;
710 return TRUE;
713 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
715 const struct wined3d_format *format = surface->container->resource.format;
717 switch (format->id)
719 case WINED3DFMT_S1_UINT_D15_UNORM:
720 *float_depth = depth / (float)0x00007fff;
721 break;
723 case WINED3DFMT_D16_UNORM:
724 *float_depth = depth / (float)0x0000ffff;
725 break;
727 case WINED3DFMT_D24_UNORM_S8_UINT:
728 case WINED3DFMT_X8D24_UNORM:
729 *float_depth = depth / (float)0x00ffffff;
730 break;
732 case WINED3DFMT_D32_UNORM:
733 *float_depth = depth / (float)0xffffffff;
734 break;
736 default:
737 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
738 return FALSE;
741 return TRUE;
744 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
746 struct wined3d_resource *resource = &surface->container->resource;
747 struct wined3d_device *device = resource->device;
748 struct wined3d_rendertarget_view_desc view_desc;
749 struct wined3d_rendertarget_view *view;
750 const struct blit_shader *blitter;
751 HRESULT hr;
753 if (!(blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info,
754 WINED3D_BLIT_OP_DEPTH_FILL, NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format)))
756 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
757 return WINED3DERR_INVALIDCALL;
760 view_desc.format_id = resource->format->id;
761 view_desc.u.texture.level_idx = surface->texture_level;
762 view_desc.u.texture.layer_idx = surface->texture_layer;
763 view_desc.u.texture.layer_count = 1;
764 if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc,
765 resource, NULL, &wined3d_null_parent_ops, &view)))
767 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
768 return hr;
771 hr = blitter->depth_fill(device, view, rect, WINED3DCLEAR_ZBUFFER, depth, 0);
772 wined3d_rendertarget_view_decref(view);
774 return hr;
777 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
778 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
780 struct wined3d_texture *src_texture = src_surface->container;
781 struct wined3d_texture *dst_texture = dst_surface->container;
782 struct wined3d_device *device = src_texture->resource.device;
784 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
785 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
786 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format))
787 return WINED3DERR_INVALIDCALL;
789 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
791 surface_modify_ds_location(dst_surface, dst_location,
792 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
794 return WINED3D_OK;
797 static ULONG surface_resource_incref(struct wined3d_resource *resource)
799 struct wined3d_surface *surface = surface_from_resource(resource);
801 TRACE("surface %p, container %p.\n", surface, surface->container);
803 return wined3d_texture_incref(surface->container);
806 static ULONG surface_resource_decref(struct wined3d_resource *resource)
808 struct wined3d_surface *surface = surface_from_resource(resource);
810 TRACE("surface %p, container %p.\n", surface, surface->container);
812 return wined3d_texture_decref(surface->container);
815 static void surface_unload(struct wined3d_resource *resource)
817 ERR("Not supported on sub-resources.\n");
820 static HRESULT surface_resource_sub_resource_map(struct wined3d_resource *resource, unsigned int sub_resource_idx,
821 struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags)
823 ERR("Not supported on sub-resources.\n");
824 return WINED3DERR_INVALIDCALL;
827 static HRESULT surface_resource_sub_resource_unmap(struct wined3d_resource *resource, unsigned int sub_resource_idx)
829 ERR("Not supported on sub-resources.\n");
830 return WINED3DERR_INVALIDCALL;
833 static const struct wined3d_resource_ops surface_resource_ops =
835 surface_resource_incref,
836 surface_resource_decref,
837 surface_unload,
838 surface_resource_sub_resource_map,
839 surface_resource_sub_resource_unmap,
842 /* This call just downloads data, the caller is responsible for binding the
843 * correct texture. */
844 /* Context activation is done by the caller. */
845 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
846 DWORD dst_location)
848 struct wined3d_texture *texture = surface->container;
849 const struct wined3d_format *format = texture->resource.format;
850 unsigned int dst_row_pitch, dst_slice_pitch;
851 unsigned int src_row_pitch, src_slice_pitch;
852 struct wined3d_bo_address data;
853 void *mem;
855 /* Only support read back of converted P8 surfaces. */
856 if (texture->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
858 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
859 return;
862 wined3d_texture_get_memory(texture, surface_get_sub_resource_idx(surface), &data, dst_location);
864 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
866 wined3d_texture_get_pitch(texture, surface->texture_level, &dst_row_pitch, &dst_slice_pitch);
867 wined3d_format_calculate_pitch(format, texture->resource.device->surface_alignment,
868 wined3d_texture_get_level_pow2_width(texture, surface->texture_level),
869 wined3d_texture_get_level_pow2_height(texture, surface->texture_level),
870 &src_row_pitch, &src_slice_pitch);
871 mem = HeapAlloc(GetProcessHeap(), 0, src_slice_pitch);
873 if (data.buffer_object)
874 ERR("NP2 emulated texture uses PBO unexpectedly.\n");
875 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
876 ERR("Unexpected compressed format for NP2 emulated texture.\n");
878 else
880 mem = data.addr;
883 if (data.buffer_object)
885 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
886 checkGLcall("glBindBuffer");
889 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
891 TRACE("Downloading compressed surface %p, level %u, format %#x, type %#x, data %p.\n",
892 surface, surface->texture_level, format->glFormat, format->glType, mem);
894 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target, surface->texture_level, mem));
895 checkGLcall("glGetCompressedTexImage");
897 else
899 TRACE("Downloading surface %p, level %u, format %#x, type %#x, data %p.\n",
900 surface, surface->texture_level, format->glFormat, format->glType, mem);
902 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
903 format->glFormat, format->glType, mem);
904 checkGLcall("glGetTexImage");
907 if (data.buffer_object)
909 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
910 checkGLcall("glBindBuffer");
913 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
915 const BYTE *src_data;
916 unsigned int h, y;
917 BYTE *dst_data;
919 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
920 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
921 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
923 * We're doing this...
925 * instead of boxing the texture :
926 * |<-texture width ->| -->pow2width| /\
927 * |111111111111111111| | |
928 * |222 Texture 222222| boxed empty | texture height
929 * |3333 Data 33333333| | |
930 * |444444444444444444| | \/
931 * ----------------------------------- |
932 * | boxed empty | boxed empty | pow2height
933 * | | | \/
934 * -----------------------------------
937 * we're repacking the data to the expected texture width
939 * |<-texture width ->| -->pow2width| /\
940 * |111111111111111111222222222222222| |
941 * |222333333333333333333444444444444| texture height
942 * |444444 | |
943 * | | \/
944 * | | |
945 * | empty | pow2height
946 * | | \/
947 * -----------------------------------
949 * == is the same as
951 * |<-texture width ->| /\
952 * |111111111111111111|
953 * |222222222222222222|texture height
954 * |333333333333333333|
955 * |444444444444444444| \/
956 * --------------------
958 * This also means that any references to surface memory should work with the data as if it were a
959 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
961 * internally the texture is still stored in a boxed format so any references to textureName will
962 * get a boxed texture with width pow2width and not a texture of width resource.width. */
963 src_data = mem;
964 dst_data = data.addr;
965 TRACE("Repacking the surface data from pitch %u to pitch %u.\n", src_row_pitch, dst_row_pitch);
966 h = wined3d_texture_get_level_height(texture, surface->texture_level);
967 for (y = 0; y < h; ++y)
969 memcpy(dst_data, src_data, dst_row_pitch);
970 src_data += src_row_pitch;
971 dst_data += dst_row_pitch;
974 HeapFree(GetProcessHeap(), 0, mem);
978 /* This call just uploads data, the caller is responsible for binding the
979 * correct texture. */
980 /* Context activation is done by the caller. */
981 void wined3d_surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
982 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
983 BOOL srgb, const struct wined3d_const_bo_address *data)
985 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
986 struct wined3d_texture *texture = surface->container;
987 UINT update_w = src_rect->right - src_rect->left;
988 UINT update_h = src_rect->bottom - src_rect->top;
990 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
991 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
992 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
994 if (texture->sub_resources[sub_resource_idx].map_count)
996 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
997 texture->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
1000 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE)
1002 update_h *= format->height_scale.numerator;
1003 update_h /= format->height_scale.denominator;
1006 if (data->buffer_object)
1008 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
1009 checkGLcall("glBindBuffer");
1012 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
1014 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1015 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1016 const BYTE *addr = data->addr;
1017 GLenum internal;
1019 addr += (src_rect->top / format->block_height) * src_pitch;
1020 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1022 if (srgb)
1023 internal = format->glGammaInternal;
1024 else if (texture->resource.usage & WINED3DUSAGE_RENDERTARGET
1025 && wined3d_resource_is_offscreen(&texture->resource))
1026 internal = format->rtInternal;
1027 else
1028 internal = format->glInternal;
1030 TRACE("glCompressedTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, "
1031 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1032 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1034 if (row_length == src_pitch)
1036 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1037 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1039 else
1041 UINT row, y;
1043 /* glCompressedTexSubImage2D() ignores pixel store state, so we
1044 * can't use the unpack row length like for glTexSubImage2D. */
1045 for (row = 0, y = dst_point->y; row < row_count; ++row)
1047 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1048 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1049 y += format->block_height;
1050 addr += src_pitch;
1053 checkGLcall("glCompressedTexSubImage2D");
1055 else
1057 const BYTE *addr = data->addr;
1059 addr += src_rect->top * src_pitch;
1060 addr += src_rect->left * format->byte_count;
1062 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1063 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1064 update_w, update_h, format->glFormat, format->glType, addr);
1066 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1067 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1068 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1069 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1070 checkGLcall("glTexSubImage2D");
1073 if (data->buffer_object)
1075 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
1076 checkGLcall("glBindBuffer");
1079 if (wined3d_settings.strict_draw_ordering)
1080 gl_info->gl_ops.gl.p_glFlush();
1082 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1084 struct wined3d_device *device = texture->resource.device;
1085 unsigned int i;
1087 for (i = 0; i < device->context_count; ++i)
1089 context_surface_update(device->contexts[i], surface);
1094 static BOOL surface_check_block_align_rect(struct wined3d_surface *surface, const RECT *rect)
1096 struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
1098 return wined3d_texture_check_block_align(surface->container, surface->texture_level, &box);
1101 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1102 struct wined3d_surface *src_surface, const RECT *src_rect)
1104 unsigned int src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
1105 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1106 struct wined3d_texture *src_texture = src_surface->container;
1107 struct wined3d_texture *dst_texture = dst_surface->container;
1108 unsigned int src_row_pitch, src_slice_pitch;
1109 const struct wined3d_format *src_format;
1110 const struct wined3d_format *dst_format;
1111 unsigned int src_fmt_flags, dst_fmt_flags;
1112 const struct wined3d_gl_info *gl_info;
1113 struct wined3d_context *context;
1114 struct wined3d_bo_address data;
1115 UINT update_w, update_h;
1116 UINT dst_w, dst_h;
1117 RECT r, dst_rect;
1118 POINT p;
1120 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1121 dst_surface, wine_dbgstr_point(dst_point),
1122 src_surface, wine_dbgstr_rect(src_rect));
1124 src_format = src_texture->resource.format;
1125 dst_format = dst_texture->resource.format;
1126 src_fmt_flags = src_texture->resource.format_flags;
1127 dst_fmt_flags = dst_texture->resource.format_flags;
1129 if (src_format->id != dst_format->id)
1131 WARN("Source and destination surfaces should have the same format.\n");
1132 return WINED3DERR_INVALIDCALL;
1135 if (!dst_point)
1137 p.x = 0;
1138 p.y = 0;
1139 dst_point = &p;
1141 else if (dst_point->x < 0 || dst_point->y < 0)
1143 WARN("Invalid destination point.\n");
1144 return WINED3DERR_INVALIDCALL;
1147 if (!src_rect)
1149 SetRect(&r, 0, 0, wined3d_texture_get_level_width(src_texture, src_surface->texture_level),
1150 wined3d_texture_get_level_height(src_texture, src_surface->texture_level));
1151 src_rect = &r;
1153 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1154 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1156 WARN("Invalid source rectangle.\n");
1157 return WINED3DERR_INVALIDCALL;
1160 dst_w = wined3d_texture_get_level_width(dst_texture, dst_surface->texture_level);
1161 dst_h = wined3d_texture_get_level_height(dst_texture, dst_surface->texture_level);
1163 update_w = src_rect->right - src_rect->left;
1164 update_h = src_rect->bottom - src_rect->top;
1166 if (update_w > dst_w || dst_point->x > dst_w - update_w
1167 || update_h > dst_h || dst_point->y > dst_h - update_h)
1169 WARN("Destination out of bounds.\n");
1170 return WINED3DERR_INVALIDCALL;
1173 if ((src_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(src_surface, src_rect))
1175 WARN("Source rectangle not block-aligned.\n");
1176 return WINED3DERR_INVALIDCALL;
1179 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1180 if ((dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(dst_surface, &dst_rect))
1182 WARN("Destination rectangle not block-aligned.\n");
1183 return WINED3DERR_INVALIDCALL;
1186 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1187 if (dst_format->convert || wined3d_format_get_color_key_conversion(dst_texture, FALSE))
1188 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1190 context = context_acquire(dst_texture->resource.device, NULL);
1191 gl_info = context->gl_info;
1193 /* Only load the surface for partial updates. For newly allocated texture
1194 * the texture wouldn't be the current location, and we'd upload zeroes
1195 * just to overwrite them again. */
1196 if (update_w == dst_w && update_h == dst_h)
1197 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
1198 else
1199 surface_load_location(dst_surface, context, WINED3D_LOCATION_TEXTURE_RGB);
1200 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
1202 wined3d_texture_get_memory(src_texture, src_sub_resource_idx, &data,
1203 src_texture->sub_resources[src_sub_resource_idx].locations);
1204 wined3d_texture_get_pitch(src_texture, src_surface->texture_level, &src_row_pitch, &src_slice_pitch);
1206 wined3d_surface_upload_data(dst_surface, gl_info, src_format, src_rect,
1207 src_row_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
1209 context_release(context);
1211 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1212 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1214 return WINED3D_OK;
1217 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1218 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1219 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1220 /* Context activation is done by the caller. */
1221 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1223 const struct wined3d_gl_info *gl_info = &surface->container->resource.device->adapter->gl_info;
1224 struct wined3d_renderbuffer_entry *entry;
1225 GLuint renderbuffer = 0;
1226 unsigned int src_width, src_height;
1227 unsigned int width, height;
1229 if (rt && rt->container->resource.format->id != WINED3DFMT_NULL)
1231 width = wined3d_texture_get_level_pow2_width(rt->container, rt->texture_level);
1232 height = wined3d_texture_get_level_pow2_height(rt->container, rt->texture_level);
1234 else
1236 width = wined3d_texture_get_level_pow2_width(surface->container, surface->texture_level);
1237 height = wined3d_texture_get_level_pow2_height(surface->container, surface->texture_level);
1240 src_width = wined3d_texture_get_level_pow2_width(surface->container, surface->texture_level);
1241 src_height = wined3d_texture_get_level_pow2_height(surface->container, surface->texture_level);
1243 /* A depth stencil smaller than the render target is not valid */
1244 if (width > src_width || height > src_height) return;
1246 /* Remove any renderbuffer set if the sizes match */
1247 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1248 || (width == src_width && height == src_height))
1250 surface->current_renderbuffer = NULL;
1251 return;
1254 /* Look if we've already got a renderbuffer of the correct dimensions */
1255 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1257 if (entry->width == width && entry->height == height)
1259 renderbuffer = entry->id;
1260 surface->current_renderbuffer = entry;
1261 break;
1265 if (!renderbuffer)
1267 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1268 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1269 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1270 surface->container->resource.format->glInternal, width, height);
1272 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1273 entry->width = width;
1274 entry->height = height;
1275 entry->id = renderbuffer;
1276 list_add_head(&surface->renderbuffers, &entry->entry);
1278 surface->current_renderbuffer = entry;
1281 checkGLcall("set_compatible_renderbuffer");
1284 /* See also float_16_to_32() in wined3d_private.h */
1285 static inline unsigned short float_32_to_16(const float *in)
1287 int exp = 0;
1288 float tmp = fabsf(*in);
1289 unsigned int mantissa;
1290 unsigned short ret;
1292 /* Deal with special numbers */
1293 if (*in == 0.0f)
1294 return 0x0000;
1295 if (isnan(*in))
1296 return 0x7c01;
1297 if (isinf(*in))
1298 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1300 if (tmp < (float)(1u << 10))
1304 tmp = tmp * 2.0f;
1305 exp--;
1306 } while (tmp < (float)(1u << 10));
1308 else if (tmp >= (float)(1u << 11))
1312 tmp /= 2.0f;
1313 exp++;
1314 } while (tmp >= (float)(1u << 11));
1317 mantissa = (unsigned int)tmp;
1318 if (tmp - mantissa >= 0.5f)
1319 ++mantissa; /* Round to nearest, away from zero. */
1321 exp += 10; /* Normalize the mantissa. */
1322 exp += 15; /* Exponent is encoded with excess 15. */
1324 if (exp > 30) /* too big */
1326 ret = 0x7c00; /* INF */
1328 else if (exp <= 0)
1330 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1331 while (exp <= 0)
1333 mantissa = mantissa >> 1;
1334 ++exp;
1336 ret = mantissa & 0x3ff;
1338 else
1340 ret = (exp << 10) | (mantissa & 0x3ff);
1343 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1344 return ret;
1347 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
1348 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1350 unsigned short *dst_s;
1351 const float *src_f;
1352 unsigned int x, y;
1354 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1356 for (y = 0; y < h; ++y)
1358 src_f = (const float *)(src + y * pitch_in);
1359 dst_s = (unsigned short *) (dst + y * pitch_out);
1360 for (x = 0; x < w; ++x)
1362 dst_s[x] = float_32_to_16(src_f + x);
1367 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
1368 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1370 static const unsigned char convert_5to8[] =
1372 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
1373 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
1374 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
1375 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
1377 static const unsigned char convert_6to8[] =
1379 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
1380 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
1381 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
1382 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
1383 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
1384 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
1385 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
1386 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
1388 unsigned int x, y;
1390 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1392 for (y = 0; y < h; ++y)
1394 const WORD *src_line = (const WORD *)(src + y * pitch_in);
1395 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1396 for (x = 0; x < w; ++x)
1398 WORD pixel = src_line[x];
1399 dst_line[x] = 0xff000000u
1400 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
1401 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
1402 | convert_5to8[(pixel & 0x001fu)];
1407 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
1408 * in both cases we're just setting the X / Alpha channel to 0xff. */
1409 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
1410 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1412 unsigned int x, y;
1414 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1416 for (y = 0; y < h; ++y)
1418 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
1419 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1421 for (x = 0; x < w; ++x)
1423 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
1428 static inline BYTE cliptobyte(int x)
1430 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
1433 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
1434 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1436 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1437 unsigned int x, y;
1439 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1441 for (y = 0; y < h; ++y)
1443 const BYTE *src_line = src + y * pitch_in;
1444 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1445 for (x = 0; x < w; ++x)
1447 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1448 * C = Y - 16; D = U - 128; E = V - 128;
1449 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1450 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1451 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1452 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1453 * U and V are shared between the pixels. */
1454 if (!(x & 1)) /* For every even pixel, read new U and V. */
1456 d = (int) src_line[1] - 128;
1457 e = (int) src_line[3] - 128;
1458 r2 = 409 * e + 128;
1459 g2 = - 100 * d - 208 * e + 128;
1460 b2 = 516 * d + 128;
1462 c2 = 298 * ((int) src_line[0] - 16);
1463 dst_line[x] = 0xff000000
1464 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
1465 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
1466 | cliptobyte((c2 + b2) >> 8); /* blue */
1467 /* Scale RGB values to 0..255 range,
1468 * then clip them if still not in range (may be negative),
1469 * then shift them within DWORD if necessary. */
1470 src_line += 2;
1475 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
1476 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1478 unsigned int x, y;
1479 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1481 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
1483 for (y = 0; y < h; ++y)
1485 const BYTE *src_line = src + y * pitch_in;
1486 WORD *dst_line = (WORD *)(dst + y * pitch_out);
1487 for (x = 0; x < w; ++x)
1489 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1490 * C = Y - 16; D = U - 128; E = V - 128;
1491 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1492 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1493 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1494 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1495 * U and V are shared between the pixels. */
1496 if (!(x & 1)) /* For every even pixel, read new U and V. */
1498 d = (int) src_line[1] - 128;
1499 e = (int) src_line[3] - 128;
1500 r2 = 409 * e + 128;
1501 g2 = - 100 * d - 208 * e + 128;
1502 b2 = 516 * d + 128;
1504 c2 = 298 * ((int) src_line[0] - 16);
1505 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
1506 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
1507 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
1508 /* Scale RGB values to 0..255 range,
1509 * then clip them if still not in range (may be negative),
1510 * then shift them within DWORD if necessary. */
1511 src_line += 2;
1516 struct d3dfmt_converter_desc
1518 enum wined3d_format_id from, to;
1519 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
1522 static const struct d3dfmt_converter_desc converters[] =
1524 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
1525 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
1526 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1527 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1528 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
1529 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
1532 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
1533 enum wined3d_format_id to)
1535 unsigned int i;
1537 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
1539 if (converters[i].from == from && converters[i].to == to)
1540 return &converters[i];
1543 return NULL;
1546 static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture,
1547 unsigned int sub_resource_idx, const struct wined3d_format *dst_format)
1549 unsigned int texture_level = sub_resource_idx % src_texture->level_count;
1550 const struct wined3d_format *src_format = src_texture->resource.format;
1551 struct wined3d_device *device = src_texture->resource.device;
1552 const struct d3dfmt_converter_desc *conv = NULL;
1553 struct wined3d_texture *dst_texture;
1554 struct wined3d_resource_desc desc;
1555 struct wined3d_map_desc src_map;
1557 if (!(conv = find_converter(src_format->id, dst_format->id)) && (!device->d3d_initialized
1558 || !is_identity_fixup(src_format->color_fixup) || src_format->convert
1559 || !is_identity_fixup(dst_format->color_fixup) || dst_format->convert))
1561 FIXME("Cannot find a conversion function from format %s to %s.\n",
1562 debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
1563 return NULL;
1566 /* FIXME: Multisampled conversion? */
1567 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
1568 desc.format = dst_format->id;
1569 desc.multisample_type = WINED3D_MULTISAMPLE_NONE;
1570 desc.multisample_quality = 0;
1571 desc.usage = 0;
1572 desc.pool = WINED3D_POOL_SCRATCH;
1573 desc.width = wined3d_texture_get_level_width(src_texture, texture_level);
1574 desc.height = wined3d_texture_get_level_height(src_texture, texture_level);
1575 desc.depth = 1;
1576 desc.size = 0;
1577 if (FAILED(wined3d_texture_create(device, &desc, 1,
1578 WINED3D_TEXTURE_CREATE_MAPPABLE | WINED3D_TEXTURE_CREATE_DISCARD,
1579 NULL, NULL, &wined3d_null_parent_ops, &dst_texture)))
1581 ERR("Failed to create a destination texture for conversion.\n");
1582 return NULL;
1585 memset(&src_map, 0, sizeof(src_map));
1586 if (FAILED(wined3d_resource_map(&src_texture->resource, sub_resource_idx,
1587 &src_map, NULL, WINED3D_MAP_READONLY)))
1589 ERR("Failed to map the source texture.\n");
1590 wined3d_texture_decref(dst_texture);
1591 return NULL;
1593 if (conv)
1595 struct wined3d_map_desc dst_map;
1597 memset(&dst_map, 0, sizeof(dst_map));
1598 if (FAILED(wined3d_resource_map(&dst_texture->resource, 0, &dst_map, NULL, 0)))
1600 ERR("Failed to map the destination texture.\n");
1601 wined3d_resource_unmap(&src_texture->resource, sub_resource_idx);
1602 wined3d_texture_decref(dst_texture);
1603 return NULL;
1606 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch, desc.width, desc.height);
1608 wined3d_resource_unmap(&dst_texture->resource, 0);
1610 else
1612 struct wined3d_bo_address data = {0, src_map.data};
1613 RECT src_rect = {0, 0, desc.width, desc.height};
1614 const struct wined3d_gl_info *gl_info;
1615 struct wined3d_context *context;
1616 POINT dst_point = {0, 0};
1618 TRACE("Using upload conversion.\n");
1619 context = context_acquire(device, NULL);
1620 gl_info = context->gl_info;
1622 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
1623 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
1624 wined3d_surface_upload_data(dst_texture->sub_resources[0].u.surface, gl_info, src_format,
1625 &src_rect, src_map.row_pitch, &dst_point, FALSE, wined3d_const_bo_address(&data));
1627 context_release(context);
1629 wined3d_texture_validate_location(dst_texture, 0, WINED3D_LOCATION_TEXTURE_RGB);
1630 wined3d_texture_invalidate_location(dst_texture, 0, ~WINED3D_LOCATION_TEXTURE_RGB);
1632 wined3d_resource_unmap(&src_texture->resource, sub_resource_idx);
1634 return dst_texture;
1637 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
1638 unsigned int bpp, UINT pitch, DWORD color)
1640 BYTE *first;
1641 unsigned int x, y;
1643 /* Do first row */
1645 #define COLORFILL_ROW(type) \
1646 do { \
1647 type *d = (type *)buf; \
1648 for (x = 0; x < width; ++x) \
1649 d[x] = (type)color; \
1650 } while(0)
1652 switch (bpp)
1654 case 1:
1655 COLORFILL_ROW(BYTE);
1656 break;
1658 case 2:
1659 COLORFILL_ROW(WORD);
1660 break;
1662 case 3:
1664 BYTE *d = buf;
1665 for (x = 0; x < width; ++x, d += 3)
1667 d[0] = (color ) & 0xff;
1668 d[1] = (color >> 8) & 0xff;
1669 d[2] = (color >> 16) & 0xff;
1671 break;
1673 case 4:
1674 COLORFILL_ROW(DWORD);
1675 break;
1677 default:
1678 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
1679 return WINED3DERR_NOTAVAILABLE;
1682 #undef COLORFILL_ROW
1684 /* Now copy first row. */
1685 first = buf;
1686 for (y = 1; y < height; ++y)
1688 buf += pitch;
1689 memcpy(buf, first, width * bpp);
1692 return WINED3D_OK;
1695 static void read_from_framebuffer(struct wined3d_surface *surface,
1696 struct wined3d_context *old_ctx, DWORD dst_location)
1698 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
1699 struct wined3d_texture *texture = surface->container;
1700 struct wined3d_device *device = texture->resource.device;
1701 const struct wined3d_gl_info *gl_info;
1702 struct wined3d_context *context = old_ctx;
1703 struct wined3d_surface *restore_rt = NULL;
1704 unsigned int row_pitch, slice_pitch;
1705 unsigned int width, height;
1706 BYTE *mem;
1707 BYTE *row, *top, *bottom;
1708 int i;
1709 BOOL srcIsUpsideDown;
1710 struct wined3d_bo_address data;
1712 wined3d_texture_get_memory(texture, sub_resource_idx, &data, dst_location);
1714 restore_rt = context_get_rt_surface(old_ctx);
1715 if (restore_rt != surface)
1716 context = context_acquire(device, surface);
1717 else
1718 restore_rt = NULL;
1720 context_apply_blit_state(context, device);
1721 gl_info = context->gl_info;
1723 /* Select the correct read buffer, and give some debug output.
1724 * There is no need to keep track of the current read buffer or reset it, every part of the code
1725 * that reads sets the read buffer as desired.
1727 if (wined3d_resource_is_offscreen(&texture->resource))
1729 /* Mapping the primary render target which is not on a swapchain.
1730 * Read from the back buffer. */
1731 TRACE("Mapping offscreen render target.\n");
1732 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1733 srcIsUpsideDown = TRUE;
1735 else
1737 /* Onscreen surfaces are always part of a swapchain */
1738 GLenum buffer = wined3d_texture_get_gl_buffer(texture);
1739 TRACE("Mapping %#x buffer.\n", buffer);
1740 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
1741 checkGLcall("glReadBuffer");
1742 srcIsUpsideDown = FALSE;
1745 if (data.buffer_object)
1747 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1748 checkGLcall("glBindBuffer");
1751 wined3d_texture_get_pitch(texture, surface->texture_level, &row_pitch, &slice_pitch);
1753 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1754 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / texture->resource.format->byte_count);
1755 checkGLcall("glPixelStorei");
1757 width = wined3d_texture_get_level_width(texture, surface->texture_level);
1758 height = wined3d_texture_get_level_height(texture, surface->texture_level);
1759 gl_info->gl_ops.gl.p_glReadPixels(0, 0, width, height,
1760 texture->resource.format->glFormat,
1761 texture->resource.format->glType, data.addr);
1762 checkGLcall("glReadPixels");
1764 /* Reset previous pixel store pack state */
1765 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
1766 checkGLcall("glPixelStorei");
1768 if (!srcIsUpsideDown)
1770 /* glReadPixels returns the image upside down, and there is no way to
1771 * prevent this. Flip the lines in software. */
1773 if (!(row = HeapAlloc(GetProcessHeap(), 0, row_pitch)))
1774 goto error;
1776 if (data.buffer_object)
1778 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
1779 checkGLcall("glMapBuffer");
1781 else
1782 mem = data.addr;
1784 top = mem;
1785 bottom = mem + row_pitch * (height - 1);
1786 for (i = 0; i < height / 2; i++)
1788 memcpy(row, top, row_pitch);
1789 memcpy(top, bottom, row_pitch);
1790 memcpy(bottom, row, row_pitch);
1791 top += row_pitch;
1792 bottom -= row_pitch;
1794 HeapFree(GetProcessHeap(), 0, row);
1796 if (data.buffer_object)
1797 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
1800 error:
1801 if (data.buffer_object)
1803 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1804 checkGLcall("glBindBuffer");
1807 if (restore_rt)
1808 context_restore(context, restore_rt);
1811 /* Read the framebuffer contents into a texture. Note that this function
1812 * doesn't do any kind of flipping. Using this on an onscreen surface will
1813 * result in a flipped D3D texture.
1815 * Context activation is done by the caller. This function may temporarily
1816 * switch to a different context and restore the original one before return. */
1817 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb, struct wined3d_context *old_ctx)
1819 struct wined3d_texture *texture = surface->container;
1820 struct wined3d_device *device = texture->resource.device;
1821 const struct wined3d_gl_info *gl_info;
1822 struct wined3d_context *context = old_ctx;
1823 struct wined3d_surface *restore_rt = NULL;
1825 restore_rt = context_get_rt_surface(old_ctx);
1826 if (restore_rt != surface)
1827 context = context_acquire(device, surface);
1828 else
1829 restore_rt = NULL;
1831 gl_info = context->gl_info;
1832 device_invalidate_state(device, STATE_FRAMEBUFFER);
1834 wined3d_texture_prepare_texture(texture, context, srgb);
1835 wined3d_texture_bind_and_dirtify(texture, context, srgb);
1837 TRACE("Reading back offscreen render target %p.\n", surface);
1839 if (wined3d_resource_is_offscreen(&texture->resource))
1840 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1841 else
1842 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(texture));
1843 checkGLcall("glReadBuffer");
1845 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
1846 0, 0, 0, 0, wined3d_texture_get_level_width(texture, surface->texture_level),
1847 wined3d_texture_get_level_height(texture, surface->texture_level));
1848 checkGLcall("glCopyTexSubImage2D");
1850 if (restore_rt)
1851 context_restore(context, restore_rt);
1854 /* Does a direct frame buffer -> texture copy. Stretching is done with single
1855 * pixel copy calls. */
1856 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
1857 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
1859 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1860 struct wined3d_texture *src_texture = src_surface->container;
1861 struct wined3d_texture *dst_texture = dst_surface->container;
1862 struct wined3d_device *device = dst_texture->resource.device;
1863 const struct wined3d_gl_info *gl_info;
1864 float xrel, yrel;
1865 struct wined3d_context *context;
1866 BOOL upsidedown = FALSE;
1867 RECT dst_rect = *dst_rect_in;
1868 unsigned int src_height;
1870 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
1871 * glCopyTexSubImage is a bit picky about the parameters we pass to it
1873 if(dst_rect.top > dst_rect.bottom) {
1874 UINT tmp = dst_rect.bottom;
1875 dst_rect.bottom = dst_rect.top;
1876 dst_rect.top = tmp;
1877 upsidedown = TRUE;
1880 context = context_acquire(device, src_surface);
1881 gl_info = context->gl_info;
1882 context_apply_blit_state(context, device);
1883 wined3d_texture_load(dst_texture, context, FALSE);
1885 /* Bind the target texture */
1886 context_bind_texture(context, dst_texture->target, dst_texture->texture_rgb.name);
1887 if (wined3d_resource_is_offscreen(&src_texture->resource))
1889 TRACE("Reading from an offscreen target\n");
1890 upsidedown = !upsidedown;
1891 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1893 else
1895 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
1897 checkGLcall("glReadBuffer");
1899 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
1900 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
1902 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1904 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
1906 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1907 ERR("Texture filtering not supported in direct blit.\n");
1909 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
1910 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1912 ERR("Texture filtering not supported in direct blit\n");
1915 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
1916 if (upsidedown
1917 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1918 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
1920 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
1921 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1922 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
1923 src_rect->left, src_height - src_rect->bottom,
1924 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
1926 else
1928 LONG row;
1929 UINT yoffset = src_height - src_rect->top + dst_rect.top - 1;
1930 /* I have to process this row by row to swap the image,
1931 * otherwise it would be upside down, so stretching in y direction
1932 * doesn't cost extra time
1934 * However, stretching in x direction can be avoided if not necessary
1936 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
1937 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
1939 /* Well, that stuff works, but it's very slow.
1940 * find a better way instead
1942 LONG col;
1944 for (col = dst_rect.left; col < dst_rect.right; ++col)
1946 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1947 dst_rect.left + col /* x offset */, row /* y offset */,
1948 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
1951 else
1953 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
1954 dst_rect.left /* x offset */, row /* y offset */,
1955 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
1959 checkGLcall("glCopyTexSubImage2D");
1961 context_release(context);
1963 /* The texture is now most up to date - If the surface is a render target
1964 * and has a drawable, this path is never entered. */
1965 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
1966 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
1969 /* Uses the hardware to stretch and flip the image */
1970 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
1971 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
1973 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
1974 unsigned int src_width, src_height, src_pow2_width, src_pow2_height;
1975 struct wined3d_texture *src_texture = src_surface->container;
1976 struct wined3d_texture *dst_texture = dst_surface->container;
1977 struct wined3d_device *device = dst_texture->resource.device;
1978 GLuint src, backup = 0;
1979 float left, right, top, bottom; /* Texture coordinates */
1980 const struct wined3d_gl_info *gl_info;
1981 struct wined3d_context *context;
1982 GLenum drawBuffer = GL_BACK;
1983 GLenum offscreen_buffer;
1984 GLenum texture_target;
1985 BOOL noBackBufferBackup;
1986 BOOL src_offscreen;
1987 BOOL upsidedown = FALSE;
1988 RECT dst_rect = *dst_rect_in;
1990 TRACE("Using hwstretch blit\n");
1991 /* Activate the Proper context for reading from the source surface, set it up for blitting */
1992 context = context_acquire(device, src_surface);
1993 gl_info = context->gl_info;
1994 context_apply_blit_state(context, device);
1995 wined3d_texture_load(dst_texture, context, FALSE);
1997 offscreen_buffer = context_get_offscreen_gl_buffer(context);
1998 src_width = wined3d_texture_get_level_width(src_texture, src_surface->texture_level);
1999 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
2000 src_pow2_width = wined3d_texture_get_level_pow2_width(src_texture, src_surface->texture_level);
2001 src_pow2_height = wined3d_texture_get_level_pow2_height(src_texture, src_surface->texture_level);
2003 src_offscreen = wined3d_resource_is_offscreen(&src_texture->resource);
2004 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
2005 if (!noBackBufferBackup && !src_texture->texture_rgb.name)
2007 /* Get it a description */
2008 wined3d_texture_load(src_texture, context, FALSE);
2011 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2012 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2014 if (context->aux_buffers >= 2)
2016 /* Got more than one aux buffer? Use the 2nd aux buffer */
2017 drawBuffer = GL_AUX1;
2019 else if ((!src_offscreen || offscreen_buffer == GL_BACK) && context->aux_buffers >= 1)
2021 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2022 drawBuffer = GL_AUX0;
2025 if (noBackBufferBackup)
2027 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
2028 checkGLcall("glGenTextures");
2029 context_bind_texture(context, GL_TEXTURE_2D, backup);
2030 texture_target = GL_TEXTURE_2D;
2032 else
2034 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2035 * we are reading from the back buffer, the backup can be used as source texture
2037 texture_target = src_surface->texture_target;
2038 context_bind_texture(context, texture_target, src_texture->texture_rgb.name);
2039 gl_info->gl_ops.gl.p_glEnable(texture_target);
2040 checkGLcall("glEnable(texture_target)");
2042 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2043 surface_get_sub_resource(src_surface)->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
2046 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2047 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2049 if(dst_rect.top > dst_rect.bottom) {
2050 UINT tmp = dst_rect.bottom;
2051 dst_rect.bottom = dst_rect.top;
2052 dst_rect.top = tmp;
2053 upsidedown = TRUE;
2056 if (src_offscreen)
2058 TRACE("Reading from an offscreen target\n");
2059 upsidedown = !upsidedown;
2060 gl_info->gl_ops.gl.p_glReadBuffer(offscreen_buffer);
2062 else
2064 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
2067 /* TODO: Only back up the part that will be overwritten */
2068 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, src_width, src_height);
2070 checkGLcall("glCopyTexSubImage2D");
2072 /* No issue with overriding these - the sampler is dirty due to blit usage */
2073 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
2074 checkGLcall("glTexParameteri");
2075 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2076 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
2077 checkGLcall("glTexParameteri");
2079 if (!src_texture->swapchain || src_texture == src_texture->swapchain->back_buffers[0])
2081 src = backup ? backup : src_texture->texture_rgb.name;
2083 else
2085 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
2086 checkGLcall("glReadBuffer(GL_FRONT)");
2088 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
2089 checkGLcall("glGenTextures(1, &src)");
2090 context_bind_texture(context, GL_TEXTURE_2D, src);
2092 /* TODO: Only copy the part that will be read. Use src_rect->left,
2093 * src_rect->bottom as origin, but with the width watch out for power
2094 * of 2 sizes. */
2095 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_pow2_width,
2096 src_pow2_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2097 checkGLcall("glTexImage2D");
2098 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, src_width, src_height);
2100 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2101 checkGLcall("glTexParameteri");
2102 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2103 checkGLcall("glTexParameteri");
2105 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
2106 checkGLcall("glReadBuffer(GL_BACK)");
2108 if (texture_target != GL_TEXTURE_2D)
2110 gl_info->gl_ops.gl.p_glDisable(texture_target);
2111 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
2112 texture_target = GL_TEXTURE_2D;
2115 checkGLcall("glEnd and previous");
2117 left = src_rect->left;
2118 right = src_rect->right;
2120 if (!upsidedown)
2122 top = src_height - src_rect->top;
2123 bottom = src_height - src_rect->bottom;
2125 else
2127 top = src_height - src_rect->bottom;
2128 bottom = src_height - src_rect->top;
2131 if (src_texture->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
2133 left /= src_pow2_width;
2134 right /= src_pow2_width;
2135 top /= src_pow2_height;
2136 bottom /= src_pow2_height;
2139 /* draw the source texture stretched and upside down. The correct surface is bound already */
2140 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2141 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2143 context_set_draw_buffer(context, drawBuffer);
2144 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
2146 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
2147 /* bottom left */
2148 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
2149 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
2151 /* top left */
2152 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
2153 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
2155 /* top right */
2156 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
2157 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2159 /* bottom right */
2160 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
2161 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
2162 gl_info->gl_ops.gl.p_glEnd();
2163 checkGLcall("glEnd and previous");
2165 if (texture_target != dst_surface->texture_target)
2167 gl_info->gl_ops.gl.p_glDisable(texture_target);
2168 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
2169 texture_target = dst_surface->texture_target;
2172 /* Now read the stretched and upside down image into the destination texture */
2173 context_bind_texture(context, texture_target, dst_texture->texture_rgb.name);
2174 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
2176 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
2177 0, 0, /* We blitted the image to the origin */
2178 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2179 checkGLcall("glCopyTexSubImage2D");
2181 if (drawBuffer == GL_BACK)
2183 /* Write the back buffer backup back. */
2184 if (backup)
2186 if (texture_target != GL_TEXTURE_2D)
2188 gl_info->gl_ops.gl.p_glDisable(texture_target);
2189 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
2190 texture_target = GL_TEXTURE_2D;
2192 context_bind_texture(context, GL_TEXTURE_2D, backup);
2194 else
2196 if (texture_target != src_surface->texture_target)
2198 gl_info->gl_ops.gl.p_glDisable(texture_target);
2199 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
2200 texture_target = src_surface->texture_target;
2202 context_bind_texture(context, src_surface->texture_target, src_texture->texture_rgb.name);
2205 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
2206 /* top left */
2207 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
2208 gl_info->gl_ops.gl.p_glVertex2i(0, src_height);
2210 /* bottom left */
2211 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)src_height / (float)src_pow2_height);
2212 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
2214 /* bottom right */
2215 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width,
2216 (float)src_height / (float)src_pow2_height);
2217 gl_info->gl_ops.gl.p_glVertex2i(src_width, 0);
2219 /* top right */
2220 gl_info->gl_ops.gl.p_glTexCoord2f((float)src_width / (float)src_pow2_width, 0.0f);
2221 gl_info->gl_ops.gl.p_glVertex2i(src_width, src_height);
2222 gl_info->gl_ops.gl.p_glEnd();
2224 gl_info->gl_ops.gl.p_glDisable(texture_target);
2225 checkGLcall("glDisable(texture_target)");
2227 /* Cleanup */
2228 if (src != src_texture->texture_rgb.name && src != backup)
2230 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
2231 checkGLcall("glDeleteTextures(1, &src)");
2233 if (backup)
2235 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
2236 checkGLcall("glDeleteTextures(1, &backup)");
2239 if (wined3d_settings.strict_draw_ordering)
2240 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2242 context_release(context);
2244 /* The texture is now most up to date - If the surface is a render target
2245 * and has a drawable, this path is never entered. */
2246 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB);
2247 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB);
2250 /* Front buffer coordinates are always full screen coordinates, but our GL
2251 * drawable is limited to the window's client area. The sysmem and texture
2252 * copies do have the full screen size. Note that GL has a bottom-left
2253 * origin, while D3D has a top-left origin. */
2254 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
2256 struct wined3d_texture *texture = surface->container;
2257 UINT drawable_height;
2259 if (texture->swapchain && texture == texture->swapchain->front_buffer)
2261 POINT offset = {0, 0};
2262 RECT windowsize;
2264 ScreenToClient(window, &offset);
2265 OffsetRect(rect, offset.x, offset.y);
2267 GetClientRect(window, &windowsize);
2268 drawable_height = windowsize.bottom - windowsize.top;
2270 else
2272 drawable_height = wined3d_texture_get_level_height(texture, surface->texture_level);
2275 rect->top = drawable_height - rect->top;
2276 rect->bottom = drawable_height - rect->bottom;
2279 /* Context activation is done by the caller. */
2280 static void surface_blt_to_drawable(const struct wined3d_device *device,
2281 struct wined3d_context *old_ctx,
2282 enum wined3d_texture_filter_type filter, BOOL alpha_test,
2283 struct wined3d_surface *src_surface, const RECT *src_rect_in,
2284 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
2286 struct wined3d_texture *src_texture = src_surface->container;
2287 struct wined3d_texture *dst_texture = dst_surface->container;
2288 const struct wined3d_gl_info *gl_info;
2289 struct wined3d_context *context = old_ctx;
2290 struct wined3d_surface *restore_rt = NULL;
2291 RECT src_rect, dst_rect;
2293 src_rect = *src_rect_in;
2294 dst_rect = *dst_rect_in;
2296 restore_rt = context_get_rt_surface(old_ctx);
2297 if (restore_rt != dst_surface)
2298 context = context_acquire(device, dst_surface);
2299 else
2300 restore_rt = NULL;
2302 gl_info = context->gl_info;
2304 /* Make sure the surface is up-to-date. This should probably use
2305 * surface_load_location() and worry about the destination surface too,
2306 * unless we're overwriting it completely. */
2307 wined3d_texture_load(src_texture, context, FALSE);
2309 /* Activate the destination context, set it up for blitting */
2310 context_apply_blit_state(context, device);
2312 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
2313 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
2315 device->blitter->set_shader(device->blit_priv, context, src_surface, NULL);
2317 if (alpha_test)
2319 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
2320 checkGLcall("glEnable(GL_ALPHA_TEST)");
2322 /* For P8 surfaces, the alpha component contains the palette index.
2323 * Which means that the colorkey is one of the palette entries. In
2324 * other cases pixels that should be masked away have alpha set to 0. */
2325 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT)
2326 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
2327 (float)src_texture->async.src_blt_color_key.color_space_low_value / 255.0f);
2328 else
2329 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
2330 checkGLcall("glAlphaFunc");
2332 else
2334 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2335 checkGLcall("glDisable(GL_ALPHA_TEST)");
2338 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
2340 if (alpha_test)
2342 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2343 checkGLcall("glDisable(GL_ALPHA_TEST)");
2346 /* Leave the opengl state valid for blitting */
2347 device->blitter->unset_shader(context->gl_info);
2349 if (wined3d_settings.strict_draw_ordering
2350 || (dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture))
2351 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2353 if (restore_rt)
2354 context_restore(context, restore_rt);
2357 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
2359 struct wined3d_resource *resource = &s->container->resource;
2360 struct wined3d_device *device = resource->device;
2361 struct wined3d_rendertarget_view_desc view_desc;
2362 struct wined3d_rendertarget_view *view;
2363 const struct blit_shader *blitter;
2364 HRESULT hr;
2366 if (!(blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info,
2367 WINED3D_BLIT_OP_COLOR_FILL, NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format)))
2369 FIXME("No blitter is capable of performing the requested color fill operation.\n");
2370 return WINED3DERR_INVALIDCALL;
2373 view_desc.format_id = resource->format->id;
2374 view_desc.u.texture.level_idx = s->texture_level;
2375 view_desc.u.texture.layer_idx = s->texture_layer;
2376 view_desc.u.texture.layer_count = 1;
2377 if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc,
2378 resource, NULL, &wined3d_null_parent_ops, &view)))
2380 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
2381 return hr;
2384 hr = blitter->color_fill(device, view, rect, color);
2385 wined3d_rendertarget_view_decref(view);
2387 return hr;
2390 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
2391 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
2392 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
2394 struct wined3d_texture *dst_texture = dst_surface->container;
2395 struct wined3d_device *device = dst_texture->resource.device;
2396 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
2397 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
2398 struct wined3d_texture *src_texture;
2400 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
2401 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
2402 flags, fx, debug_d3dtexturefiltertype(filter));
2404 /* Get the swapchain. One of the surfaces has to be a primary surface */
2405 if (dst_texture->resource.pool == WINED3D_POOL_SYSTEM_MEM)
2407 WARN("Destination is in sysmem, rejecting gl blt\n");
2408 return WINED3DERR_INVALIDCALL;
2411 dst_swapchain = dst_texture->swapchain;
2413 if (src_surface)
2415 src_texture = src_surface->container;
2416 if (src_texture->resource.pool == WINED3D_POOL_SYSTEM_MEM)
2418 WARN("Src is in sysmem, rejecting gl blt\n");
2419 return WINED3DERR_INVALIDCALL;
2422 src_swapchain = src_texture->swapchain;
2424 else
2426 src_texture = NULL;
2427 src_swapchain = NULL;
2430 /* Early sort out of cases where no render target is used */
2431 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
2433 TRACE("No surface is render target, not using hardware blit.\n");
2434 return WINED3DERR_INVALIDCALL;
2437 /* No destination color keying supported */
2438 if (flags & (WINED3D_BLT_DST_CKEY | WINED3D_BLT_DST_CKEY_OVERRIDE))
2440 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2441 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2442 return WINED3DERR_INVALIDCALL;
2445 if (dst_swapchain && dst_swapchain == src_swapchain)
2447 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
2448 return WINED3DERR_INVALIDCALL;
2451 if (dst_swapchain && src_swapchain)
2453 FIXME("Implement hardware blit between two different swapchains\n");
2454 return WINED3DERR_INVALIDCALL;
2457 if (dst_swapchain)
2459 /* Handled with regular texture -> swapchain blit */
2460 if (src_surface == rt)
2461 TRACE("Blit from active render target to a swapchain\n");
2463 else if (src_swapchain && dst_surface == rt)
2465 FIXME("Implement blit from a swapchain to the active render target\n");
2466 return WINED3DERR_INVALIDCALL;
2469 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
2471 unsigned int src_width, src_height;
2472 /* Blit from render target to texture */
2473 BOOL stretchx;
2475 /* P8 read back is not implemented */
2476 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT
2477 || dst_texture->resource.format->id == WINED3DFMT_P8_UINT)
2479 TRACE("P8 read back not supported by frame buffer to texture blit\n");
2480 return WINED3DERR_INVALIDCALL;
2483 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_SRC_CKEY_OVERRIDE))
2485 TRACE("Color keying not supported by frame buffer to texture blit\n");
2486 return WINED3DERR_INVALIDCALL;
2487 /* Destination color key is checked above */
2490 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
2491 stretchx = TRUE;
2492 else
2493 stretchx = FALSE;
2495 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2496 * flip the image nor scale it.
2498 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2499 * -> If the app wants an image width an unscaled width, copy it line per line
2500 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
2501 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2502 * back buffer. This is slower than reading line per line, thus not used for flipping
2503 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2504 * pixel by pixel. */
2505 src_width = wined3d_texture_get_level_width(src_texture, src_surface->texture_level);
2506 src_height = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
2507 if (!stretchx || dst_rect->right - dst_rect->left > src_width
2508 || dst_rect->bottom - dst_rect->top > src_height)
2510 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
2511 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
2513 else
2515 TRACE("Using hardware stretching to flip / stretch the texture.\n");
2516 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
2519 return WINED3D_OK;
2522 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
2523 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
2524 return WINED3DERR_INVALIDCALL;
2527 /* Context activation is done by the caller. */
2528 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
2529 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
2531 struct wined3d_device *device = surface->container->resource.device;
2532 const struct wined3d_gl_info *gl_info = context->gl_info;
2533 GLint compare_mode = GL_NONE;
2534 struct blt_info info;
2535 GLint old_binding = 0;
2536 RECT rect;
2538 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
2540 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
2541 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
2542 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2543 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
2544 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
2545 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
2546 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
2547 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
2548 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
2549 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
2550 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
2552 SetRect(&rect, 0, h, w, 0);
2553 surface_get_blt_info(target, &rect,
2554 wined3d_texture_get_level_pow2_width(surface->container, surface->texture_level),
2555 wined3d_texture_get_level_pow2_height(surface->container, surface->texture_level), &info);
2556 context_active_texture(context, context->gl_info, 0);
2557 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
2558 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
2559 if (gl_info->supported[ARB_SHADOW])
2561 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
2562 if (compare_mode != GL_NONE)
2563 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
2566 device->shader_backend->shader_select_depth_blt(device->shader_priv,
2567 gl_info, info.tex_type, &surface->ds_current_size);
2569 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
2570 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
2571 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
2572 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
2573 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
2574 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
2575 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
2576 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
2577 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
2578 gl_info->gl_ops.gl.p_glEnd();
2580 if (compare_mode != GL_NONE)
2581 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
2582 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
2584 gl_info->gl_ops.gl.p_glPopAttrib();
2586 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
2589 void surface_modify_ds_location(struct wined3d_surface *surface,
2590 DWORD location, UINT w, UINT h)
2592 struct wined3d_texture *texture = surface->container;
2593 unsigned int sub_resource_idx;
2595 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
2597 sub_resource_idx = surface_get_sub_resource_idx(surface);
2598 surface->ds_current_size.cx = w;
2599 surface->ds_current_size.cy = h;
2600 wined3d_texture_validate_location(texture, sub_resource_idx, location);
2601 wined3d_texture_invalidate_location(texture, sub_resource_idx, ~location);
2604 /* Context activation is done by the caller. */
2605 static void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
2607 struct wined3d_texture *texture = surface->container;
2608 struct wined3d_device *device = texture->resource.device;
2609 const struct wined3d_gl_info *gl_info = context->gl_info;
2610 GLsizei w, h;
2612 TRACE("surface %p, context %p, new location %#x.\n", surface, context, location);
2614 /* TODO: Make this work for modes other than FBO */
2615 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
2617 if (!(surface_get_sub_resource(surface)->locations & location))
2619 w = surface->ds_current_size.cx;
2620 h = surface->ds_current_size.cy;
2621 surface->ds_current_size.cx = 0;
2622 surface->ds_current_size.cy = 0;
2624 else
2626 w = wined3d_texture_get_level_width(texture, surface->texture_level);
2627 h = wined3d_texture_get_level_height(texture, surface->texture_level);
2630 if (surface->current_renderbuffer)
2632 FIXME("Not supported with fixed up depth stencil.\n");
2633 return;
2636 wined3d_surface_prepare(surface, context, location);
2638 if (location == WINED3D_LOCATION_TEXTURE_RGB)
2640 GLint old_binding = 0;
2641 GLenum bind_target;
2643 /* The render target is allowed to be smaller than the depth/stencil
2644 * buffer, so the onscreen depth/stencil buffer is potentially smaller
2645 * than the offscreen surface. Don't overwrite the offscreen surface
2646 * with undefined data. */
2647 w = min(w, context->swapchain->desc.backbuffer_width);
2648 h = min(h, context->swapchain->desc.backbuffer_height);
2650 TRACE("Copying onscreen depth buffer to depth texture.\n");
2652 if (!device->depth_blt_texture)
2653 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
2655 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
2656 * directly on the FBO texture. That's because we need to flip. */
2657 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
2658 context->swapchain->front_buffer->sub_resources[0].u.surface,
2659 NULL, WINED3D_LOCATION_DRAWABLE);
2660 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2662 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
2663 bind_target = GL_TEXTURE_RECTANGLE_ARB;
2665 else
2667 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
2668 bind_target = GL_TEXTURE_2D;
2670 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
2671 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
2672 * internal format, because the internal format might include stencil
2673 * data. In principle we should copy stencil data as well, but unless
2674 * the driver supports stencil export it's hard to do, and doesn't
2675 * seem to be needed in practice. If the hardware doesn't support
2676 * writing stencil data, the glCopyTexImage2D() call might trigger
2677 * software fallbacks. */
2678 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
2679 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2680 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2681 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2682 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2683 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2684 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
2686 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
2687 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
2688 context_set_draw_buffer(context, GL_NONE);
2690 /* Do the actual blit */
2691 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
2692 checkGLcall("depth_blt");
2694 context_invalidate_state(context, STATE_FRAMEBUFFER);
2696 if (wined3d_settings.strict_draw_ordering)
2697 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2699 else if (location == WINED3D_LOCATION_DRAWABLE)
2701 TRACE("Copying depth texture to onscreen depth buffer.\n");
2703 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
2704 context->swapchain->front_buffer->sub_resources[0].u.surface,
2705 NULL, WINED3D_LOCATION_DRAWABLE);
2706 surface_depth_blt(surface, context, texture->texture_rgb.name, 0,
2707 wined3d_texture_get_level_pow2_height(texture, surface->texture_level) - h,
2708 w, h, surface->texture_target);
2709 checkGLcall("depth_blt");
2711 context_invalidate_state(context, STATE_FRAMEBUFFER);
2713 if (wined3d_settings.strict_draw_ordering)
2714 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2716 else
2718 ERR("Invalid location (%#x) specified.\n", location);
2722 static DWORD resource_access_from_location(DWORD location)
2724 switch (location)
2726 case WINED3D_LOCATION_SYSMEM:
2727 case WINED3D_LOCATION_USER_MEMORY:
2728 case WINED3D_LOCATION_BUFFER:
2729 return WINED3D_RESOURCE_ACCESS_CPU;
2731 case WINED3D_LOCATION_DRAWABLE:
2732 case WINED3D_LOCATION_TEXTURE_SRGB:
2733 case WINED3D_LOCATION_TEXTURE_RGB:
2734 case WINED3D_LOCATION_RB_MULTISAMPLE:
2735 case WINED3D_LOCATION_RB_RESOLVED:
2736 return WINED3D_RESOURCE_ACCESS_GPU;
2738 default:
2739 FIXME("Unhandled location %#x.\n", location);
2740 return 0;
2744 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
2746 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2747 struct wined3d_texture *texture = surface->container;
2748 struct wined3d_device *device = texture->resource.device;
2749 struct wined3d_texture_sub_resource *sub_resource;
2750 struct wined3d_context *context;
2751 const struct wined3d_gl_info *gl_info;
2752 struct wined3d_bo_address dst, src;
2754 sub_resource = &texture->sub_resources[sub_resource_idx];
2755 wined3d_texture_get_memory(texture, sub_resource_idx, &dst, location);
2756 wined3d_texture_get_memory(texture, sub_resource_idx, &src, sub_resource->locations);
2758 if (dst.buffer_object)
2760 context = context_acquire(device, NULL);
2761 gl_info = context->gl_info;
2762 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, dst.buffer_object));
2763 GL_EXTCALL(glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, sub_resource->size, src.addr));
2764 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
2765 checkGLcall("Upload PBO");
2766 context_release(context);
2767 return;
2769 if (src.buffer_object)
2771 context = context_acquire(device, NULL);
2772 gl_info = context->gl_info;
2773 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, src.buffer_object));
2774 GL_EXTCALL(glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, sub_resource->size, dst.addr));
2775 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
2776 checkGLcall("Download PBO");
2777 context_release(context);
2778 return;
2780 memcpy(dst.addr, src.addr, sub_resource->size);
2783 /* Context activation is done by the caller. */
2784 static void surface_load_sysmem(struct wined3d_surface *surface,
2785 struct wined3d_context *context, DWORD dst_location)
2787 const struct wined3d_gl_info *gl_info = context->gl_info;
2788 struct wined3d_texture_sub_resource *sub_resource;
2790 wined3d_surface_prepare(surface, context, dst_location);
2792 sub_resource = surface_get_sub_resource(surface);
2793 if (sub_resource->locations & surface_simple_locations)
2795 surface_copy_simple_location(surface, dst_location);
2796 return;
2799 if (sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
2800 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
2802 /* Download the surface to system memory. */
2803 if (sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
2805 struct wined3d_texture *texture = surface->container;
2807 wined3d_texture_bind_and_dirtify(texture, context,
2808 !(sub_resource->locations & WINED3D_LOCATION_TEXTURE_RGB));
2809 surface_download_data(surface, gl_info, dst_location);
2810 ++texture->download_count;
2812 return;
2815 if (sub_resource->locations & WINED3D_LOCATION_DRAWABLE)
2817 read_from_framebuffer(surface, context, dst_location);
2818 return;
2821 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
2822 surface, wined3d_debug_location(sub_resource->locations));
2825 /* Context activation is done by the caller. */
2826 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
2827 struct wined3d_context *context)
2829 struct wined3d_texture *texture = surface->container;
2830 RECT r;
2832 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
2833 && wined3d_resource_is_offscreen(&texture->resource))
2835 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
2836 return WINED3DERR_INVALIDCALL;
2839 surface_get_rect(surface, NULL, &r);
2840 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
2841 surface_blt_to_drawable(texture->resource.device, context,
2842 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
2844 return WINED3D_OK;
2847 static HRESULT surface_load_texture(struct wined3d_surface *surface,
2848 struct wined3d_context *context, BOOL srgb)
2850 unsigned int width, height, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch;
2851 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
2852 const struct wined3d_gl_info *gl_info = context->gl_info;
2853 struct wined3d_texture *texture = surface->container;
2854 struct wined3d_device *device = texture->resource.device;
2855 const struct wined3d_color_key_conversion *conversion;
2856 struct wined3d_texture_sub_resource *sub_resource;
2857 struct wined3d_bo_address data;
2858 BYTE *src_mem, *dst_mem = NULL;
2859 struct wined3d_format format;
2860 POINT dst_point = {0, 0};
2861 RECT src_rect;
2863 sub_resource = surface_get_sub_resource(surface);
2864 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
2865 && wined3d_resource_is_offscreen(&texture->resource)
2866 && (sub_resource->locations & WINED3D_LOCATION_DRAWABLE))
2868 surface_load_fb_texture(surface, srgb, context);
2870 return WINED3D_OK;
2873 width = wined3d_texture_get_level_width(texture, surface->texture_level);
2874 height = wined3d_texture_get_level_height(texture, surface->texture_level);
2875 SetRect(&src_rect, 0, 0, width, height);
2877 if (sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
2878 && (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
2879 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
2880 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format,
2881 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format))
2883 if (srgb)
2884 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
2885 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
2886 else
2887 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
2888 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
2890 return WINED3D_OK;
2893 if (sub_resource->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
2894 && (!srgb || (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
2895 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
2896 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format,
2897 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format))
2899 DWORD src_location = sub_resource->locations & WINED3D_LOCATION_RB_RESOLVED ?
2900 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
2901 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
2903 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, src_location,
2904 &src_rect, surface, dst_location, &src_rect);
2906 return WINED3D_OK;
2909 /* Upload from system memory */
2911 if (srgb)
2913 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_RGB | texture->resource.map_binding))
2914 == WINED3D_LOCATION_TEXTURE_RGB)
2916 /* Performance warning... */
2917 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
2918 surface_load_location(surface, context, texture->resource.map_binding);
2921 else
2923 if ((sub_resource->locations & (WINED3D_LOCATION_TEXTURE_SRGB | texture->resource.map_binding))
2924 == WINED3D_LOCATION_TEXTURE_SRGB)
2926 /* Performance warning... */
2927 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
2928 surface_load_location(surface, context, texture->resource.map_binding);
2932 if (!(sub_resource->locations & surface_simple_locations))
2934 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
2935 /* Lets hope we get it from somewhere... */
2936 surface_load_location(surface, context, WINED3D_LOCATION_SYSMEM);
2939 wined3d_texture_prepare_texture(texture, context, srgb);
2940 wined3d_texture_bind_and_dirtify(texture, context, srgb);
2941 wined3d_texture_get_pitch(texture, surface->texture_level, &src_row_pitch, &src_slice_pitch);
2943 format = *texture->resource.format;
2944 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
2945 format = *wined3d_get_format(gl_info, conversion->dst_format);
2947 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
2948 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
2949 * getting called. */
2950 if ((format.convert || conversion) && texture->sub_resources[sub_resource_idx].buffer_object)
2952 TRACE("Removing the pbo attached to surface %p.\n", surface);
2954 surface_load_location(surface, context, WINED3D_LOCATION_SYSMEM);
2955 wined3d_texture_set_map_binding(texture, WINED3D_LOCATION_SYSMEM);
2958 wined3d_texture_get_memory(texture, sub_resource_idx, &data, sub_resource->locations);
2959 if (format.convert)
2961 /* This code is entered for texture formats which need a fixup. */
2962 format.byte_count = format.conv_byte_count;
2963 wined3d_format_calculate_pitch(&format, 1, width, height, &dst_row_pitch, &dst_slice_pitch);
2965 src_mem = wined3d_texture_map_bo_address(&data, src_slice_pitch,
2966 gl_info, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READONLY);
2967 if (!(dst_mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
2969 ERR("Out of memory (%u).\n", dst_slice_pitch);
2970 context_release(context);
2971 return E_OUTOFMEMORY;
2973 format.convert(src_mem, dst_mem, src_row_pitch, src_slice_pitch,
2974 dst_row_pitch, dst_slice_pitch, width, height, 1);
2975 src_row_pitch = dst_row_pitch;
2976 wined3d_texture_unmap_bo_address(&data, gl_info, GL_PIXEL_UNPACK_BUFFER);
2978 data.buffer_object = 0;
2979 data.addr = dst_mem;
2981 else if (conversion)
2983 /* This code is only entered for color keying fixups */
2984 struct wined3d_palette *palette = NULL;
2986 wined3d_format_calculate_pitch(&format, device->surface_alignment,
2987 width, height, &dst_row_pitch, &dst_slice_pitch);
2989 src_mem = wined3d_texture_map_bo_address(&data, src_slice_pitch,
2990 gl_info, GL_PIXEL_UNPACK_BUFFER, WINED3D_MAP_READONLY);
2991 if (!(dst_mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
2993 ERR("Out of memory (%u).\n", dst_slice_pitch);
2994 context_release(context);
2995 return E_OUTOFMEMORY;
2997 if (texture->swapchain && texture->swapchain->palette)
2998 palette = texture->swapchain->palette;
2999 conversion->convert(src_mem, src_row_pitch, dst_mem, dst_row_pitch,
3000 width, height, palette, &texture->async.gl_color_key);
3001 src_row_pitch = dst_row_pitch;
3002 wined3d_texture_unmap_bo_address(&data, gl_info, GL_PIXEL_UNPACK_BUFFER);
3004 data.buffer_object = 0;
3005 data.addr = dst_mem;
3008 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
3009 src_row_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
3011 HeapFree(GetProcessHeap(), 0, dst_mem);
3013 return WINED3D_OK;
3016 /* Context activation is done by the caller. */
3017 static void surface_load_renderbuffer(struct wined3d_surface *surface, struct wined3d_context *context,
3018 DWORD dst_location)
3020 struct wined3d_texture *texture = surface->container;
3021 const RECT rect = {0, 0,
3022 wined3d_texture_get_level_width(texture, surface->texture_level),
3023 wined3d_texture_get_level_height(texture, surface->texture_level)};
3024 DWORD locations = surface_get_sub_resource(surface)->locations;
3025 DWORD src_location;
3027 if (locations & WINED3D_LOCATION_RB_MULTISAMPLE)
3028 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
3029 else if (locations & WINED3D_LOCATION_RB_RESOLVED)
3030 src_location = WINED3D_LOCATION_RB_RESOLVED;
3031 else if (locations & WINED3D_LOCATION_TEXTURE_SRGB)
3032 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
3033 else /* surface_blt_fbo will load the source location if necessary. */
3034 src_location = WINED3D_LOCATION_TEXTURE_RGB;
3036 surface_blt_fbo(texture->resource.device, context, WINED3D_TEXF_POINT,
3037 surface, src_location, &rect, surface, dst_location, &rect);
3040 /* Context activation is done by the caller. Context may be NULL in ddraw-only mode. */
3041 HRESULT surface_load_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3043 unsigned int sub_resource_idx = surface_get_sub_resource_idx(surface);
3044 struct wined3d_texture *texture = surface->container;
3045 struct wined3d_texture_sub_resource *sub_resource;
3046 unsigned int surface_w, surface_h;
3047 HRESULT hr;
3049 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3051 surface_w = wined3d_texture_get_level_width(texture, surface->texture_level);
3052 surface_h = wined3d_texture_get_level_height(texture, surface->texture_level);
3054 sub_resource = &texture->sub_resources[sub_resource_idx];
3055 if (sub_resource->locations & location && (!(texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
3056 || (surface->ds_current_size.cx == surface_w && surface->ds_current_size.cy == surface_h)))
3058 TRACE("Location (%#x) is already up to date.\n", location);
3059 return WINED3D_OK;
3062 if (WARN_ON(d3d))
3064 DWORD required_access = resource_access_from_location(location);
3065 if ((texture->resource.access_flags & required_access) != required_access)
3066 WARN("Operation requires %#x access, but surface only has %#x.\n",
3067 required_access, texture->resource.access_flags);
3070 if (sub_resource->locations & WINED3D_LOCATION_DISCARDED)
3072 TRACE("Surface previously discarded, nothing to do.\n");
3073 wined3d_surface_prepare(surface, context, location);
3074 wined3d_texture_validate_location(texture, sub_resource_idx, location);
3075 wined3d_texture_invalidate_location(texture, sub_resource_idx, WINED3D_LOCATION_DISCARDED);
3076 goto done;
3079 if (!sub_resource->locations)
3081 ERR("Surface %p does not have any up to date location.\n", surface);
3082 wined3d_texture_validate_location(texture, sub_resource_idx, WINED3D_LOCATION_DISCARDED);
3083 return surface_load_location(surface, context, location);
3086 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
3088 if ((location == WINED3D_LOCATION_TEXTURE_RGB && sub_resource->locations & WINED3D_LOCATION_DRAWABLE)
3089 || (location == WINED3D_LOCATION_DRAWABLE && sub_resource->locations & WINED3D_LOCATION_TEXTURE_RGB))
3091 surface_load_ds_location(surface, context, location);
3092 goto done;
3095 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
3096 wined3d_debug_location(sub_resource->locations), wined3d_debug_location(location));
3097 return WINED3DERR_INVALIDCALL;
3100 switch (location)
3102 case WINED3D_LOCATION_USER_MEMORY:
3103 case WINED3D_LOCATION_SYSMEM:
3104 case WINED3D_LOCATION_BUFFER:
3105 surface_load_sysmem(surface, context, location);
3106 break;
3108 case WINED3D_LOCATION_DRAWABLE:
3109 if (FAILED(hr = surface_load_drawable(surface, context)))
3110 return hr;
3111 break;
3113 case WINED3D_LOCATION_RB_RESOLVED:
3114 case WINED3D_LOCATION_RB_MULTISAMPLE:
3115 surface_load_renderbuffer(surface, context, location);
3116 break;
3118 case WINED3D_LOCATION_TEXTURE_RGB:
3119 case WINED3D_LOCATION_TEXTURE_SRGB:
3120 if (FAILED(hr = surface_load_texture(surface, context,
3121 location == WINED3D_LOCATION_TEXTURE_SRGB)))
3122 return hr;
3123 break;
3125 default:
3126 ERR("Don't know how to handle location %#x.\n", location);
3127 break;
3130 done:
3131 wined3d_texture_validate_location(texture, sub_resource_idx, location);
3133 if (texture->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
3135 surface->ds_current_size.cx = surface_w;
3136 surface->ds_current_size.cy = surface_h;
3139 return WINED3D_OK;
3142 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
3143 /* Context activation is done by the caller. */
3144 static void ffp_blit_free(struct wined3d_device *device) { }
3146 /* Context activation is done by the caller. */
3147 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
3148 const struct wined3d_color_key *color_key)
3150 const struct wined3d_gl_info *gl_info = context->gl_info;
3152 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
3153 checkGLcall("glEnable(target)");
3155 return WINED3D_OK;
3158 /* Context activation is done by the caller. */
3159 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
3161 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
3162 checkGLcall("glDisable(GL_TEXTURE_2D)");
3163 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
3165 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3166 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3168 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
3170 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
3171 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
3175 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info,
3176 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
3177 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
3178 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
3180 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
3182 TRACE("Source or destination is in system memory.\n");
3183 return FALSE;
3186 switch (blit_op)
3188 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
3189 if (d3d_info->shader_color_key)
3191 TRACE("Color keying requires converted textures.\n");
3192 return FALSE;
3194 case WINED3D_BLIT_OP_COLOR_BLIT:
3195 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
3196 if (TRACE_ON(d3d))
3198 TRACE("Checking support for fixup:\n");
3199 dump_color_fixup_desc(src_format->color_fixup);
3202 /* We only support identity conversions. */
3203 if (!is_identity_fixup(src_format->color_fixup)
3204 || !is_identity_fixup(dst_format->color_fixup))
3206 TRACE("Fixups are not supported.\n");
3207 return FALSE;
3210 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
3212 TRACE("Can only blit to render targets.\n");
3213 return FALSE;
3215 return TRUE;
3217 case WINED3D_BLIT_OP_COLOR_FILL:
3218 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
3220 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
3221 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
3222 return FALSE;
3224 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
3226 TRACE("Color fill not supported\n");
3227 return FALSE;
3230 /* FIXME: We should reject color fills on formats with fixups,
3231 * but this would break P8 color fills for example. */
3233 return TRUE;
3235 case WINED3D_BLIT_OP_DEPTH_FILL:
3236 return TRUE;
3238 default:
3239 TRACE("Unsupported blit_op=%d\n", blit_op);
3240 return FALSE;
3244 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
3245 const RECT *rect, const struct wined3d_color *color)
3247 const RECT draw_rect = {0, 0, view->width, view->height};
3248 struct wined3d_fb_state fb = {&view, NULL};
3250 device_clear_render_targets(device, 1, &fb, 1, rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
3252 return WINED3D_OK;
3255 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
3256 struct wined3d_rendertarget_view *view, const RECT *rect, DWORD clear_flags,
3257 float depth, DWORD stencil)
3259 const RECT draw_rect = {0, 0, view->width, view->height};
3260 struct wined3d_fb_state fb = {NULL, view};
3262 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, clear_flags, NULL, depth, stencil);
3264 return WINED3D_OK;
3267 static void ffp_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
3268 struct wined3d_surface *src_surface, const RECT *src_rect,
3269 struct wined3d_surface *dst_surface, const RECT *dst_rect,
3270 const struct wined3d_color_key *color_key)
3272 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
3273 struct wined3d_texture *dst_texture = dst_surface->container;
3274 struct wined3d_texture *src_texture = src_surface->container;
3275 struct wined3d_context *context;
3277 /* Blit from offscreen surface to render target */
3278 struct wined3d_color_key old_blt_key = src_texture->async.src_blt_color_key;
3279 DWORD old_color_key_flags = src_texture->async.color_key_flags;
3281 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
3283 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT, color_key);
3285 context = context_acquire(device, dst_surface);
3287 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
3288 glEnable(GL_ALPHA_TEST);
3290 surface_blt_to_drawable(device, context, filter,
3291 !!color_key, src_surface, src_rect, dst_surface, dst_rect);
3293 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
3294 glDisable(GL_ALPHA_TEST);
3296 context_release(context);
3298 /* Restore the color key parameters */
3299 wined3d_texture_set_color_key(src_texture, WINED3D_CKEY_SRC_BLT,
3300 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
3302 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx, dst_texture->resource.draw_binding);
3303 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx, ~dst_texture->resource.draw_binding);
3306 const struct blit_shader ffp_blit = {
3307 ffp_blit_alloc,
3308 ffp_blit_free,
3309 ffp_blit_set,
3310 ffp_blit_unset,
3311 ffp_blit_supported,
3312 ffp_blit_color_fill,
3313 ffp_blit_depth_fill,
3314 ffp_blit_blit_surface,
3317 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
3319 return WINED3D_OK;
3322 /* Context activation is done by the caller. */
3323 static void cpu_blit_free(struct wined3d_device *device)
3327 /* Context activation is done by the caller. */
3328 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
3329 const struct wined3d_color_key *color_key)
3331 return WINED3D_OK;
3334 /* Context activation is done by the caller. */
3335 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
3339 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info,
3340 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
3341 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
3342 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
3344 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
3346 return TRUE;
3349 return FALSE;
3352 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
3353 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
3354 const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx)
3356 UINT row_block_count;
3357 const BYTE *src_row;
3358 BYTE *dst_row;
3359 UINT x, y;
3361 src_row = src_data;
3362 dst_row = dst_data;
3364 row_block_count = (update_w + format->block_width - 1) / format->block_width;
3366 if (!flags)
3368 for (y = 0; y < update_h; y += format->block_height)
3370 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
3371 src_row += src_pitch;
3372 dst_row += dst_pitch;
3375 return WINED3D_OK;
3378 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
3380 src_row += (((update_h / format->block_height) - 1) * src_pitch);
3382 switch (format->id)
3384 case WINED3DFMT_DXT1:
3385 for (y = 0; y < update_h; y += format->block_height)
3387 struct block
3389 WORD color[2];
3390 BYTE control_row[4];
3393 const struct block *s = (const struct block *)src_row;
3394 struct block *d = (struct block *)dst_row;
3396 for (x = 0; x < row_block_count; ++x)
3398 d[x].color[0] = s[x].color[0];
3399 d[x].color[1] = s[x].color[1];
3400 d[x].control_row[0] = s[x].control_row[3];
3401 d[x].control_row[1] = s[x].control_row[2];
3402 d[x].control_row[2] = s[x].control_row[1];
3403 d[x].control_row[3] = s[x].control_row[0];
3405 src_row -= src_pitch;
3406 dst_row += dst_pitch;
3408 return WINED3D_OK;
3410 case WINED3DFMT_DXT2:
3411 case WINED3DFMT_DXT3:
3412 for (y = 0; y < update_h; y += format->block_height)
3414 struct block
3416 WORD alpha_row[4];
3417 WORD color[2];
3418 BYTE control_row[4];
3421 const struct block *s = (const struct block *)src_row;
3422 struct block *d = (struct block *)dst_row;
3424 for (x = 0; x < row_block_count; ++x)
3426 d[x].alpha_row[0] = s[x].alpha_row[3];
3427 d[x].alpha_row[1] = s[x].alpha_row[2];
3428 d[x].alpha_row[2] = s[x].alpha_row[1];
3429 d[x].alpha_row[3] = s[x].alpha_row[0];
3430 d[x].color[0] = s[x].color[0];
3431 d[x].color[1] = s[x].color[1];
3432 d[x].control_row[0] = s[x].control_row[3];
3433 d[x].control_row[1] = s[x].control_row[2];
3434 d[x].control_row[2] = s[x].control_row[1];
3435 d[x].control_row[3] = s[x].control_row[0];
3437 src_row -= src_pitch;
3438 dst_row += dst_pitch;
3440 return WINED3D_OK;
3442 default:
3443 FIXME("Compressed flip not implemented for format %s.\n",
3444 debug_d3dformat(format->id));
3445 return E_NOTIMPL;
3449 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
3450 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
3452 return E_NOTIMPL;
3455 static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
3456 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
3457 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
3458 enum wined3d_texture_filter_type filter)
3460 unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count;
3461 const struct wined3d_format *src_format, *dst_format;
3462 struct wined3d_texture *converted_texture = NULL;
3463 unsigned int src_fmt_flags, dst_fmt_flags;
3464 struct wined3d_map_desc dst_map, src_map;
3465 const BYTE *sbase = NULL;
3466 HRESULT hr = WINED3D_OK;
3467 BOOL same_sub_resource;
3468 const BYTE *sbuf;
3469 BYTE *dbuf;
3470 int x, y;
3472 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
3473 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
3474 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture,
3475 src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
3477 if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx)
3479 same_sub_resource = TRUE;
3480 wined3d_resource_map(&dst_texture->resource, dst_sub_resource_idx, &dst_map, NULL, 0);
3481 src_map = dst_map;
3482 src_format = dst_texture->resource.format;
3483 dst_format = src_format;
3484 dst_fmt_flags = dst_texture->resource.format_flags;
3485 src_fmt_flags = dst_fmt_flags;
3487 else
3489 same_sub_resource = FALSE;
3490 dst_format = dst_texture->resource.format;
3491 dst_fmt_flags = dst_texture->resource.format_flags;
3492 if (src_texture)
3494 if (dst_texture->resource.format->id != src_texture->resource.format->id)
3496 if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format)))
3498 /* The conv function writes a FIXME */
3499 WARN("Cannot convert source surface format to dest format.\n");
3500 goto release;
3502 src_texture = converted_texture;
3503 src_sub_resource_idx = 0;
3505 wined3d_resource_map(&src_texture->resource, src_sub_resource_idx, &src_map, NULL, WINED3D_MAP_READONLY);
3506 src_format = src_texture->resource.format;
3507 src_fmt_flags = src_texture->resource.format_flags;
3509 else
3511 src_format = dst_format;
3512 src_fmt_flags = dst_fmt_flags;
3515 wined3d_resource_map(&dst_texture->resource, dst_sub_resource_idx, &dst_map, dst_box, 0);
3518 bpp = dst_format->byte_count;
3519 src_height = src_box->bottom - src_box->top;
3520 src_width = src_box->right - src_box->left;
3521 dst_height = dst_box->bottom - dst_box->top;
3522 dst_width = dst_box->right - dst_box->left;
3523 row_byte_count = dst_width * bpp;
3525 if (src_texture)
3526 sbase = (BYTE *)src_map.data
3527 + ((src_box->top / src_format->block_height) * src_map.row_pitch)
3528 + ((src_box->left / src_format->block_width) * src_format->block_byte_count);
3529 if (same_sub_resource)
3530 dbuf = (BYTE *)dst_map.data
3531 + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch)
3532 + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count);
3533 else
3534 dbuf = dst_map.data;
3536 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
3538 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
3540 if (same_sub_resource)
3542 FIXME("Only plain blits supported on compressed surfaces.\n");
3543 hr = E_NOTIMPL;
3544 goto release;
3547 if (src_height != dst_height || src_width != dst_width)
3549 WARN("Stretching not supported on compressed surfaces.\n");
3550 hr = WINED3DERR_INVALIDCALL;
3551 goto release;
3554 if (!wined3d_texture_check_block_align(src_texture,
3555 src_sub_resource_idx % src_texture->level_count, src_box))
3557 WARN("Source rectangle not block-aligned.\n");
3558 hr = WINED3DERR_INVALIDCALL;
3559 goto release;
3562 if (!wined3d_texture_check_block_align(dst_texture,
3563 dst_sub_resource_idx % dst_texture->level_count, dst_box))
3565 WARN("Destination rectangle not block-aligned.\n");
3566 hr = WINED3DERR_INVALIDCALL;
3567 goto release;
3570 hr = surface_cpu_blt_compressed(sbase, dbuf,
3571 src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height,
3572 src_format, flags, fx);
3573 goto release;
3576 /* First, all the 'source-less' blits */
3577 if (flags & WINED3D_BLT_COLOR_FILL)
3579 hr = _Blt_ColorFill(dbuf, dst_width, dst_height, bpp, dst_map.row_pitch, fx->fill_color);
3580 flags &= ~WINED3D_BLT_COLOR_FILL;
3583 if (flags & WINED3D_BLT_DEPTH_FILL)
3584 FIXME("WINED3D_BLT_DEPTH_FILL needs to be implemented!\n");
3586 /* Now the 'with source' blits. */
3587 if (src_texture)
3589 int sx, xinc, sy, yinc;
3591 if (!dst_width || !dst_height) /* Hmm... stupid program? */
3592 goto release;
3594 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
3595 && (src_width != dst_width || src_height != dst_height))
3597 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
3598 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
3601 xinc = (src_width << 16) / dst_width;
3602 yinc = (src_height << 16) / dst_height;
3604 if (!flags)
3606 /* No effects, we can cheat here. */
3607 if (dst_width == src_width)
3609 if (dst_height == src_height)
3611 /* No stretching in either direction. This needs to be as
3612 * fast as possible. */
3613 sbuf = sbase;
3615 /* Check for overlapping surfaces. */
3616 if (!same_sub_resource || dst_box->top < src_box->top
3617 || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
3619 /* No overlap, or dst above src, so copy from top downwards. */
3620 for (y = 0; y < dst_height; ++y)
3622 memcpy(dbuf, sbuf, row_byte_count);
3623 sbuf += src_map.row_pitch;
3624 dbuf += dst_map.row_pitch;
3627 else if (dst_box->top > src_box->top)
3629 /* Copy from bottom upwards. */
3630 sbuf += src_map.row_pitch * dst_height;
3631 dbuf += dst_map.row_pitch * dst_height;
3632 for (y = 0; y < dst_height; ++y)
3634 sbuf -= src_map.row_pitch;
3635 dbuf -= dst_map.row_pitch;
3636 memcpy(dbuf, sbuf, row_byte_count);
3639 else
3641 /* Src and dst overlapping on the same line, use memmove. */
3642 for (y = 0; y < dst_height; ++y)
3644 memmove(dbuf, sbuf, row_byte_count);
3645 sbuf += src_map.row_pitch;
3646 dbuf += dst_map.row_pitch;
3650 else
3652 /* Stretching in y direction only. */
3653 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3655 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3656 memcpy(dbuf, sbuf, row_byte_count);
3657 dbuf += dst_map.row_pitch;
3661 else
3663 /* Stretching in X direction. */
3664 int last_sy = -1;
3665 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3667 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3669 if ((sy >> 16) == (last_sy >> 16))
3671 /* This source row is the same as last source row -
3672 * Copy the already stretched row. */
3673 memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count);
3675 else
3677 #define STRETCH_ROW(type) \
3678 do { \
3679 const type *s = (const type *)sbuf; \
3680 type *d = (type *)dbuf; \
3681 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3682 d[x] = s[sx >> 16]; \
3683 } while(0)
3685 switch(bpp)
3687 case 1:
3688 STRETCH_ROW(BYTE);
3689 break;
3690 case 2:
3691 STRETCH_ROW(WORD);
3692 break;
3693 case 4:
3694 STRETCH_ROW(DWORD);
3695 break;
3696 case 3:
3698 const BYTE *s;
3699 BYTE *d = dbuf;
3700 for (x = sx = 0; x < dst_width; x++, sx+= xinc)
3702 DWORD pixel;
3704 s = sbuf + 3 * (sx >> 16);
3705 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3706 d[0] = (pixel ) & 0xff;
3707 d[1] = (pixel >> 8) & 0xff;
3708 d[2] = (pixel >> 16) & 0xff;
3709 d += 3;
3711 break;
3713 default:
3714 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
3715 hr = WINED3DERR_NOTAVAILABLE;
3716 goto error;
3718 #undef STRETCH_ROW
3720 dbuf += dst_map.row_pitch;
3721 last_sy = sy;
3725 else
3727 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
3728 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
3729 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
3730 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3731 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
3733 /* The color keying flags are checked for correctness in ddraw */
3734 if (flags & WINED3D_BLT_SRC_CKEY)
3736 keylow = src_texture->async.src_blt_color_key.color_space_low_value;
3737 keyhigh = src_texture->async.src_blt_color_key.color_space_high_value;
3739 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3741 keylow = fx->src_color_key.color_space_low_value;
3742 keyhigh = fx->src_color_key.color_space_high_value;
3745 if (flags & WINED3D_BLT_DST_CKEY)
3747 /* Destination color keys are taken from the source surface! */
3748 destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value;
3749 destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value;
3751 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
3753 destkeylow = fx->dst_color_key.color_space_low_value;
3754 destkeyhigh = fx->dst_color_key.color_space_high_value;
3757 if (bpp == 1)
3759 keymask = 0xff;
3761 else
3763 DWORD masks[3];
3764 get_color_masks(src_format, masks);
3765 keymask = masks[0]
3766 | masks[1]
3767 | masks[2];
3769 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3770 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
3773 if (flags & WINED3D_BLT_FX)
3775 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
3776 LONG tmpxy;
3777 dTopLeft = dbuf;
3778 dTopRight = dbuf + ((dst_width - 1) * bpp);
3779 dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
3780 dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
3782 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
3784 /* I don't think we need to do anything about this flag */
3785 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
3787 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
3789 tmp = dTopRight;
3790 dTopRight = dTopLeft;
3791 dTopLeft = tmp;
3792 tmp = dBottomRight;
3793 dBottomRight = dBottomLeft;
3794 dBottomLeft = tmp;
3795 dstxinc = dstxinc * -1;
3797 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
3799 tmp = dTopLeft;
3800 dTopLeft = dBottomLeft;
3801 dBottomLeft = tmp;
3802 tmp = dTopRight;
3803 dTopRight = dBottomRight;
3804 dBottomRight = tmp;
3805 dstyinc = dstyinc * -1;
3807 if (fx->fx & WINEDDBLTFX_NOTEARING)
3809 /* I don't think we need to do anything about this flag */
3810 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
3812 if (fx->fx & WINEDDBLTFX_ROTATE180)
3814 tmp = dBottomRight;
3815 dBottomRight = dTopLeft;
3816 dTopLeft = tmp;
3817 tmp = dBottomLeft;
3818 dBottomLeft = dTopRight;
3819 dTopRight = tmp;
3820 dstxinc = dstxinc * -1;
3821 dstyinc = dstyinc * -1;
3823 if (fx->fx & WINEDDBLTFX_ROTATE270)
3825 tmp = dTopLeft;
3826 dTopLeft = dBottomLeft;
3827 dBottomLeft = dBottomRight;
3828 dBottomRight = dTopRight;
3829 dTopRight = tmp;
3830 tmpxy = dstxinc;
3831 dstxinc = dstyinc;
3832 dstyinc = tmpxy;
3833 dstxinc = dstxinc * -1;
3835 if (fx->fx & WINEDDBLTFX_ROTATE90)
3837 tmp = dTopLeft;
3838 dTopLeft = dTopRight;
3839 dTopRight = dBottomRight;
3840 dBottomRight = dBottomLeft;
3841 dBottomLeft = tmp;
3842 tmpxy = dstxinc;
3843 dstxinc = dstyinc;
3844 dstyinc = tmpxy;
3845 dstyinc = dstyinc * -1;
3847 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
3849 /* I don't think we need to do anything about this flag */
3850 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
3852 dbuf = dTopLeft;
3853 flags &= ~(WINED3D_BLT_FX);
3856 #define COPY_COLORKEY_FX(type) \
3857 do { \
3858 const type *s; \
3859 type *d = (type *)dbuf, *dx, tmp; \
3860 for (y = sy = 0; y < dst_height; ++y, sy += yinc) \
3862 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
3863 dx = d; \
3864 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3866 tmp = s[sx >> 16]; \
3867 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
3868 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
3870 dx[0] = tmp; \
3872 dx = (type *)(((BYTE *)dx) + dstxinc); \
3874 d = (type *)(((BYTE *)d) + dstyinc); \
3876 } while(0)
3878 switch (bpp)
3880 case 1:
3881 COPY_COLORKEY_FX(BYTE);
3882 break;
3883 case 2:
3884 COPY_COLORKEY_FX(WORD);
3885 break;
3886 case 4:
3887 COPY_COLORKEY_FX(DWORD);
3888 break;
3889 case 3:
3891 const BYTE *s;
3892 BYTE *d = dbuf, *dx;
3893 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3895 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3896 dx = d;
3897 for (x = sx = 0; x < dst_width; ++x, sx+= xinc)
3899 DWORD pixel, dpixel = 0;
3900 s = sbuf + 3 * (sx>>16);
3901 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3902 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
3903 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
3904 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
3906 dx[0] = (pixel ) & 0xff;
3907 dx[1] = (pixel >> 8) & 0xff;
3908 dx[2] = (pixel >> 16) & 0xff;
3910 dx += dstxinc;
3912 d += dstyinc;
3914 break;
3916 default:
3917 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
3918 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
3919 hr = WINED3DERR_NOTAVAILABLE;
3920 goto error;
3921 #undef COPY_COLORKEY_FX
3926 error:
3927 if (flags)
3928 FIXME(" Unsupported flags %#x.\n", flags);
3930 release:
3931 wined3d_resource_unmap(&dst_texture->resource, dst_sub_resource_idx);
3932 if (src_texture && !same_sub_resource)
3933 wined3d_resource_unmap(&src_texture->resource, src_sub_resource_idx);
3934 if (converted_texture)
3935 wined3d_texture_decref(converted_texture);
3937 return hr;
3940 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
3941 const RECT *rect, const struct wined3d_color *color)
3943 const struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
3944 static const struct wined3d_box src_box;
3945 struct wined3d_blt_fx fx;
3947 fx.fill_color = wined3d_format_convert_from_float(view->format, color);
3948 return surface_cpu_blt(texture_from_resource(view->resource), view->sub_resource_idx,
3949 &box, NULL, 0, &src_box, WINED3D_BLT_COLOR_FILL, &fx, WINED3D_TEXF_POINT);
3952 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
3953 struct wined3d_rendertarget_view *view, const RECT *rect, DWORD clear_flags,
3954 float depth, DWORD stencil)
3956 FIXME("Depth/stencil filling not implemented by cpu_blit.\n");
3957 return WINED3DERR_INVALIDCALL;
3960 static void cpu_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
3961 struct wined3d_surface *src_surface, const RECT *src_rect,
3962 struct wined3d_surface *dst_surface, const RECT *dst_rect,
3963 const struct wined3d_color_key *color_key)
3965 /* FIXME: Remove error returns from surface_blt_cpu. */
3966 ERR("Blit method not implemented by cpu_blit.\n");
3969 const struct blit_shader cpu_blit = {
3970 cpu_blit_alloc,
3971 cpu_blit_free,
3972 cpu_blit_set,
3973 cpu_blit_unset,
3974 cpu_blit_supported,
3975 cpu_blit_color_fill,
3976 cpu_blit_depth_fill,
3977 cpu_blit_blit_surface,
3980 HRESULT wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3981 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
3982 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
3984 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
3985 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
3986 unsigned int dst_sub_resource_idx = surface_get_sub_resource_idx(dst_surface);
3987 struct wined3d_texture *dst_texture = dst_surface->container;
3988 struct wined3d_device *device = dst_texture->resource.device;
3989 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3990 struct wined3d_texture *src_texture = NULL;
3991 unsigned int dst_w, dst_h, src_w, src_h;
3992 unsigned int src_sub_resource_idx = 0;
3993 DWORD src_ds_flags, dst_ds_flags;
3994 BOOL scale, convert;
3996 static const DWORD simple_blit = WINED3D_BLT_ASYNC
3997 | WINED3D_BLT_COLOR_FILL
3998 | WINED3D_BLT_SRC_CKEY
3999 | WINED3D_BLT_SRC_CKEY_OVERRIDE
4000 | WINED3D_BLT_WAIT
4001 | WINED3D_BLT_DEPTH_FILL
4002 | WINED3D_BLT_DO_NOT_WAIT
4003 | WINED3D_BLT_ALPHA_TEST;
4005 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
4006 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4007 flags, fx, debug_d3dtexturefiltertype(filter));
4008 TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage));
4010 if (fx)
4012 TRACE("fx %#x.\n", fx->fx);
4013 TRACE("fill_color 0x%08x.\n", fx->fill_color);
4014 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
4015 fx->dst_color_key.color_space_low_value,
4016 fx->dst_color_key.color_space_high_value);
4017 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
4018 fx->src_color_key.color_space_low_value,
4019 fx->src_color_key.color_space_high_value);
4022 if (src_surface)
4024 src_texture = src_surface->container;
4025 src_sub_resource_idx = surface_get_sub_resource_idx(src_surface);
4028 if (dst_texture->sub_resources[dst_sub_resource_idx].map_count
4029 || (src_texture && src_texture->sub_resources[src_sub_resource_idx].map_count))
4031 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
4032 return WINEDDERR_SURFACEBUSY;
4035 dst_w = wined3d_texture_get_level_width(dst_texture, dst_surface->texture_level);
4036 dst_h = wined3d_texture_get_level_height(dst_texture, dst_surface->texture_level);
4037 if (dst_rect->left >= dst_rect->right || dst_rect->top >= dst_rect->bottom
4038 || dst_rect->left > dst_w || dst_rect->left < 0
4039 || dst_rect->top > dst_h || dst_rect->top < 0
4040 || dst_rect->right > dst_w || dst_rect->right < 0
4041 || dst_rect->bottom > dst_h || dst_rect->bottom < 0)
4043 WARN("The application gave us a bad destination rectangle.\n");
4044 return WINEDDERR_INVALIDRECT;
4047 if (src_texture)
4049 src_w = wined3d_texture_get_level_width(src_texture, src_surface->texture_level);
4050 src_h = wined3d_texture_get_level_height(src_texture, src_surface->texture_level);
4051 if (src_rect->left >= src_rect->right || src_rect->top >= src_rect->bottom
4052 || src_rect->left > src_w || src_rect->left < 0
4053 || src_rect->top > src_h || src_rect->top < 0
4054 || src_rect->right > src_w || src_rect->right < 0
4055 || src_rect->bottom > src_h || src_rect->bottom < 0)
4057 WARN("The application gave us a bad source rectangle.\n");
4058 return WINEDDERR_INVALIDRECT;
4062 if (!fx || !(fx->fx))
4063 flags &= ~WINED3D_BLT_FX;
4065 if (flags & WINED3D_BLT_WAIT)
4066 flags &= ~WINED3D_BLT_WAIT;
4068 if (flags & WINED3D_BLT_ASYNC)
4070 static unsigned int once;
4072 if (!once++)
4073 FIXME("Can't handle WINED3D_BLT_ASYNC flag.\n");
4074 flags &= ~WINED3D_BLT_ASYNC;
4077 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
4078 if (flags & WINED3D_BLT_DO_NOT_WAIT)
4080 static unsigned int once;
4082 if (!once++)
4083 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
4084 flags &= ~WINED3D_BLT_DO_NOT_WAIT;
4087 if (!device->d3d_initialized)
4089 WARN("D3D not initialized, using fallback.\n");
4090 goto cpu;
4093 /* We want to avoid invalidating the sysmem location for converted
4094 * surfaces, since otherwise we'd have to convert the data back when
4095 * locking them. */
4096 if (dst_texture->flags & WINED3D_TEXTURE_CONVERTED || dst_texture->resource.format->convert
4097 || wined3d_format_get_color_key_conversion(dst_texture, TRUE))
4099 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
4100 goto cpu;
4103 if (flags & ~simple_blit)
4105 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
4106 goto fallback;
4109 if (src_texture)
4110 src_swapchain = src_texture->swapchain;
4111 else
4112 src_swapchain = NULL;
4114 dst_swapchain = dst_texture->swapchain;
4116 /* This isn't strictly needed. FBO blits for example could deal with
4117 * cross-swapchain blits by first downloading the source to a texture
4118 * before switching to the destination context. We just have this here to
4119 * not have to deal with the issue, since cross-swapchain blits should be
4120 * rare. */
4121 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
4123 FIXME("Using fallback for cross-swapchain blit.\n");
4124 goto fallback;
4127 scale = src_texture
4128 && (src_rect->right - src_rect->left != dst_rect->right - dst_rect->left
4129 || src_rect->bottom - src_rect->top != dst_rect->bottom - dst_rect->top);
4130 convert = src_texture && src_texture->resource.format->id != dst_texture->resource.format->id;
4132 dst_ds_flags = dst_texture->resource.format_flags
4133 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
4134 if (src_texture)
4135 src_ds_flags = src_texture->resource.format_flags
4136 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
4137 else
4138 src_ds_flags = 0;
4140 if (src_ds_flags || dst_ds_flags)
4142 if (flags & WINED3D_BLT_DEPTH_FILL)
4144 float depth;
4146 TRACE("Depth fill.\n");
4148 if (!surface_convert_depth_to_float(dst_surface, fx->fill_color, &depth))
4149 return WINED3DERR_INVALIDCALL;
4151 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, dst_rect, depth)))
4152 return WINED3D_OK;
4154 else
4156 if (src_ds_flags != dst_ds_flags)
4158 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
4159 return WINED3DERR_INVALIDCALL;
4162 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_texture->resource.draw_binding,
4163 src_rect, dst_surface, dst_texture->resource.draw_binding, dst_rect)))
4164 return WINED3D_OK;
4167 else
4169 struct wined3d_texture_sub_resource *src_sub_resource, *dst_sub_resource;
4170 const struct blit_shader *blitter;
4172 dst_sub_resource = surface_get_sub_resource(dst_surface);
4173 src_sub_resource = src_texture ? &src_texture->sub_resources[src_sub_resource_idx] : NULL;
4175 /* In principle this would apply to depth blits as well, but we don't
4176 * implement those in the CPU blitter at the moment. */
4177 if ((dst_sub_resource->locations & dst_texture->resource.map_binding)
4178 && (!src_texture || (src_sub_resource->locations & src_texture->resource.map_binding)))
4180 if (scale)
4181 TRACE("Not doing sysmem blit because of scaling.\n");
4182 else if (convert)
4183 TRACE("Not doing sysmem blit because of format conversion.\n");
4184 else
4185 goto cpu;
4188 if (flags & WINED3D_BLT_COLOR_FILL)
4190 struct wined3d_color color;
4191 const struct wined3d_palette *palette = dst_swapchain ? dst_swapchain->palette : NULL;
4193 TRACE("Color fill.\n");
4195 if (!wined3d_format_convert_color_to_float(dst_texture->resource.format,
4196 palette, fx->fill_color, &color))
4197 goto fallback;
4199 if (SUCCEEDED(surface_color_fill(dst_surface, dst_rect, &color)))
4200 return WINED3D_OK;
4202 else
4204 enum wined3d_blit_op blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
4205 const struct wined3d_color_key *color_key = NULL;
4207 TRACE("Color blit.\n");
4208 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
4210 color_key = &fx->src_color_key;
4211 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
4213 else if (flags & WINED3D_BLT_SRC_CKEY)
4215 color_key = &src_texture->async.src_blt_color_key;
4216 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
4218 else if (flags & WINED3D_BLT_ALPHA_TEST)
4220 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
4222 else if ((src_sub_resource->locations & WINED3D_LOCATION_SYSMEM)
4223 && !(dst_sub_resource->locations & WINED3D_LOCATION_SYSMEM))
4225 /* Upload */
4226 if (scale)
4227 TRACE("Not doing upload because of scaling.\n");
4228 else if (convert)
4229 TRACE("Not doing upload because of format conversion.\n");
4230 else
4232 POINT dst_point = {dst_rect->left, dst_rect->top};
4234 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, src_rect)))
4236 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
4238 struct wined3d_context *context = context_acquire(device, dst_surface);
4239 surface_load_location(dst_surface, context, dst_texture->resource.draw_binding);
4240 context_release(context);
4242 return WINED3D_OK;
4246 else if (dst_swapchain && dst_swapchain->back_buffers
4247 && dst_texture == dst_swapchain->front_buffer
4248 && src_texture == dst_swapchain->back_buffers[0])
4250 /* Use present for back -> front blits. The idea behind this is
4251 * that present is potentially faster than a blit, in particular
4252 * when FBO blits aren't available. Some ddraw applications like
4253 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
4254 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
4255 * applications can't blit directly to the frontbuffer. */
4256 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
4258 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
4260 /* Set the swap effect to COPY, we don't want the backbuffer
4261 * to become undefined. */
4262 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
4263 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, 0);
4264 dst_swapchain->desc.swap_effect = swap_effect;
4266 return WINED3D_OK;
4269 if (fbo_blit_supported(&device->adapter->gl_info, blit_op,
4270 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
4271 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format))
4273 struct wined3d_context *context;
4274 TRACE("Using FBO blit.\n");
4276 context = context_acquire(device, NULL);
4277 surface_blt_fbo(device, context, filter,
4278 src_surface, src_texture->resource.draw_binding, src_rect,
4279 dst_surface, dst_texture->resource.draw_binding, dst_rect);
4280 context_release(context);
4282 wined3d_texture_validate_location(dst_texture, dst_sub_resource_idx,
4283 dst_texture->resource.draw_binding);
4284 wined3d_texture_invalidate_location(dst_texture, dst_sub_resource_idx,
4285 ~dst_texture->resource.draw_binding);
4287 return WINED3D_OK;
4290 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, blit_op,
4291 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
4292 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format);
4293 if (blitter)
4295 blitter->blit_surface(device, blit_op, filter, src_surface,
4296 src_rect, dst_surface, dst_rect, color_key);
4297 return WINED3D_OK;
4302 fallback:
4303 /* Special cases for render targets. */
4304 if (SUCCEEDED(surface_blt_special(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter)))
4305 return WINED3D_OK;
4307 cpu:
4308 return surface_cpu_blt(dst_texture, dst_sub_resource_idx, &dst_box,
4309 src_texture, src_sub_resource_idx, &src_box, flags, fx, filter);
4312 HRESULT wined3d_surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
4313 const struct wined3d_resource_desc *desc, GLenum target, unsigned int level, unsigned int layer)
4315 struct wined3d_device *device = container->resource.device;
4316 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4317 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
4318 UINT multisample_quality = desc->multisample_quality;
4319 HRESULT hr;
4321 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE,
4322 format, desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height,
4323 1, 0, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
4325 WARN("Failed to initialize resource, returning %#x.\n", hr);
4326 return hr;
4328 surface->resource.access_flags = container->resource.access_flags;
4330 surface->container = container;
4331 surface->texture_target = target;
4332 surface->texture_level = level;
4333 surface->texture_layer = layer;
4335 list_init(&surface->renderbuffers);
4336 list_init(&surface->overlays);
4338 return hr;
4341 /* Context activation is done by the caller. Context may be NULL in
4342 * WINED3D_NO3D mode. */
4343 void wined3d_surface_prepare(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
4345 wined3d_texture_prepare_location(surface->container,
4346 surface_get_sub_resource_idx(surface), context, location);