makefiles: Always expand the library name for cross-compiled import libraries.
[wine.git] / dlls / wined3d / surface.c
blobcab1451d589d93f04fd94b1adc93e9cebade2b03
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_surface);
34 WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
35 WINE_DECLARE_DEBUG_CHANNEL(d3d);
37 #define MAXLOCKCOUNT 50 /* After this amount of locks do not free the sysmem copy. */
39 static const DWORD surface_simple_locations =
40 WINED3D_LOCATION_SYSMEM | WINED3D_LOCATION_USER_MEMORY
41 | WINED3D_LOCATION_DIB | WINED3D_LOCATION_BUFFER;
43 static unsigned int surface_get_sub_resource_idx(const struct wined3d_surface *surface)
45 return surface->texture_layer * surface->container->level_count + surface->texture_level;
48 void wined3d_surface_cleanup(struct wined3d_surface *surface)
50 struct wined3d_surface *overlay, *cur;
52 TRACE("surface %p.\n", surface);
54 if (surface->rb_multisample || surface->rb_resolved || !list_empty(&surface->renderbuffers))
56 struct wined3d_device *device = surface->container->resource.device;
57 struct wined3d_renderbuffer_entry *entry, *entry2;
58 const struct wined3d_gl_info *gl_info;
59 struct wined3d_context *context;
61 context = context_acquire(device, NULL);
62 gl_info = context->gl_info;
64 if (surface->rb_multisample)
66 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
67 context_gl_resource_released(device, surface->rb_multisample, TRUE);
68 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
71 if (surface->rb_resolved)
73 TRACE("Deleting resolved renderbuffer %u.\n", surface->rb_resolved);
74 context_gl_resource_released(device, surface->rb_resolved, TRUE);
75 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
78 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
80 TRACE("Deleting renderbuffer %u.\n", entry->id);
81 context_gl_resource_released(device, entry->id, TRUE);
82 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
83 HeapFree(GetProcessHeap(), 0, entry);
86 context_release(context);
89 if (surface->flags & SFLAG_DIBSECTION)
91 DeleteDC(surface->hDC);
92 DeleteObject(surface->dib.DIBsection);
93 surface->dib.bitmap_data = NULL;
96 if (surface->overlay_dest)
97 list_remove(&surface->overlay_entry);
99 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
101 list_remove(&overlay->overlay_entry);
102 overlay->overlay_dest = NULL;
105 resource_cleanup(&surface->resource);
108 void surface_get_drawable_size(const struct wined3d_surface *surface, const struct wined3d_context *context,
109 unsigned int *width, unsigned int *height)
111 if (surface->container->swapchain)
113 /* The drawable size of an onscreen drawable is the surface size.
114 * (Actually: The window size, but the surface is created in window
115 * size.) */
116 *width = context->current_rt->resource.width;
117 *height = context->current_rt->resource.height;
119 else if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER)
121 const struct wined3d_swapchain *swapchain = context->swapchain;
123 /* The drawable size of a backbuffer / aux buffer offscreen target is
124 * the size of the current context's drawable, which is the size of
125 * the back buffer of the swapchain the active context belongs to. */
126 *width = swapchain->desc.backbuffer_width;
127 *height = swapchain->desc.backbuffer_height;
129 else
131 /* The drawable size of an FBO target is the OpenGL texture size,
132 * which is the power of two size. */
133 *width = context->current_rt->pow2Width;
134 *height = context->current_rt->pow2Height;
138 struct blt_info
140 GLenum binding;
141 GLenum bind_target;
142 enum wined3d_gl_resource_type tex_type;
143 GLfloat coords[4][3];
146 struct float_rect
148 float l;
149 float t;
150 float r;
151 float b;
154 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
156 f->l = ((r->left * 2.0f) / w) - 1.0f;
157 f->t = ((r->top * 2.0f) / h) - 1.0f;
158 f->r = ((r->right * 2.0f) / w) - 1.0f;
159 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
162 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
164 GLfloat (*coords)[3] = info->coords;
165 struct float_rect f;
167 switch (target)
169 default:
170 FIXME("Unsupported texture target %#x\n", target);
171 /* Fall back to GL_TEXTURE_2D */
172 case GL_TEXTURE_2D:
173 info->binding = GL_TEXTURE_BINDING_2D;
174 info->bind_target = GL_TEXTURE_2D;
175 info->tex_type = WINED3D_GL_RES_TYPE_TEX_2D;
176 coords[0][0] = (float)rect->left / w;
177 coords[0][1] = (float)rect->top / h;
178 coords[0][2] = 0.0f;
180 coords[1][0] = (float)rect->right / w;
181 coords[1][1] = (float)rect->top / h;
182 coords[1][2] = 0.0f;
184 coords[2][0] = (float)rect->left / w;
185 coords[2][1] = (float)rect->bottom / h;
186 coords[2][2] = 0.0f;
188 coords[3][0] = (float)rect->right / w;
189 coords[3][1] = (float)rect->bottom / h;
190 coords[3][2] = 0.0f;
191 break;
193 case GL_TEXTURE_RECTANGLE_ARB:
194 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
195 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
196 info->tex_type = WINED3D_GL_RES_TYPE_TEX_RECT;
197 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
198 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
199 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
200 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
201 break;
203 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
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] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
210 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
211 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
212 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
213 break;
215 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
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] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
222 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
223 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
224 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
225 break;
227 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
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] = 1.0f; coords[0][2] = f.t;
234 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
235 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
236 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
237 break;
239 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
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] = -1.0f; coords[0][2] = -f.t;
246 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
247 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
248 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
249 break;
251 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
252 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
253 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
254 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
255 cube_coords_float(rect, w, h, &f);
257 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
258 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
259 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
260 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
261 break;
263 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
264 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
265 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
266 info->tex_type = WINED3D_GL_RES_TYPE_TEX_CUBE;
267 cube_coords_float(rect, w, h, &f);
269 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
270 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
271 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
272 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
273 break;
277 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
279 if (rect_in)
280 *rect_out = *rect_in;
281 else
283 rect_out->left = 0;
284 rect_out->top = 0;
285 rect_out->right = surface->resource.width;
286 rect_out->bottom = surface->resource.height;
290 /* Context activation is done by the caller. */
291 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
292 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
294 const struct wined3d_gl_info *gl_info = context->gl_info;
295 struct wined3d_texture *texture = src_surface->container;
296 struct blt_info info;
298 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
300 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
301 checkGLcall("glEnable(bind_target)");
303 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
305 /* Filtering for StretchRect */
306 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
307 checkGLcall("glTexParameteri");
308 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
309 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
310 checkGLcall("glTexParameteri");
311 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
312 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
313 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
314 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
315 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
316 checkGLcall("glTexEnvi");
318 /* Draw a quad */
319 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
320 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
321 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
323 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
324 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
326 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
327 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
329 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
330 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
331 gl_info->gl_ops.gl.p_glEnd();
333 /* Unbind the texture */
334 context_bind_texture(context, info.bind_target, 0);
336 /* We changed the filtering settings on the texture. Inform the
337 * container about this to get the filters reset properly next draw. */
338 texture->texture_rgb.sampler_desc.mag_filter = WINED3D_TEXF_POINT;
339 texture->texture_rgb.sampler_desc.min_filter = WINED3D_TEXF_POINT;
340 texture->texture_rgb.sampler_desc.mip_filter = WINED3D_TEXF_NONE;
341 texture->texture_rgb.sampler_desc.srgb_decode = FALSE;
344 /* Works correctly only for <= 4 bpp formats. */
345 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
347 masks[0] = ((1u << format->red_size) - 1) << format->red_offset;
348 masks[1] = ((1u << format->green_size) - 1) << format->green_offset;
349 masks[2] = ((1u << format->blue_size) - 1) << format->blue_offset;
352 HRESULT surface_create_dib_section(struct wined3d_surface *surface)
354 struct wined3d_texture *texture = surface->container;
355 const struct wined3d_format *format = texture->resource.format;
356 unsigned int format_flags = texture->resource.format_flags;
357 unsigned int row_pitch, slice_pitch;
358 BITMAPINFO *b_info;
359 DWORD *masks;
361 TRACE("surface %p.\n", surface);
363 if (!(format_flags & WINED3DFMT_FLAG_GETDC))
365 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
366 return WINED3DERR_INVALIDCALL;
369 switch (format->byte_count)
371 case 2:
372 case 4:
373 /* Allocate extra space to store the RGB bit masks. */
374 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, FIELD_OFFSET(BITMAPINFO, bmiColors[3]));
375 break;
377 case 3:
378 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, FIELD_OFFSET(BITMAPINFO, bmiColors[0]));
379 break;
381 default:
382 /* Allocate extra space for a palette. */
383 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
384 FIELD_OFFSET(BITMAPINFO, bmiColors[1u << (format->byte_count * 8)]));
385 break;
388 if (!b_info)
389 return E_OUTOFMEMORY;
391 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
392 wined3d_texture_get_pitch(texture, surface->texture_level, &row_pitch, &slice_pitch);
393 b_info->bmiHeader.biWidth = row_pitch / format->byte_count;
394 b_info->bmiHeader.biHeight = 0 - surface->resource.height;
395 b_info->bmiHeader.biSizeImage = slice_pitch;
396 b_info->bmiHeader.biPlanes = 1;
397 b_info->bmiHeader.biBitCount = format->byte_count * 8;
399 b_info->bmiHeader.biXPelsPerMeter = 0;
400 b_info->bmiHeader.biYPelsPerMeter = 0;
401 b_info->bmiHeader.biClrUsed = 0;
402 b_info->bmiHeader.biClrImportant = 0;
404 /* Get the bit masks */
405 masks = (DWORD *)b_info->bmiColors;
406 switch (format->id)
408 case WINED3DFMT_B8G8R8_UNORM:
409 b_info->bmiHeader.biCompression = BI_RGB;
410 break;
412 case WINED3DFMT_B5G5R5X1_UNORM:
413 case WINED3DFMT_B5G5R5A1_UNORM:
414 case WINED3DFMT_B4G4R4A4_UNORM:
415 case WINED3DFMT_B4G4R4X4_UNORM:
416 case WINED3DFMT_B2G3R3_UNORM:
417 case WINED3DFMT_B2G3R3A8_UNORM:
418 case WINED3DFMT_R10G10B10A2_UNORM:
419 case WINED3DFMT_R8G8B8A8_UNORM:
420 case WINED3DFMT_R8G8B8X8_UNORM:
421 case WINED3DFMT_B10G10R10A2_UNORM:
422 case WINED3DFMT_B5G6R5_UNORM:
423 case WINED3DFMT_R16G16B16A16_UNORM:
424 b_info->bmiHeader.biCompression = BI_BITFIELDS;
425 get_color_masks(format, masks);
426 break;
428 default:
429 /* Don't know palette */
430 b_info->bmiHeader.biCompression = BI_RGB;
431 break;
434 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
435 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
436 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
437 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
439 if (!surface->dib.DIBsection)
441 ERR("Failed to create DIB section.\n");
442 HeapFree(GetProcessHeap(), 0, b_info);
443 return HRESULT_FROM_WIN32(GetLastError());
446 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
447 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
449 HeapFree(GetProcessHeap(), 0, b_info);
451 /* Now allocate a DC. */
452 surface->hDC = CreateCompatibleDC(0);
453 SelectObject(surface->hDC, surface->dib.DIBsection);
455 surface->flags |= SFLAG_DIBSECTION;
457 return WINED3D_OK;
460 static void surface_get_memory(const struct wined3d_surface *surface, struct wined3d_bo_address *data,
461 DWORD location)
463 if (location & WINED3D_LOCATION_BUFFER)
465 data->addr = NULL;
466 data->buffer_object = surface->container->sub_resources[surface_get_sub_resource_idx(surface)].buffer_object;
467 return;
469 if (location & WINED3D_LOCATION_USER_MEMORY)
471 data->addr = surface->container->user_memory;
472 data->buffer_object = 0;
473 return;
475 if (location & WINED3D_LOCATION_DIB)
477 data->addr = surface->dib.bitmap_data;
478 data->buffer_object = 0;
479 return;
481 if (location & WINED3D_LOCATION_SYSMEM)
483 data->addr = surface->resource.heap_memory;
484 data->buffer_object = 0;
485 return;
488 ERR("Unexpected locations %s.\n", wined3d_debug_location(location));
489 data->addr = NULL;
490 data->buffer_object = 0;
493 static void surface_prepare_system_memory(struct wined3d_surface *surface)
495 TRACE("surface %p.\n", surface);
497 if (surface->resource.heap_memory)
498 return;
500 /* Whatever surface we have, make sure that there is memory allocated
501 * for the downloaded copy, or a PBO to map. */
502 if (!wined3d_resource_allocate_sysmem(&surface->resource))
503 ERR("Failed to allocate system memory.\n");
505 if (surface->locations & WINED3D_LOCATION_SYSMEM)
506 ERR("Surface without system memory has WINED3D_LOCATION_SYSMEM set.\n");
509 static void surface_evict_sysmem(struct wined3d_surface *surface)
511 struct wined3d_texture *texture = surface->container;
513 if (surface->resource.map_count || texture->download_count > MAXLOCKCOUNT
514 || texture->flags & (WINED3D_TEXTURE_CONVERTED | WINED3D_TEXTURE_PIN_SYSMEM))
515 return;
517 wined3d_resource_free_sysmem(&surface->resource);
518 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
521 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
523 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
524 return FALSE;
525 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
526 return FALSE;
527 return TRUE;
530 static void surface_depth_blt_fbo(const struct wined3d_device *device,
531 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
532 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
534 const struct wined3d_gl_info *gl_info;
535 struct wined3d_context *context;
536 DWORD src_mask, dst_mask;
537 GLbitfield gl_mask;
539 TRACE("device %p\n", device);
540 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
541 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
542 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
543 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
545 src_mask = src_surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
546 dst_mask = dst_surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
548 if (src_mask != dst_mask)
550 ERR("Incompatible formats %s and %s.\n",
551 debug_d3dformat(src_surface->container->resource.format->id),
552 debug_d3dformat(dst_surface->container->resource.format->id));
553 return;
556 if (!src_mask)
558 ERR("Not a depth / stencil format: %s.\n",
559 debug_d3dformat(src_surface->container->resource.format->id));
560 return;
563 gl_mask = 0;
564 if (src_mask & WINED3DFMT_FLAG_DEPTH)
565 gl_mask |= GL_DEPTH_BUFFER_BIT;
566 if (src_mask & WINED3DFMT_FLAG_STENCIL)
567 gl_mask |= GL_STENCIL_BUFFER_BIT;
569 context = context_acquire(device, NULL);
570 if (!context->valid)
572 context_release(context);
573 WARN("Invalid context, skipping blit.\n");
574 return;
577 /* Make sure the locations are up-to-date. Loading the destination
578 * surface isn't required if the entire surface is overwritten. */
579 surface_load_location(src_surface, context, src_location);
580 if (!surface_is_full_rect(dst_surface, dst_rect))
581 surface_load_location(dst_surface, context, dst_location);
582 else
583 wined3d_surface_prepare(dst_surface, context, dst_location);
585 gl_info = context->gl_info;
587 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
588 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
590 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
591 context_set_draw_buffer(context, GL_NONE);
592 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
593 context_invalidate_state(context, STATE_FRAMEBUFFER);
595 if (gl_mask & GL_DEPTH_BUFFER_BIT)
597 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
598 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
600 if (gl_mask & GL_STENCIL_BUFFER_BIT)
602 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
604 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
605 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
607 gl_info->gl_ops.gl.p_glStencilMask(~0U);
608 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
611 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
612 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
614 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
615 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
616 checkGLcall("glBlitFramebuffer()");
618 if (wined3d_settings.strict_draw_ordering)
619 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
621 context_release(context);
624 /* Blit between surface locations. Onscreen on different swapchains is not supported.
625 * Depth / stencil is not supported. Context activation is done by the caller. */
626 static void surface_blt_fbo(const struct wined3d_device *device,
627 struct wined3d_context *old_ctx, enum wined3d_texture_filter_type filter,
628 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
629 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
631 const struct wined3d_gl_info *gl_info;
632 struct wined3d_context *context = old_ctx;
633 struct wined3d_surface *required_rt, *restore_rt = NULL;
634 RECT src_rect, dst_rect;
635 GLenum gl_filter;
636 GLenum buffer;
638 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
639 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
640 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
641 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
642 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
644 src_rect = *src_rect_in;
645 dst_rect = *dst_rect_in;
647 switch (filter)
649 case WINED3D_TEXF_LINEAR:
650 gl_filter = GL_LINEAR;
651 break;
653 default:
654 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
655 case WINED3D_TEXF_NONE:
656 case WINED3D_TEXF_POINT:
657 gl_filter = GL_NEAREST;
658 break;
661 /* Resolve the source surface first if needed. */
662 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
663 && (src_surface->container->resource.format->id != dst_surface->container->resource.format->id
664 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
665 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
666 src_location = WINED3D_LOCATION_RB_RESOLVED;
668 /* Make sure the locations are up-to-date. Loading the destination
669 * surface isn't required if the entire surface is overwritten. (And is
670 * in fact harmful if we're being called by surface_load_location() with
671 * the purpose of loading the destination surface.) */
672 surface_load_location(src_surface, old_ctx, src_location);
673 if (!surface_is_full_rect(dst_surface, &dst_rect))
674 surface_load_location(dst_surface, old_ctx, dst_location);
675 else
676 wined3d_surface_prepare(dst_surface, old_ctx, dst_location);
679 if (src_location == WINED3D_LOCATION_DRAWABLE) required_rt = src_surface;
680 else if (dst_location == WINED3D_LOCATION_DRAWABLE) required_rt = dst_surface;
681 else required_rt = NULL;
683 if (required_rt && required_rt != old_ctx->current_rt)
685 restore_rt = old_ctx->current_rt;
686 context = context_acquire(device, required_rt);
689 if (!context->valid)
691 context_release(context);
692 WARN("Invalid context, skipping blit.\n");
693 return;
696 gl_info = context->gl_info;
698 if (src_location == WINED3D_LOCATION_DRAWABLE)
700 TRACE("Source surface %p is onscreen.\n", src_surface);
701 buffer = wined3d_texture_get_gl_buffer(src_surface->container);
702 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
704 else
706 TRACE("Source surface %p is offscreen.\n", src_surface);
707 buffer = GL_COLOR_ATTACHMENT0;
710 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
711 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
712 checkGLcall("glReadBuffer()");
713 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
715 if (dst_location == WINED3D_LOCATION_DRAWABLE)
717 TRACE("Destination surface %p is onscreen.\n", dst_surface);
718 buffer = wined3d_texture_get_gl_buffer(dst_surface->container);
719 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
721 else
723 TRACE("Destination surface %p is offscreen.\n", dst_surface);
724 buffer = GL_COLOR_ATTACHMENT0;
727 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
728 context_set_draw_buffer(context, buffer);
729 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
730 context_invalidate_state(context, STATE_FRAMEBUFFER);
732 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
733 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
734 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
735 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
736 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
738 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
739 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
741 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
742 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
743 checkGLcall("glBlitFramebuffer()");
745 if (wined3d_settings.strict_draw_ordering
746 || (dst_location == WINED3D_LOCATION_DRAWABLE
747 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
748 gl_info->gl_ops.gl.p_glFlush();
750 if (restore_rt)
751 context_restore(context, restore_rt);
754 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
755 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
756 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
758 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
759 return FALSE;
761 /* Source and/or destination need to be on the GL side */
762 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
763 return FALSE;
765 switch (blit_op)
767 case WINED3D_BLIT_OP_COLOR_BLIT:
768 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
769 || (src_usage & WINED3DUSAGE_RENDERTARGET)))
770 return FALSE;
771 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
772 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
773 return FALSE;
774 if (!(src_format->id == dst_format->id
775 || (is_identity_fixup(src_format->color_fixup)
776 && is_identity_fixup(dst_format->color_fixup))))
777 return FALSE;
778 break;
780 case WINED3D_BLIT_OP_DEPTH_BLIT:
781 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
782 return FALSE;
783 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
784 return FALSE;
785 /* Accept pure swizzle fixups for depth formats. In general we
786 * ignore the stencil component (if present) at the moment and the
787 * swizzle is not relevant with just the depth component. */
788 if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup)
789 || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup))
790 return FALSE;
791 break;
793 default:
794 return FALSE;
797 return TRUE;
800 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
802 const struct wined3d_format *format = surface->container->resource.format;
804 switch (format->id)
806 case WINED3DFMT_S1_UINT_D15_UNORM:
807 *float_depth = depth / (float)0x00007fff;
808 break;
810 case WINED3DFMT_D16_UNORM:
811 *float_depth = depth / (float)0x0000ffff;
812 break;
814 case WINED3DFMT_D24_UNORM_S8_UINT:
815 case WINED3DFMT_X8D24_UNORM:
816 *float_depth = depth / (float)0x00ffffff;
817 break;
819 case WINED3DFMT_D32_UNORM:
820 *float_depth = depth / (float)0xffffffff;
821 break;
823 default:
824 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
825 return FALSE;
828 return TRUE;
831 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
833 struct wined3d_resource *resource = &surface->container->resource;
834 struct wined3d_device *device = resource->device;
835 struct wined3d_rendertarget_view_desc view_desc;
836 struct wined3d_rendertarget_view *view;
837 const struct blit_shader *blitter;
838 HRESULT hr;
840 if (!(blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info,
841 WINED3D_BLIT_OP_DEPTH_FILL, NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format)))
843 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
844 return WINED3DERR_INVALIDCALL;
847 view_desc.format_id = resource->format->id;
848 view_desc.u.texture.level_idx = surface->texture_level;
849 view_desc.u.texture.layer_idx = surface->texture_layer;
850 view_desc.u.texture.layer_count = 1;
851 if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc,
852 resource, NULL, &wined3d_null_parent_ops, &view)))
854 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
855 return hr;
858 hr = blitter->depth_fill(device, view, rect, WINED3DCLEAR_ZBUFFER, depth, 0);
859 wined3d_rendertarget_view_decref(view);
861 return hr;
864 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
865 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
867 struct wined3d_texture *src_texture = src_surface->container;
868 struct wined3d_texture *dst_texture = dst_surface->container;
869 struct wined3d_device *device = src_texture->resource.device;
871 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
872 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
873 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format))
874 return WINED3DERR_INVALIDCALL;
876 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
878 surface_modify_ds_location(dst_surface, dst_location,
879 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
881 return WINED3D_OK;
884 static ULONG surface_resource_incref(struct wined3d_resource *resource)
886 struct wined3d_surface *surface = surface_from_resource(resource);
888 TRACE("surface %p, container %p.\n", surface, surface->container);
890 return wined3d_texture_incref(surface->container);
893 static ULONG surface_resource_decref(struct wined3d_resource *resource)
895 struct wined3d_surface *surface = surface_from_resource(resource);
897 TRACE("surface %p, container %p.\n", surface, surface->container);
899 return wined3d_texture_decref(surface->container);
902 static void surface_unload(struct wined3d_resource *resource)
904 struct wined3d_surface *surface = surface_from_resource(resource);
905 struct wined3d_renderbuffer_entry *entry, *entry2;
906 struct wined3d_device *device = resource->device;
907 const struct wined3d_gl_info *gl_info;
908 struct wined3d_context *context;
910 TRACE("surface %p.\n", surface);
912 context = context_acquire(device, NULL);
913 gl_info = context->gl_info;
915 if (resource->pool == WINED3D_POOL_DEFAULT)
917 /* Default pool resources are supposed to be destroyed before Reset is called.
918 * Implicit resources stay however. So this means we have an implicit render target
919 * or depth stencil. The content may be destroyed, but we still have to tear down
920 * opengl resources, so we cannot leave early.
922 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
923 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
924 * or the depth stencil into an FBO the texture or render buffer will be removed
925 * and all flags get lost */
926 if (resource->usage & WINED3DUSAGE_DEPTHSTENCIL)
928 surface_validate_location(surface, WINED3D_LOCATION_DISCARDED);
929 surface_invalidate_location(surface, ~WINED3D_LOCATION_DISCARDED);
931 else
933 surface_prepare_system_memory(surface);
934 memset(surface->resource.heap_memory, 0, surface->resource.size);
935 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
936 surface_invalidate_location(surface, ~WINED3D_LOCATION_SYSMEM);
939 else
941 surface_load_location(surface, context, surface->resource.map_binding);
942 surface_invalidate_location(surface, ~surface->resource.map_binding);
945 /* Destroy fbo render buffers. This is needed for implicit render targets, for
946 * all application-created targets the application has to release the surface
947 * before calling _Reset
949 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
951 context_gl_resource_released(device, entry->id, TRUE);
952 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
953 list_remove(&entry->entry);
954 HeapFree(GetProcessHeap(), 0, entry);
956 list_init(&surface->renderbuffers);
957 surface->current_renderbuffer = NULL;
959 if (surface->rb_multisample)
961 context_gl_resource_released(device, surface->rb_multisample, TRUE);
962 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
963 surface->rb_multisample = 0;
965 if (surface->rb_resolved)
967 context_gl_resource_released(device, surface->rb_resolved, TRUE);
968 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
969 surface->rb_resolved = 0;
972 context_release(context);
974 resource_unload(resource);
977 static HRESULT surface_resource_sub_resource_map(struct wined3d_resource *resource, unsigned int sub_resource_idx,
978 struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags)
980 ERR("Not supported on sub-resources.\n");
981 return WINED3DERR_INVALIDCALL;
984 static HRESULT surface_resource_sub_resource_unmap(struct wined3d_resource *resource, unsigned int sub_resource_idx)
986 ERR("Not supported on sub-resources.\n");
987 return WINED3DERR_INVALIDCALL;
990 static const struct wined3d_resource_ops surface_resource_ops =
992 surface_resource_incref,
993 surface_resource_decref,
994 surface_unload,
995 surface_resource_sub_resource_map,
996 surface_resource_sub_resource_unmap,
999 /* This call just downloads data, the caller is responsible for binding the
1000 * correct texture. */
1001 /* Context activation is done by the caller. */
1002 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1003 DWORD dst_location)
1005 struct wined3d_texture *texture = surface->container;
1006 const struct wined3d_format *format = texture->resource.format;
1007 struct wined3d_bo_address data;
1009 /* Only support read back of converted P8 surfaces. */
1010 if (texture->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1012 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1013 return;
1016 surface_get_memory(surface, &data, dst_location);
1018 if (texture->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
1020 TRACE("(%p) : Calling glGetCompressedTexImage level %d, format %#x, type %#x, data %p.\n",
1021 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1023 if (data.buffer_object)
1025 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1026 checkGLcall("glBindBuffer");
1027 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target, surface->texture_level, NULL));
1028 checkGLcall("glGetCompressedTexImage");
1029 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1030 checkGLcall("glBindBuffer");
1032 else
1034 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target,
1035 surface->texture_level, data.addr));
1036 checkGLcall("glGetCompressedTexImage");
1039 else
1041 unsigned int dst_row_pitch, dst_slice_pitch;
1042 unsigned int src_row_pitch, src_slice_pitch;
1043 GLenum gl_format = format->glFormat;
1044 GLenum gl_type = format->glType;
1045 void *mem;
1047 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
1049 wined3d_texture_get_pitch(texture, surface->texture_level, &dst_row_pitch, &dst_slice_pitch);
1050 wined3d_format_calculate_pitch(format, texture->resource.device->surface_alignment,
1051 surface->pow2Width, surface->pow2Height, &src_row_pitch, &src_slice_pitch);
1052 mem = HeapAlloc(GetProcessHeap(), 0, src_slice_pitch);
1054 else
1056 mem = data.addr;
1059 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1060 surface, surface->texture_level, gl_format, gl_type, mem);
1062 if (data.buffer_object)
1064 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1065 checkGLcall("glBindBuffer");
1067 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1068 gl_format, gl_type, NULL);
1069 checkGLcall("glGetTexImage");
1071 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1072 checkGLcall("glBindBuffer");
1074 else
1076 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1077 gl_format, gl_type, mem);
1078 checkGLcall("glGetTexImage");
1081 if (texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
1083 const BYTE *src_data;
1084 BYTE *dst_data;
1085 UINT y;
1087 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1088 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1089 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1091 * We're doing this...
1093 * instead of boxing the texture :
1094 * |<-texture width ->| -->pow2width| /\
1095 * |111111111111111111| | |
1096 * |222 Texture 222222| boxed empty | texture height
1097 * |3333 Data 33333333| | |
1098 * |444444444444444444| | \/
1099 * ----------------------------------- |
1100 * | boxed empty | boxed empty | pow2height
1101 * | | | \/
1102 * -----------------------------------
1105 * we're repacking the data to the expected texture width
1107 * |<-texture width ->| -->pow2width| /\
1108 * |111111111111111111222222222222222| |
1109 * |222333333333333333333444444444444| texture height
1110 * |444444 | |
1111 * | | \/
1112 * | | |
1113 * | empty | pow2height
1114 * | | \/
1115 * -----------------------------------
1117 * == is the same as
1119 * |<-texture width ->| /\
1120 * |111111111111111111|
1121 * |222222222222222222|texture height
1122 * |333333333333333333|
1123 * |444444444444444444| \/
1124 * --------------------
1126 * This also means that any references to surface memory should work with the data as if it were a
1127 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
1129 * internally the texture is still stored in a boxed format so any references to textureName will
1130 * get a boxed texture with width pow2width and not a texture of width resource.width. */
1131 src_data = mem;
1132 dst_data = data.addr;
1133 TRACE("Repacking the surface data from pitch %u to pitch %u.\n", src_row_pitch, dst_row_pitch);
1134 for (y = 0; y < surface->resource.height; ++y)
1136 memcpy(dst_data, src_data, dst_row_pitch);
1137 src_data += src_row_pitch;
1138 dst_data += dst_row_pitch;
1141 HeapFree(GetProcessHeap(), 0, mem);
1146 /* This call just uploads data, the caller is responsible for binding the
1147 * correct texture. */
1148 /* Context activation is done by the caller. */
1149 void wined3d_surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1150 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1151 BOOL srgb, const struct wined3d_const_bo_address *data)
1153 struct wined3d_texture *texture = surface->container;
1154 UINT update_w = src_rect->right - src_rect->left;
1155 UINT update_h = src_rect->bottom - src_rect->top;
1157 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1158 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1159 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1161 if (surface->resource.map_count)
1163 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
1164 texture->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
1167 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE)
1169 update_h *= format->height_scale.numerator;
1170 update_h /= format->height_scale.denominator;
1173 if (data->buffer_object)
1175 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
1176 checkGLcall("glBindBuffer");
1179 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
1181 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1182 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1183 const BYTE *addr = data->addr;
1184 GLenum internal;
1186 addr += (src_rect->top / format->block_height) * src_pitch;
1187 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1189 if (srgb)
1190 internal = format->glGammaInternal;
1191 else if (texture->resource.usage & WINED3DUSAGE_RENDERTARGET
1192 && wined3d_resource_is_offscreen(&texture->resource))
1193 internal = format->rtInternal;
1194 else
1195 internal = format->glInternal;
1197 TRACE("glCompressedTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, "
1198 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1199 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1201 if (row_length == src_pitch)
1203 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1204 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1206 else
1208 UINT row, y;
1210 /* glCompressedTexSubImage2D() ignores pixel store state, so we
1211 * can't use the unpack row length like for glTexSubImage2D. */
1212 for (row = 0, y = dst_point->y; row < row_count; ++row)
1214 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1215 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1216 y += format->block_height;
1217 addr += src_pitch;
1220 checkGLcall("glCompressedTexSubImage2D");
1222 else
1224 const BYTE *addr = data->addr;
1226 addr += src_rect->top * src_pitch;
1227 addr += src_rect->left * format->byte_count;
1229 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1230 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1231 update_w, update_h, format->glFormat, format->glType, addr);
1233 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1234 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1235 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1236 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1237 checkGLcall("glTexSubImage2D");
1240 if (data->buffer_object)
1242 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
1243 checkGLcall("glBindBuffer");
1246 if (wined3d_settings.strict_draw_ordering)
1247 gl_info->gl_ops.gl.p_glFlush();
1249 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1251 struct wined3d_device *device = texture->resource.device;
1252 unsigned int i;
1254 for (i = 0; i < device->context_count; ++i)
1256 context_surface_update(device->contexts[i], surface);
1261 static BOOL surface_check_block_align_rect(struct wined3d_surface *surface, const RECT *rect)
1263 struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
1265 return wined3d_texture_check_block_align(surface->container, surface->texture_level, &box);
1268 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1269 struct wined3d_surface *src_surface, const RECT *src_rect)
1271 struct wined3d_texture *src_texture = src_surface->container;
1272 struct wined3d_texture *dst_texture = dst_surface->container;
1273 unsigned int src_row_pitch, src_slice_pitch;
1274 const struct wined3d_format *src_format;
1275 const struct wined3d_format *dst_format;
1276 unsigned int src_fmt_flags, dst_fmt_flags;
1277 const struct wined3d_gl_info *gl_info;
1278 struct wined3d_context *context;
1279 struct wined3d_bo_address data;
1280 UINT update_w, update_h;
1281 UINT dst_w, dst_h;
1282 RECT r, dst_rect;
1283 POINT p;
1285 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1286 dst_surface, wine_dbgstr_point(dst_point),
1287 src_surface, wine_dbgstr_rect(src_rect));
1289 src_format = src_texture->resource.format;
1290 dst_format = dst_texture->resource.format;
1291 src_fmt_flags = src_texture->resource.format_flags;
1292 dst_fmt_flags = dst_texture->resource.format_flags;
1294 if (src_format->id != dst_format->id)
1296 WARN("Source and destination surfaces should have the same format.\n");
1297 return WINED3DERR_INVALIDCALL;
1300 if (!dst_point)
1302 p.x = 0;
1303 p.y = 0;
1304 dst_point = &p;
1306 else if (dst_point->x < 0 || dst_point->y < 0)
1308 WARN("Invalid destination point.\n");
1309 return WINED3DERR_INVALIDCALL;
1312 if (!src_rect)
1314 SetRect(&r, 0, 0, src_surface->resource.width, src_surface->resource.height);
1315 src_rect = &r;
1317 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1318 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1320 WARN("Invalid source rectangle.\n");
1321 return WINED3DERR_INVALIDCALL;
1324 dst_w = dst_surface->resource.width;
1325 dst_h = dst_surface->resource.height;
1327 update_w = src_rect->right - src_rect->left;
1328 update_h = src_rect->bottom - src_rect->top;
1330 if (update_w > dst_w || dst_point->x > dst_w - update_w
1331 || update_h > dst_h || dst_point->y > dst_h - update_h)
1333 WARN("Destination out of bounds.\n");
1334 return WINED3DERR_INVALIDCALL;
1337 if ((src_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(src_surface, src_rect))
1339 WARN("Source rectangle not block-aligned.\n");
1340 return WINED3DERR_INVALIDCALL;
1343 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1344 if ((dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(dst_surface, &dst_rect))
1346 WARN("Destination rectangle not block-aligned.\n");
1347 return WINED3DERR_INVALIDCALL;
1350 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1351 if (dst_format->convert || wined3d_format_get_color_key_conversion(dst_texture, FALSE))
1352 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1354 context = context_acquire(dst_texture->resource.device, NULL);
1355 gl_info = context->gl_info;
1357 /* Only load the surface for partial updates. For newly allocated texture
1358 * the texture wouldn't be the current location, and we'd upload zeroes
1359 * just to overwrite them again. */
1360 if (update_w == dst_w && update_h == dst_h)
1361 wined3d_texture_prepare_texture(dst_texture, context, FALSE);
1362 else
1363 surface_load_location(dst_surface, context, WINED3D_LOCATION_TEXTURE_RGB);
1364 wined3d_texture_bind_and_dirtify(dst_texture, context, FALSE);
1366 surface_get_memory(src_surface, &data, src_surface->locations);
1367 wined3d_texture_get_pitch(src_texture, src_surface->texture_level, &src_row_pitch, &src_slice_pitch);
1369 wined3d_surface_upload_data(dst_surface, gl_info, src_format, src_rect,
1370 src_row_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
1372 context_release(context);
1374 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1375 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1377 return WINED3D_OK;
1380 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1381 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1382 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1383 /* Context activation is done by the caller. */
1384 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1386 const struct wined3d_gl_info *gl_info = &surface->container->resource.device->adapter->gl_info;
1387 struct wined3d_renderbuffer_entry *entry;
1388 GLuint renderbuffer = 0;
1389 unsigned int src_width, src_height;
1390 unsigned int width, height;
1392 if (rt && rt->container->resource.format->id != WINED3DFMT_NULL)
1394 width = rt->pow2Width;
1395 height = rt->pow2Height;
1397 else
1399 width = surface->pow2Width;
1400 height = surface->pow2Height;
1403 src_width = surface->pow2Width;
1404 src_height = surface->pow2Height;
1406 /* A depth stencil smaller than the render target is not valid */
1407 if (width > src_width || height > src_height) return;
1409 /* Remove any renderbuffer set if the sizes match */
1410 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1411 || (width == src_width && height == src_height))
1413 surface->current_renderbuffer = NULL;
1414 return;
1417 /* Look if we've already got a renderbuffer of the correct dimensions */
1418 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1420 if (entry->width == width && entry->height == height)
1422 renderbuffer = entry->id;
1423 surface->current_renderbuffer = entry;
1424 break;
1428 if (!renderbuffer)
1430 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1431 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1432 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1433 surface->container->resource.format->glInternal, width, height);
1435 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1436 entry->width = width;
1437 entry->height = height;
1438 entry->id = renderbuffer;
1439 list_add_head(&surface->renderbuffers, &entry->entry);
1441 surface->current_renderbuffer = entry;
1444 checkGLcall("set_compatible_renderbuffer");
1447 /* Context activation is done by the caller. */
1448 void surface_load(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
1450 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
1452 TRACE("surface %p, srgb %#x.\n", surface, srgb);
1454 if (surface->container->resource.pool == WINED3D_POOL_SCRATCH)
1455 ERR("Not supported on scratch surfaces.\n");
1457 if (surface->locations & location)
1459 TRACE("surface is already in texture\n");
1460 return;
1462 TRACE("Reloading because surface is dirty.\n");
1464 surface_load_location(surface, context, location);
1465 surface_evict_sysmem(surface);
1468 /* See also float_16_to_32() in wined3d_private.h */
1469 static inline unsigned short float_32_to_16(const float *in)
1471 int exp = 0;
1472 float tmp = fabsf(*in);
1473 unsigned int mantissa;
1474 unsigned short ret;
1476 /* Deal with special numbers */
1477 if (*in == 0.0f)
1478 return 0x0000;
1479 if (isnan(*in))
1480 return 0x7c01;
1481 if (isinf(*in))
1482 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1484 if (tmp < (float)(1u << 10))
1488 tmp = tmp * 2.0f;
1489 exp--;
1490 } while (tmp < (float)(1u << 10));
1492 else if (tmp >= (float)(1u << 11))
1496 tmp /= 2.0f;
1497 exp++;
1498 } while (tmp >= (float)(1u << 11));
1501 mantissa = (unsigned int)tmp;
1502 if (tmp - mantissa >= 0.5f)
1503 ++mantissa; /* Round to nearest, away from zero. */
1505 exp += 10; /* Normalize the mantissa. */
1506 exp += 15; /* Exponent is encoded with excess 15. */
1508 if (exp > 30) /* too big */
1510 ret = 0x7c00; /* INF */
1512 else if (exp <= 0)
1514 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1515 while (exp <= 0)
1517 mantissa = mantissa >> 1;
1518 ++exp;
1520 ret = mantissa & 0x3ff;
1522 else
1524 ret = (exp << 10) | (mantissa & 0x3ff);
1527 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1528 return ret;
1531 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
1532 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1534 unsigned short *dst_s;
1535 const float *src_f;
1536 unsigned int x, y;
1538 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1540 for (y = 0; y < h; ++y)
1542 src_f = (const float *)(src + y * pitch_in);
1543 dst_s = (unsigned short *) (dst + y * pitch_out);
1544 for (x = 0; x < w; ++x)
1546 dst_s[x] = float_32_to_16(src_f + x);
1551 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
1552 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1554 static const unsigned char convert_5to8[] =
1556 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
1557 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
1558 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
1559 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
1561 static const unsigned char convert_6to8[] =
1563 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
1564 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
1565 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
1566 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
1567 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
1568 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
1569 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
1570 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
1572 unsigned int x, y;
1574 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1576 for (y = 0; y < h; ++y)
1578 const WORD *src_line = (const WORD *)(src + y * pitch_in);
1579 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1580 for (x = 0; x < w; ++x)
1582 WORD pixel = src_line[x];
1583 dst_line[x] = 0xff000000u
1584 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
1585 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
1586 | convert_5to8[(pixel & 0x001fu)];
1591 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
1592 * in both cases we're just setting the X / Alpha channel to 0xff. */
1593 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
1594 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1596 unsigned int x, y;
1598 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1600 for (y = 0; y < h; ++y)
1602 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
1603 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1605 for (x = 0; x < w; ++x)
1607 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
1612 static inline BYTE cliptobyte(int x)
1614 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
1617 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
1618 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1620 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1621 unsigned int x, y;
1623 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1625 for (y = 0; y < h; ++y)
1627 const BYTE *src_line = src + y * pitch_in;
1628 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1629 for (x = 0; x < w; ++x)
1631 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1632 * C = Y - 16; D = U - 128; E = V - 128;
1633 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1634 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1635 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1636 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1637 * U and V are shared between the pixels. */
1638 if (!(x & 1)) /* For every even pixel, read new U and V. */
1640 d = (int) src_line[1] - 128;
1641 e = (int) src_line[3] - 128;
1642 r2 = 409 * e + 128;
1643 g2 = - 100 * d - 208 * e + 128;
1644 b2 = 516 * d + 128;
1646 c2 = 298 * ((int) src_line[0] - 16);
1647 dst_line[x] = 0xff000000
1648 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
1649 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
1650 | cliptobyte((c2 + b2) >> 8); /* blue */
1651 /* Scale RGB values to 0..255 range,
1652 * then clip them if still not in range (may be negative),
1653 * then shift them within DWORD if necessary. */
1654 src_line += 2;
1659 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
1660 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1662 unsigned int x, y;
1663 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1665 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
1667 for (y = 0; y < h; ++y)
1669 const BYTE *src_line = src + y * pitch_in;
1670 WORD *dst_line = (WORD *)(dst + y * pitch_out);
1671 for (x = 0; x < w; ++x)
1673 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1674 * C = Y - 16; D = U - 128; E = V - 128;
1675 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1676 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1677 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1678 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1679 * U and V are shared between the pixels. */
1680 if (!(x & 1)) /* For every even pixel, read new U and V. */
1682 d = (int) src_line[1] - 128;
1683 e = (int) src_line[3] - 128;
1684 r2 = 409 * e + 128;
1685 g2 = - 100 * d - 208 * e + 128;
1686 b2 = 516 * d + 128;
1688 c2 = 298 * ((int) src_line[0] - 16);
1689 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
1690 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
1691 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
1692 /* Scale RGB values to 0..255 range,
1693 * then clip them if still not in range (may be negative),
1694 * then shift them within DWORD if necessary. */
1695 src_line += 2;
1700 struct d3dfmt_converter_desc
1702 enum wined3d_format_id from, to;
1703 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
1706 static const struct d3dfmt_converter_desc converters[] =
1708 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
1709 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
1710 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1711 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1712 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
1713 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
1716 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
1717 enum wined3d_format_id to)
1719 unsigned int i;
1721 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
1723 if (converters[i].from == from && converters[i].to == to)
1724 return &converters[i];
1727 return NULL;
1730 static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture,
1731 unsigned int sub_resource_idx, enum wined3d_format_id format)
1733 struct wined3d_map_desc src_map, dst_map;
1734 const struct d3dfmt_converter_desc *conv;
1735 struct wined3d_texture *dst_texture;
1736 struct wined3d_resource_desc desc;
1738 if (!(conv = find_converter(src_texture->resource.format->id, format)))
1740 FIXME("Cannot find a conversion function from format %s to %s.\n",
1741 debug_d3dformat(src_texture->resource.format->id), debug_d3dformat(format));
1742 return NULL;
1745 /* FIXME: Multisampled conversion? */
1746 wined3d_resource_get_desc(src_texture->sub_resources[sub_resource_idx].resource, &desc);
1747 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
1748 desc.format = format;
1749 desc.usage = 0;
1750 desc.pool = WINED3D_POOL_SCRATCH;
1751 if (FAILED(wined3d_texture_create(src_texture->resource.device, &desc, 1,
1752 WINED3D_TEXTURE_CREATE_MAPPABLE | WINED3D_TEXTURE_CREATE_DISCARD,
1753 NULL, NULL, &wined3d_null_parent_ops, &dst_texture)))
1755 ERR("Failed to create a destination texture for conversion.\n");
1756 return NULL;
1759 memset(&src_map, 0, sizeof(src_map));
1760 memset(&dst_map, 0, sizeof(dst_map));
1762 if (FAILED(wined3d_resource_map(&src_texture->resource, sub_resource_idx,
1763 &src_map, NULL, WINED3D_MAP_READONLY)))
1765 ERR("Failed to map the source texture.\n");
1766 wined3d_texture_decref(dst_texture);
1767 return NULL;
1769 if (FAILED(wined3d_resource_map(&dst_texture->resource, 0, &dst_map, NULL, 0)))
1771 ERR("Failed to map the destination texture.\n");
1772 wined3d_resource_unmap(&src_texture->resource, sub_resource_idx);
1773 wined3d_texture_decref(dst_texture);
1774 return NULL;
1777 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch, desc.width, desc.height);
1779 wined3d_resource_unmap(&dst_texture->resource, 0);
1780 wined3d_resource_unmap(&src_texture->resource, sub_resource_idx);
1782 return dst_texture;
1785 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
1786 unsigned int bpp, UINT pitch, DWORD color)
1788 BYTE *first;
1789 unsigned int x, y;
1791 /* Do first row */
1793 #define COLORFILL_ROW(type) \
1794 do { \
1795 type *d = (type *)buf; \
1796 for (x = 0; x < width; ++x) \
1797 d[x] = (type)color; \
1798 } while(0)
1800 switch (bpp)
1802 case 1:
1803 COLORFILL_ROW(BYTE);
1804 break;
1806 case 2:
1807 COLORFILL_ROW(WORD);
1808 break;
1810 case 3:
1812 BYTE *d = buf;
1813 for (x = 0; x < width; ++x, d += 3)
1815 d[0] = (color ) & 0xff;
1816 d[1] = (color >> 8) & 0xff;
1817 d[2] = (color >> 16) & 0xff;
1819 break;
1821 case 4:
1822 COLORFILL_ROW(DWORD);
1823 break;
1825 default:
1826 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
1827 return WINED3DERR_NOTAVAILABLE;
1830 #undef COLORFILL_ROW
1832 /* Now copy first row. */
1833 first = buf;
1834 for (y = 1; y < height; ++y)
1836 buf += pitch;
1837 memcpy(buf, first, width * bpp);
1840 return WINED3D_OK;
1843 static void read_from_framebuffer(struct wined3d_surface *surface,
1844 struct wined3d_context *old_ctx, DWORD dst_location)
1846 struct wined3d_texture *texture = surface->container;
1847 struct wined3d_device *device = texture->resource.device;
1848 const struct wined3d_gl_info *gl_info;
1849 struct wined3d_context *context = old_ctx;
1850 struct wined3d_surface *restore_rt = NULL;
1851 unsigned int row_pitch, slice_pitch;
1852 BYTE *mem;
1853 BYTE *row, *top, *bottom;
1854 int i;
1855 BOOL srcIsUpsideDown;
1856 struct wined3d_bo_address data;
1858 surface_get_memory(surface, &data, dst_location);
1860 if (surface != old_ctx->current_rt)
1862 restore_rt = old_ctx->current_rt;
1863 context = context_acquire(device, surface);
1866 context_apply_blit_state(context, device);
1867 gl_info = context->gl_info;
1869 /* Select the correct read buffer, and give some debug output.
1870 * There is no need to keep track of the current read buffer or reset it, every part of the code
1871 * that reads sets the read buffer as desired.
1873 if (wined3d_resource_is_offscreen(&texture->resource))
1875 /* Mapping the primary render target which is not on a swapchain.
1876 * Read from the back buffer. */
1877 TRACE("Mapping offscreen render target.\n");
1878 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1879 srcIsUpsideDown = TRUE;
1881 else
1883 /* Onscreen surfaces are always part of a swapchain */
1884 GLenum buffer = wined3d_texture_get_gl_buffer(texture);
1885 TRACE("Mapping %#x buffer.\n", buffer);
1886 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
1887 checkGLcall("glReadBuffer");
1888 srcIsUpsideDown = FALSE;
1891 if (data.buffer_object)
1893 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1894 checkGLcall("glBindBuffer");
1897 wined3d_texture_get_pitch(texture, surface->texture_level, &row_pitch, &slice_pitch);
1899 /* Setup pixel store pack state -- to glReadPixels into the correct place */
1900 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / texture->resource.format->byte_count);
1901 checkGLcall("glPixelStorei");
1903 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
1904 surface->resource.width, surface->resource.height,
1905 texture->resource.format->glFormat,
1906 texture->resource.format->glType, data.addr);
1907 checkGLcall("glReadPixels");
1909 /* Reset previous pixel store pack state */
1910 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
1911 checkGLcall("glPixelStorei");
1913 if (!srcIsUpsideDown)
1915 /* glReadPixels returns the image upside down, and there is no way to
1916 * prevent this. Flip the lines in software. */
1918 if (!(row = HeapAlloc(GetProcessHeap(), 0, row_pitch)))
1919 goto error;
1921 if (data.buffer_object)
1923 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
1924 checkGLcall("glMapBuffer");
1926 else
1927 mem = data.addr;
1929 top = mem;
1930 bottom = mem + row_pitch * (surface->resource.height - 1);
1931 for (i = 0; i < surface->resource.height / 2; i++)
1933 memcpy(row, top, row_pitch);
1934 memcpy(top, bottom, row_pitch);
1935 memcpy(bottom, row, row_pitch);
1936 top += row_pitch;
1937 bottom -= row_pitch;
1939 HeapFree(GetProcessHeap(), 0, row);
1941 if (data.buffer_object)
1942 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
1945 error:
1946 if (data.buffer_object)
1948 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1949 checkGLcall("glBindBuffer");
1952 if (restore_rt)
1953 context_restore(context, restore_rt);
1956 /* Read the framebuffer contents into a texture. Note that this function
1957 * doesn't do any kind of flipping. Using this on an onscreen surface will
1958 * result in a flipped D3D texture.
1960 * Context activation is done by the caller. This function may temporarily
1961 * switch to a different context and restore the original one before return. */
1962 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb, struct wined3d_context *old_ctx)
1964 struct wined3d_texture *texture = surface->container;
1965 struct wined3d_device *device = texture->resource.device;
1966 const struct wined3d_gl_info *gl_info;
1967 struct wined3d_context *context = old_ctx;
1968 struct wined3d_surface *restore_rt = NULL;
1970 if (old_ctx->current_rt != surface)
1972 restore_rt = old_ctx->current_rt;
1973 context = context_acquire(device, surface);
1976 gl_info = context->gl_info;
1977 device_invalidate_state(device, STATE_FRAMEBUFFER);
1979 wined3d_texture_prepare_texture(texture, context, srgb);
1980 wined3d_texture_bind_and_dirtify(texture, context, srgb);
1982 TRACE("Reading back offscreen render target %p.\n", surface);
1984 if (wined3d_resource_is_offscreen(&texture->resource))
1985 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
1986 else
1987 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(texture));
1988 checkGLcall("glReadBuffer");
1990 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
1991 0, 0, 0, 0, surface->resource.width, surface->resource.height);
1992 checkGLcall("glCopyTexSubImage2D");
1994 if (restore_rt)
1995 context_restore(context, restore_rt);
1998 static void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
2000 struct wined3d_texture *texture = surface->container;
2001 const struct wined3d_format *format = texture->resource.format;
2003 if (multisample)
2005 DWORD samples;
2007 if (surface->rb_multisample)
2008 return;
2010 /* TODO: Nvidia exposes their Coverage Sample Anti-Aliasing (CSAA) feature
2011 * through type == MULTISAMPLE_XX and quality != 0. This could be mapped
2012 * to GL_NV_framebuffer_multisample_coverage.
2014 * AMD has a similar feature called Enhanced Quality Anti-Aliasing (EQAA),
2015 * but it does not have an equivalent OpenGL extension. */
2017 /* We advertise as many WINED3D_MULTISAMPLE_NON_MASKABLE quality levels
2018 * as the count of advertised multisample types for the surface format. */
2019 if (texture->resource.multisample_type == WINED3D_MULTISAMPLE_NON_MASKABLE)
2021 unsigned int i, count = 0;
2023 for (i = 0; i < sizeof(format->multisample_types) * 8; ++i)
2025 if (format->multisample_types & 1u << i)
2027 if (texture->resource.multisample_quality == count++)
2028 break;
2031 samples = i + 1;
2033 else
2035 samples = texture->resource.multisample_type;
2038 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
2039 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
2040 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
2041 format->glInternal, surface->pow2Width, surface->pow2Height);
2042 checkGLcall("glRenderbufferStorageMultisample()");
2043 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
2045 else
2047 if (surface->rb_resolved)
2048 return;
2050 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
2051 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
2052 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, format->glInternal,
2053 surface->pow2Width, surface->pow2Height);
2054 checkGLcall("glRenderbufferStorage()");
2055 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
2059 /* Does a direct frame buffer -> texture copy. Stretching is done with single
2060 * pixel copy calls. */
2061 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
2062 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
2064 struct wined3d_texture *src_texture = src_surface->container;
2065 struct wined3d_texture *dst_texture = dst_surface->container;
2066 struct wined3d_device *device = dst_texture->resource.device;
2067 const struct wined3d_gl_info *gl_info;
2068 float xrel, yrel;
2069 struct wined3d_context *context;
2070 BOOL upsidedown = FALSE;
2071 RECT dst_rect = *dst_rect_in;
2073 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2074 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2076 if(dst_rect.top > dst_rect.bottom) {
2077 UINT tmp = dst_rect.bottom;
2078 dst_rect.bottom = dst_rect.top;
2079 dst_rect.top = tmp;
2080 upsidedown = TRUE;
2083 context = context_acquire(device, src_surface);
2084 gl_info = context->gl_info;
2085 context_apply_blit_state(context, device);
2086 wined3d_texture_load(dst_texture, context, FALSE);
2088 /* Bind the target texture */
2089 context_bind_texture(context, dst_texture->target, dst_texture->texture_rgb.name);
2090 if (wined3d_resource_is_offscreen(&src_texture->resource))
2092 TRACE("Reading from an offscreen target\n");
2093 upsidedown = !upsidedown;
2094 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
2096 else
2098 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
2100 checkGLcall("glReadBuffer");
2102 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
2103 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
2105 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2107 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2109 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
2110 ERR("Texture filtering not supported in direct blit.\n");
2112 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
2113 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
2115 ERR("Texture filtering not supported in direct blit\n");
2118 if (upsidedown
2119 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2120 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
2122 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
2123 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2124 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
2125 src_rect->left, src_surface->resource.height - src_rect->bottom,
2126 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2128 else
2130 LONG row;
2131 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
2132 /* I have to process this row by row to swap the image,
2133 * otherwise it would be upside down, so stretching in y direction
2134 * doesn't cost extra time
2136 * However, stretching in x direction can be avoided if not necessary
2138 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
2139 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2141 /* Well, that stuff works, but it's very slow.
2142 * find a better way instead
2144 LONG col;
2146 for (col = dst_rect.left; col < dst_rect.right; ++col)
2148 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2149 dst_rect.left + col /* x offset */, row /* y offset */,
2150 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
2153 else
2155 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2156 dst_rect.left /* x offset */, row /* y offset */,
2157 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
2161 checkGLcall("glCopyTexSubImage2D");
2163 context_release(context);
2165 /* The texture is now most up to date - If the surface is a render target
2166 * and has a drawable, this path is never entered. */
2167 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
2168 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
2171 /* Uses the hardware to stretch and flip the image */
2172 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
2173 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
2175 struct wined3d_texture *src_texture = src_surface->container;
2176 struct wined3d_texture *dst_texture = dst_surface->container;
2177 struct wined3d_device *device = dst_texture->resource.device;
2178 GLuint src, backup = 0;
2179 float left, right, top, bottom; /* Texture coordinates */
2180 UINT fbwidth = src_surface->resource.width;
2181 UINT fbheight = src_surface->resource.height;
2182 const struct wined3d_gl_info *gl_info;
2183 struct wined3d_context *context;
2184 GLenum drawBuffer = GL_BACK;
2185 GLenum offscreen_buffer;
2186 GLenum texture_target;
2187 BOOL noBackBufferBackup;
2188 BOOL src_offscreen;
2189 BOOL upsidedown = FALSE;
2190 RECT dst_rect = *dst_rect_in;
2192 TRACE("Using hwstretch blit\n");
2193 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2194 context = context_acquire(device, src_surface);
2195 gl_info = context->gl_info;
2196 context_apply_blit_state(context, device);
2197 wined3d_texture_load(dst_texture, context, FALSE);
2199 offscreen_buffer = context_get_offscreen_gl_buffer(context);
2201 src_offscreen = wined3d_resource_is_offscreen(&src_texture->resource);
2202 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
2203 if (!noBackBufferBackup && !src_texture->texture_rgb.name)
2205 /* Get it a description */
2206 wined3d_texture_load(src_texture, context, FALSE);
2209 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2210 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2212 if (context->aux_buffers >= 2)
2214 /* Got more than one aux buffer? Use the 2nd aux buffer */
2215 drawBuffer = GL_AUX1;
2217 else if ((!src_offscreen || offscreen_buffer == GL_BACK) && context->aux_buffers >= 1)
2219 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2220 drawBuffer = GL_AUX0;
2223 if (noBackBufferBackup)
2225 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
2226 checkGLcall("glGenTextures");
2227 context_bind_texture(context, GL_TEXTURE_2D, backup);
2228 texture_target = GL_TEXTURE_2D;
2230 else
2232 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2233 * we are reading from the back buffer, the backup can be used as source texture
2235 texture_target = src_surface->texture_target;
2236 context_bind_texture(context, texture_target, src_texture->texture_rgb.name);
2237 gl_info->gl_ops.gl.p_glEnable(texture_target);
2238 checkGLcall("glEnable(texture_target)");
2240 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2241 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
2244 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2245 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2247 if(dst_rect.top > dst_rect.bottom) {
2248 UINT tmp = dst_rect.bottom;
2249 dst_rect.bottom = dst_rect.top;
2250 dst_rect.top = tmp;
2251 upsidedown = TRUE;
2254 if (src_offscreen)
2256 TRACE("Reading from an offscreen target\n");
2257 upsidedown = !upsidedown;
2258 gl_info->gl_ops.gl.p_glReadBuffer(offscreen_buffer);
2260 else
2262 gl_info->gl_ops.gl.p_glReadBuffer(wined3d_texture_get_gl_buffer(src_texture));
2265 /* TODO: Only back up the part that will be overwritten */
2266 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
2268 checkGLcall("glCopyTexSubImage2D");
2270 /* No issue with overriding these - the sampler is dirty due to blit usage */
2271 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
2272 checkGLcall("glTexParameteri");
2273 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2274 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
2275 checkGLcall("glTexParameteri");
2277 if (!src_texture->swapchain || src_texture == src_texture->swapchain->back_buffers[0])
2279 src = backup ? backup : src_texture->texture_rgb.name;
2281 else
2283 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
2284 checkGLcall("glReadBuffer(GL_FRONT)");
2286 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
2287 checkGLcall("glGenTextures(1, &src)");
2288 context_bind_texture(context, GL_TEXTURE_2D, src);
2290 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
2291 * out for power of 2 sizes
2293 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
2294 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2295 checkGLcall("glTexImage2D");
2296 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
2298 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2299 checkGLcall("glTexParameteri");
2300 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2301 checkGLcall("glTexParameteri");
2303 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
2304 checkGLcall("glReadBuffer(GL_BACK)");
2306 if (texture_target != GL_TEXTURE_2D)
2308 gl_info->gl_ops.gl.p_glDisable(texture_target);
2309 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
2310 texture_target = GL_TEXTURE_2D;
2313 checkGLcall("glEnd and previous");
2315 left = src_rect->left;
2316 right = src_rect->right;
2318 if (!upsidedown)
2320 top = src_surface->resource.height - src_rect->top;
2321 bottom = src_surface->resource.height - src_rect->bottom;
2323 else
2325 top = src_surface->resource.height - src_rect->bottom;
2326 bottom = src_surface->resource.height - src_rect->top;
2329 if (src_texture->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
2331 left /= src_surface->pow2Width;
2332 right /= src_surface->pow2Width;
2333 top /= src_surface->pow2Height;
2334 bottom /= src_surface->pow2Height;
2337 /* draw the source texture stretched and upside down. The correct surface is bound already */
2338 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2339 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2341 context_set_draw_buffer(context, drawBuffer);
2342 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
2344 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
2345 /* bottom left */
2346 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
2347 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
2349 /* top left */
2350 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
2351 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
2353 /* top right */
2354 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
2355 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2357 /* bottom right */
2358 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
2359 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
2360 gl_info->gl_ops.gl.p_glEnd();
2361 checkGLcall("glEnd and previous");
2363 if (texture_target != dst_surface->texture_target)
2365 gl_info->gl_ops.gl.p_glDisable(texture_target);
2366 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
2367 texture_target = dst_surface->texture_target;
2370 /* Now read the stretched and upside down image into the destination texture */
2371 context_bind_texture(context, texture_target, dst_texture->texture_rgb.name);
2372 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
2374 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
2375 0, 0, /* We blitted the image to the origin */
2376 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2377 checkGLcall("glCopyTexSubImage2D");
2379 if (drawBuffer == GL_BACK)
2381 /* Write the back buffer backup back. */
2382 if (backup)
2384 if (texture_target != GL_TEXTURE_2D)
2386 gl_info->gl_ops.gl.p_glDisable(texture_target);
2387 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
2388 texture_target = GL_TEXTURE_2D;
2390 context_bind_texture(context, GL_TEXTURE_2D, backup);
2392 else
2394 if (texture_target != src_surface->texture_target)
2396 gl_info->gl_ops.gl.p_glDisable(texture_target);
2397 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
2398 texture_target = src_surface->texture_target;
2400 context_bind_texture(context, src_surface->texture_target, src_texture->texture_rgb.name);
2403 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
2404 /* top left */
2405 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
2406 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
2408 /* bottom left */
2409 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
2410 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
2412 /* bottom right */
2413 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
2414 (float)fbheight / (float)src_surface->pow2Height);
2415 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
2417 /* top right */
2418 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
2419 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
2420 gl_info->gl_ops.gl.p_glEnd();
2422 gl_info->gl_ops.gl.p_glDisable(texture_target);
2423 checkGLcall("glDisable(texture_target)");
2425 /* Cleanup */
2426 if (src != src_texture->texture_rgb.name && src != backup)
2428 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
2429 checkGLcall("glDeleteTextures(1, &src)");
2431 if (backup)
2433 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
2434 checkGLcall("glDeleteTextures(1, &backup)");
2437 if (wined3d_settings.strict_draw_ordering)
2438 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2440 context_release(context);
2442 /* The texture is now most up to date - If the surface is a render target
2443 * and has a drawable, this path is never entered. */
2444 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
2445 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
2448 /* Front buffer coordinates are always full screen coordinates, but our GL
2449 * drawable is limited to the window's client area. The sysmem and texture
2450 * copies do have the full screen size. Note that GL has a bottom-left
2451 * origin, while D3D has a top-left origin. */
2452 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
2454 UINT drawable_height;
2456 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
2458 POINT offset = {0, 0};
2459 RECT windowsize;
2461 ScreenToClient(window, &offset);
2462 OffsetRect(rect, offset.x, offset.y);
2464 GetClientRect(window, &windowsize);
2465 drawable_height = windowsize.bottom - windowsize.top;
2467 else
2469 drawable_height = surface->resource.height;
2472 rect->top = drawable_height - rect->top;
2473 rect->bottom = drawable_height - rect->bottom;
2476 /* Context activation is done by the caller. */
2477 static void surface_blt_to_drawable(const struct wined3d_device *device,
2478 struct wined3d_context *old_ctx,
2479 enum wined3d_texture_filter_type filter, BOOL alpha_test,
2480 struct wined3d_surface *src_surface, const RECT *src_rect_in,
2481 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
2483 struct wined3d_texture *src_texture = src_surface->container;
2484 struct wined3d_texture *dst_texture = dst_surface->container;
2485 const struct wined3d_gl_info *gl_info;
2486 struct wined3d_context *context = old_ctx;
2487 struct wined3d_surface *restore_rt = NULL;
2488 RECT src_rect, dst_rect;
2490 src_rect = *src_rect_in;
2491 dst_rect = *dst_rect_in;
2494 if (old_ctx->current_rt != dst_surface)
2496 restore_rt = old_ctx->current_rt;
2497 context = context_acquire(device, dst_surface);
2500 gl_info = context->gl_info;
2502 /* Make sure the surface is up-to-date. This should probably use
2503 * surface_load_location() and worry about the destination surface too,
2504 * unless we're overwriting it completely. */
2505 wined3d_texture_load(src_texture, context, FALSE);
2507 /* Activate the destination context, set it up for blitting */
2508 context_apply_blit_state(context, device);
2510 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
2511 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
2513 device->blitter->set_shader(device->blit_priv, context, src_surface, NULL);
2515 if (alpha_test)
2517 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
2518 checkGLcall("glEnable(GL_ALPHA_TEST)");
2520 /* For P8 surfaces, the alpha component contains the palette index.
2521 * Which means that the colorkey is one of the palette entries. In
2522 * other cases pixels that should be masked away have alpha set to 0. */
2523 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT)
2524 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
2525 (float)src_texture->async.src_blt_color_key.color_space_low_value / 255.0f);
2526 else
2527 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
2528 checkGLcall("glAlphaFunc");
2530 else
2532 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2533 checkGLcall("glDisable(GL_ALPHA_TEST)");
2536 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
2538 if (alpha_test)
2540 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2541 checkGLcall("glDisable(GL_ALPHA_TEST)");
2544 /* Leave the opengl state valid for blitting */
2545 device->blitter->unset_shader(context->gl_info);
2547 if (wined3d_settings.strict_draw_ordering
2548 || (dst_texture->swapchain && dst_texture->swapchain->front_buffer == dst_texture))
2549 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2551 if (restore_rt)
2552 context_restore(context, restore_rt);
2555 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
2557 struct wined3d_resource *resource = &s->container->resource;
2558 struct wined3d_device *device = resource->device;
2559 struct wined3d_rendertarget_view_desc view_desc;
2560 struct wined3d_rendertarget_view *view;
2561 const struct blit_shader *blitter;
2562 HRESULT hr;
2564 if (!(blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info,
2565 WINED3D_BLIT_OP_COLOR_FILL, NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format)))
2567 FIXME("No blitter is capable of performing the requested color fill operation.\n");
2568 return WINED3DERR_INVALIDCALL;
2571 view_desc.format_id = resource->format->id;
2572 view_desc.u.texture.level_idx = s->texture_level;
2573 view_desc.u.texture.layer_idx = s->texture_layer;
2574 view_desc.u.texture.layer_count = 1;
2575 if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc,
2576 resource, NULL, &wined3d_null_parent_ops, &view)))
2578 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
2579 return hr;
2582 hr = blitter->color_fill(device, view, rect, color);
2583 wined3d_rendertarget_view_decref(view);
2585 return hr;
2588 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
2589 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
2590 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
2592 struct wined3d_texture *dst_texture = dst_surface->container;
2593 struct wined3d_device *device = dst_texture->resource.device;
2594 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
2595 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
2596 struct wined3d_texture *src_texture;
2598 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
2599 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
2600 flags, fx, debug_d3dtexturefiltertype(filter));
2602 /* Get the swapchain. One of the surfaces has to be a primary surface */
2603 if (dst_texture->resource.pool == WINED3D_POOL_SYSTEM_MEM)
2605 WARN("Destination is in sysmem, rejecting gl blt\n");
2606 return WINED3DERR_INVALIDCALL;
2609 dst_swapchain = dst_texture->swapchain;
2611 if (src_surface)
2613 src_texture = src_surface->container;
2614 if (src_texture->resource.pool == WINED3D_POOL_SYSTEM_MEM)
2616 WARN("Src is in sysmem, rejecting gl blt\n");
2617 return WINED3DERR_INVALIDCALL;
2620 src_swapchain = src_texture->swapchain;
2622 else
2624 src_texture = NULL;
2625 src_swapchain = NULL;
2628 /* Early sort out of cases where no render target is used */
2629 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
2631 TRACE("No surface is render target, not using hardware blit.\n");
2632 return WINED3DERR_INVALIDCALL;
2635 /* No destination color keying supported */
2636 if (flags & (WINED3D_BLT_DST_CKEY | WINED3D_BLT_DST_CKEY_OVERRIDE))
2638 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2639 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2640 return WINED3DERR_INVALIDCALL;
2643 if (dst_swapchain && dst_swapchain == src_swapchain)
2645 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
2646 return WINED3DERR_INVALIDCALL;
2649 if (dst_swapchain && src_swapchain)
2651 FIXME("Implement hardware blit between two different swapchains\n");
2652 return WINED3DERR_INVALIDCALL;
2655 if (dst_swapchain)
2657 /* Handled with regular texture -> swapchain blit */
2658 if (src_surface == rt)
2659 TRACE("Blit from active render target to a swapchain\n");
2661 else if (src_swapchain && dst_surface == rt)
2663 FIXME("Implement blit from a swapchain to the active render target\n");
2664 return WINED3DERR_INVALIDCALL;
2667 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
2669 /* Blit from render target to texture */
2670 BOOL stretchx;
2672 /* P8 read back is not implemented */
2673 if (src_texture->resource.format->id == WINED3DFMT_P8_UINT
2674 || dst_texture->resource.format->id == WINED3DFMT_P8_UINT)
2676 TRACE("P8 read back not supported by frame buffer to texture blit\n");
2677 return WINED3DERR_INVALIDCALL;
2680 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_SRC_CKEY_OVERRIDE))
2682 TRACE("Color keying not supported by frame buffer to texture blit\n");
2683 return WINED3DERR_INVALIDCALL;
2684 /* Destination color key is checked above */
2687 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
2688 stretchx = TRUE;
2689 else
2690 stretchx = FALSE;
2692 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2693 * flip the image nor scale it.
2695 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2696 * -> If the app wants an image width an unscaled width, copy it line per line
2697 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
2698 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2699 * back buffer. This is slower than reading line per line, thus not used for flipping
2700 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2701 * pixel by pixel. */
2702 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
2703 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
2705 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
2706 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
2708 else
2710 TRACE("Using hardware stretching to flip / stretch the texture.\n");
2711 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
2714 surface_evict_sysmem(dst_surface);
2716 return WINED3D_OK;
2719 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
2720 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
2721 return WINED3DERR_INVALIDCALL;
2724 /* Context activation is done by the caller. */
2725 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
2726 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
2728 struct wined3d_device *device = surface->container->resource.device;
2729 const struct wined3d_gl_info *gl_info = context->gl_info;
2730 GLint compare_mode = GL_NONE;
2731 struct blt_info info;
2732 GLint old_binding = 0;
2733 RECT rect;
2735 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
2737 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
2738 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
2739 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2740 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
2741 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
2742 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
2743 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
2744 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
2745 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
2746 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
2747 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
2749 SetRect(&rect, 0, h, w, 0);
2750 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
2751 context_active_texture(context, context->gl_info, 0);
2752 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
2753 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
2754 if (gl_info->supported[ARB_SHADOW])
2756 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
2757 if (compare_mode != GL_NONE)
2758 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
2761 device->shader_backend->shader_select_depth_blt(device->shader_priv,
2762 gl_info, info.tex_type, &surface->ds_current_size);
2764 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
2765 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
2766 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
2767 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
2768 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
2769 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
2770 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
2771 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
2772 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
2773 gl_info->gl_ops.gl.p_glEnd();
2775 if (compare_mode != GL_NONE)
2776 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
2777 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
2779 gl_info->gl_ops.gl.p_glPopAttrib();
2781 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
2784 void surface_modify_ds_location(struct wined3d_surface *surface,
2785 DWORD location, UINT w, UINT h)
2787 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
2789 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
2790 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
2791 wined3d_texture_set_dirty(surface->container);
2793 surface->ds_current_size.cx = w;
2794 surface->ds_current_size.cy = h;
2795 surface->locations = location;
2798 /* Context activation is done by the caller. */
2799 static void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
2801 struct wined3d_texture *texture = surface->container;
2802 struct wined3d_device *device = texture->resource.device;
2803 const struct wined3d_gl_info *gl_info = context->gl_info;
2804 GLsizei w, h;
2806 TRACE("surface %p, context %p, new location %#x.\n", surface, context, location);
2808 /* TODO: Make this work for modes other than FBO */
2809 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
2811 if (!(surface->locations & location))
2813 w = surface->ds_current_size.cx;
2814 h = surface->ds_current_size.cy;
2815 surface->ds_current_size.cx = 0;
2816 surface->ds_current_size.cy = 0;
2818 else
2820 w = surface->resource.width;
2821 h = surface->resource.height;
2824 if (surface->current_renderbuffer)
2826 FIXME("Not supported with fixed up depth stencil.\n");
2827 return;
2830 wined3d_surface_prepare(surface, context, location);
2832 if (location == WINED3D_LOCATION_TEXTURE_RGB)
2834 GLint old_binding = 0;
2835 GLenum bind_target;
2837 /* The render target is allowed to be smaller than the depth/stencil
2838 * buffer, so the onscreen depth/stencil buffer is potentially smaller
2839 * than the offscreen surface. Don't overwrite the offscreen surface
2840 * with undefined data. */
2841 w = min(w, context->swapchain->desc.backbuffer_width);
2842 h = min(h, context->swapchain->desc.backbuffer_height);
2844 TRACE("Copying onscreen depth buffer to depth texture.\n");
2846 if (!device->depth_blt_texture)
2847 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
2849 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
2850 * directly on the FBO texture. That's because we need to flip. */
2851 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
2852 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
2853 NULL, WINED3D_LOCATION_DRAWABLE);
2854 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
2856 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
2857 bind_target = GL_TEXTURE_RECTANGLE_ARB;
2859 else
2861 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
2862 bind_target = GL_TEXTURE_2D;
2864 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
2865 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
2866 * internal format, because the internal format might include stencil
2867 * data. In principle we should copy stencil data as well, but unless
2868 * the driver supports stencil export it's hard to do, and doesn't
2869 * seem to be needed in practice. If the hardware doesn't support
2870 * writing stencil data, the glCopyTexImage2D() call might trigger
2871 * software fallbacks. */
2872 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
2873 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2874 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2875 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2876 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2877 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
2878 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
2880 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
2881 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
2882 context_set_draw_buffer(context, GL_NONE);
2884 /* Do the actual blit */
2885 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
2886 checkGLcall("depth_blt");
2888 context_invalidate_state(context, STATE_FRAMEBUFFER);
2890 if (wined3d_settings.strict_draw_ordering)
2891 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2893 else if (location == WINED3D_LOCATION_DRAWABLE)
2895 TRACE("Copying depth texture to onscreen depth buffer.\n");
2897 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
2898 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
2899 NULL, WINED3D_LOCATION_DRAWABLE);
2900 surface_depth_blt(surface, context, texture->texture_rgb.name,
2901 0, surface->pow2Height - h, w, h, surface->texture_target);
2902 checkGLcall("depth_blt");
2904 context_invalidate_state(context, STATE_FRAMEBUFFER);
2906 if (wined3d_settings.strict_draw_ordering)
2907 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2909 else
2911 ERR("Invalid location (%#x) specified.\n", location);
2915 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
2917 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
2919 surface->locations |= location;
2922 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
2924 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
2926 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
2927 wined3d_texture_set_dirty(surface->container);
2928 surface->locations &= ~location;
2930 if (!surface->locations)
2931 ERR("Surface %p does not have any up to date location.\n", surface);
2934 static DWORD resource_access_from_location(DWORD location)
2936 switch (location)
2938 case WINED3D_LOCATION_SYSMEM:
2939 case WINED3D_LOCATION_USER_MEMORY:
2940 case WINED3D_LOCATION_DIB:
2941 case WINED3D_LOCATION_BUFFER:
2942 return WINED3D_RESOURCE_ACCESS_CPU;
2944 case WINED3D_LOCATION_DRAWABLE:
2945 case WINED3D_LOCATION_TEXTURE_SRGB:
2946 case WINED3D_LOCATION_TEXTURE_RGB:
2947 case WINED3D_LOCATION_RB_MULTISAMPLE:
2948 case WINED3D_LOCATION_RB_RESOLVED:
2949 return WINED3D_RESOURCE_ACCESS_GPU;
2951 default:
2952 FIXME("Unhandled location %#x.\n", location);
2953 return 0;
2957 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
2959 struct wined3d_device *device = surface->container->resource.device;
2960 struct wined3d_context *context;
2961 const struct wined3d_gl_info *gl_info;
2962 struct wined3d_bo_address dst, src;
2963 UINT size = surface->resource.size;
2965 surface_get_memory(surface, &dst, location);
2966 surface_get_memory(surface, &src, surface->locations);
2968 if (dst.buffer_object)
2970 context = context_acquire(device, NULL);
2971 gl_info = context->gl_info;
2972 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, dst.buffer_object));
2973 GL_EXTCALL(glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, src.addr));
2974 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
2975 checkGLcall("Upload PBO");
2976 context_release(context);
2977 return;
2979 if (src.buffer_object)
2981 context = context_acquire(device, NULL);
2982 gl_info = context->gl_info;
2983 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, src.buffer_object));
2984 GL_EXTCALL(glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, size, dst.addr));
2985 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
2986 checkGLcall("Download PBO");
2987 context_release(context);
2988 return;
2990 memcpy(dst.addr, src.addr, size);
2993 /* Context activation is done by the caller. */
2994 static void surface_load_sysmem(struct wined3d_surface *surface,
2995 struct wined3d_context *context, DWORD dst_location)
2997 const struct wined3d_gl_info *gl_info = context->gl_info;
2999 wined3d_surface_prepare(surface, context, dst_location);
3001 if (surface->locations & surface_simple_locations)
3003 surface_copy_simple_location(surface, dst_location);
3004 return;
3007 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
3008 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
3010 /* Download the surface to system memory. */
3011 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3013 struct wined3d_texture *texture = surface->container;
3015 wined3d_texture_bind_and_dirtify(texture, context,
3016 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
3017 surface_download_data(surface, gl_info, dst_location);
3018 ++texture->download_count;
3020 return;
3023 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
3025 read_from_framebuffer(surface, context, dst_location);
3026 return;
3029 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
3030 surface, wined3d_debug_location(surface->locations));
3033 /* Context activation is done by the caller. */
3034 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
3035 struct wined3d_context *context)
3037 struct wined3d_texture *texture = surface->container;
3038 RECT r;
3040 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
3041 && wined3d_resource_is_offscreen(&texture->resource))
3043 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
3044 return WINED3DERR_INVALIDCALL;
3047 surface_get_rect(surface, NULL, &r);
3048 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
3049 surface_blt_to_drawable(texture->resource.device, context,
3050 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
3052 return WINED3D_OK;
3055 static HRESULT surface_load_texture(struct wined3d_surface *surface,
3056 struct wined3d_context *context, BOOL srgb)
3058 unsigned int width, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch;
3059 const RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
3060 const struct wined3d_gl_info *gl_info = context->gl_info;
3061 struct wined3d_texture *texture = surface->container;
3062 struct wined3d_device *device = texture->resource.device;
3063 const struct wined3d_color_key_conversion *conversion;
3064 struct wined3d_bo_address data;
3065 struct wined3d_format format;
3066 POINT dst_point = {0, 0};
3067 BYTE *mem = NULL;
3069 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
3070 && wined3d_resource_is_offscreen(&texture->resource)
3071 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
3073 surface_load_fb_texture(surface, srgb, context);
3075 return WINED3D_OK;
3078 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
3079 && (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
3080 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3081 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format,
3082 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format))
3084 if (srgb)
3085 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
3086 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
3087 else
3088 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
3089 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
3091 return WINED3D_OK;
3094 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
3095 && (!srgb || (texture->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
3096 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3097 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format,
3098 NULL, texture->resource.usage, texture->resource.pool, texture->resource.format))
3100 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
3101 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
3102 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
3103 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
3105 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, src_location,
3106 &rect, surface, dst_location, &rect);
3108 return WINED3D_OK;
3111 /* Upload from system memory */
3113 if (srgb)
3115 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
3116 == WINED3D_LOCATION_TEXTURE_RGB)
3118 /* Performance warning... */
3119 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
3120 surface_load_location(surface, context, surface->resource.map_binding);
3123 else
3125 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
3126 == WINED3D_LOCATION_TEXTURE_SRGB)
3128 /* Performance warning... */
3129 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
3130 surface_load_location(surface, context, surface->resource.map_binding);
3134 if (!(surface->locations & surface_simple_locations))
3136 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
3137 /* Lets hope we get it from somewhere... */
3138 surface_load_location(surface, context, WINED3D_LOCATION_SYSMEM);
3141 wined3d_texture_prepare_texture(texture, context, srgb);
3142 wined3d_texture_bind_and_dirtify(texture, context, srgb);
3143 wined3d_texture_get_pitch(texture, surface->texture_level, &src_row_pitch, &src_slice_pitch);
3145 width = surface->resource.width;
3147 format = *texture->resource.format;
3148 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
3149 format = *wined3d_get_format(gl_info, conversion->dst_format);
3151 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
3152 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
3153 * getting called. */
3154 if ((format.convert || conversion)
3155 && texture->sub_resources[surface_get_sub_resource_idx(surface)].buffer_object)
3157 TRACE("Removing the pbo attached to surface %p.\n", surface);
3159 if (surface->flags & SFLAG_DIBSECTION)
3160 surface->resource.map_binding = WINED3D_LOCATION_DIB;
3161 else
3162 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
3164 surface_load_location(surface, context, surface->resource.map_binding);
3165 wined3d_texture_remove_buffer_object(texture, surface_get_sub_resource_idx(surface), gl_info);
3168 surface_get_memory(surface, &data, surface->locations);
3169 if (format.convert)
3171 /* This code is entered for texture formats which need a fixup. */
3172 UINT height = surface->resource.height;
3174 format.byte_count = format.conv_byte_count;
3175 wined3d_format_calculate_pitch(&format, 1, width, height, &dst_row_pitch, &dst_slice_pitch);
3177 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
3179 ERR("Out of memory (%u).\n", dst_slice_pitch);
3180 context_release(context);
3181 return E_OUTOFMEMORY;
3183 format.convert(data.addr, mem, src_row_pitch, src_slice_pitch,
3184 dst_row_pitch, dst_slice_pitch, width, height, 1);
3185 src_row_pitch = dst_row_pitch;
3186 data.addr = mem;
3188 else if (conversion)
3190 /* This code is only entered for color keying fixups */
3191 struct wined3d_palette *palette = NULL;
3192 UINT height = surface->resource.height;
3194 wined3d_format_calculate_pitch(&format, device->surface_alignment,
3195 width, height, &dst_row_pitch, &dst_slice_pitch);
3197 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
3199 ERR("Out of memory (%u).\n", dst_slice_pitch);
3200 context_release(context);
3201 return E_OUTOFMEMORY;
3203 if (texture->swapchain && texture->swapchain->palette)
3204 palette = texture->swapchain->palette;
3205 conversion->convert(data.addr, src_row_pitch, mem, dst_row_pitch,
3206 width, height, palette, &texture->async.gl_color_key);
3207 src_row_pitch = dst_row_pitch;
3208 data.addr = mem;
3211 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
3212 src_row_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
3214 HeapFree(GetProcessHeap(), 0, mem);
3216 return WINED3D_OK;
3219 /* Context activation is done by the caller. */
3220 static void surface_load_renderbuffer(struct wined3d_surface *surface, struct wined3d_context *context,
3221 DWORD dst_location)
3223 const RECT rect = {0, 0, surface->resource.width, surface->resource.height};
3224 DWORD src_location;
3226 if (surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE)
3227 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
3228 else if (surface->locations & WINED3D_LOCATION_RB_RESOLVED)
3229 src_location = WINED3D_LOCATION_RB_RESOLVED;
3230 else if (surface->locations & WINED3D_LOCATION_TEXTURE_SRGB)
3231 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
3232 else /* surface_blt_fbo will load the source location if necessary. */
3233 src_location = WINED3D_LOCATION_TEXTURE_RGB;
3235 surface_blt_fbo(surface->container->resource.device, context, WINED3D_TEXF_POINT,
3236 surface, src_location, &rect, surface, dst_location, &rect);
3239 /* Context activation is done by the caller. Context may be NULL in ddraw-only mode. */
3240 HRESULT surface_load_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3242 HRESULT hr;
3244 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3246 if (surface->locations & location && (!(surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
3247 || (surface->ds_current_size.cx == surface->resource.width
3248 && surface->ds_current_size.cy == surface->resource.height)))
3250 TRACE("Location (%#x) is already up to date.\n", location);
3251 return WINED3D_OK;
3254 if (WARN_ON(d3d_surface))
3256 DWORD required_access = resource_access_from_location(location);
3257 if ((surface->resource.access_flags & required_access) != required_access)
3258 WARN("Operation requires %#x access, but surface only has %#x.\n",
3259 required_access, surface->resource.access_flags);
3262 if (surface->locations & WINED3D_LOCATION_DISCARDED)
3264 TRACE("Surface previously discarded, nothing to do.\n");
3265 wined3d_surface_prepare(surface, context, location);
3266 surface_validate_location(surface, location);
3267 surface_invalidate_location(surface, WINED3D_LOCATION_DISCARDED);
3268 goto done;
3271 if (!surface->locations)
3273 ERR("Surface %p does not have any up to date location.\n", surface);
3274 surface_validate_location(surface, WINED3D_LOCATION_DISCARDED);
3275 return surface_load_location(surface, context, location);
3278 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
3280 if ((location == WINED3D_LOCATION_TEXTURE_RGB && surface->locations & WINED3D_LOCATION_DRAWABLE)
3281 || (location == WINED3D_LOCATION_DRAWABLE && surface->locations & WINED3D_LOCATION_TEXTURE_RGB))
3283 surface_load_ds_location(surface, context, location);
3284 goto done;
3287 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
3288 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
3289 return WINED3DERR_INVALIDCALL;
3292 switch (location)
3294 case WINED3D_LOCATION_DIB:
3295 case WINED3D_LOCATION_USER_MEMORY:
3296 case WINED3D_LOCATION_SYSMEM:
3297 case WINED3D_LOCATION_BUFFER:
3298 surface_load_sysmem(surface, context, location);
3299 break;
3301 case WINED3D_LOCATION_DRAWABLE:
3302 if (FAILED(hr = surface_load_drawable(surface, context)))
3303 return hr;
3304 break;
3306 case WINED3D_LOCATION_RB_RESOLVED:
3307 case WINED3D_LOCATION_RB_MULTISAMPLE:
3308 surface_load_renderbuffer(surface, context, location);
3309 break;
3311 case WINED3D_LOCATION_TEXTURE_RGB:
3312 case WINED3D_LOCATION_TEXTURE_SRGB:
3313 if (FAILED(hr = surface_load_texture(surface, context,
3314 location == WINED3D_LOCATION_TEXTURE_SRGB)))
3315 return hr;
3316 break;
3318 default:
3319 ERR("Don't know how to handle location %#x.\n", location);
3320 break;
3323 done:
3324 surface_validate_location(surface, location);
3326 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
3328 surface->ds_current_size.cx = surface->resource.width;
3329 surface->ds_current_size.cy = surface->resource.height;
3332 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
3333 surface_evict_sysmem(surface);
3335 return WINED3D_OK;
3338 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
3339 /* Context activation is done by the caller. */
3340 static void ffp_blit_free(struct wined3d_device *device) { }
3342 /* Context activation is done by the caller. */
3343 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
3344 const struct wined3d_color_key *color_key)
3346 const struct wined3d_gl_info *gl_info = context->gl_info;
3348 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
3349 checkGLcall("glEnable(target)");
3351 return WINED3D_OK;
3354 /* Context activation is done by the caller. */
3355 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
3357 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
3358 checkGLcall("glDisable(GL_TEXTURE_2D)");
3359 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
3361 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3362 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3364 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
3366 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
3367 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
3371 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info,
3372 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
3373 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
3374 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
3376 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
3378 TRACE("Source or destination is in system memory.\n");
3379 return FALSE;
3382 switch (blit_op)
3384 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
3385 if (d3d_info->shader_color_key)
3387 TRACE("Color keying requires converted textures.\n");
3388 return FALSE;
3390 case WINED3D_BLIT_OP_COLOR_BLIT:
3391 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
3392 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
3394 TRACE("Checking support for fixup:\n");
3395 dump_color_fixup_desc(src_format->color_fixup);
3398 /* We only support identity conversions. */
3399 if (!is_identity_fixup(src_format->color_fixup)
3400 || !is_identity_fixup(dst_format->color_fixup))
3402 TRACE("Fixups are not supported.\n");
3403 return FALSE;
3406 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
3408 TRACE("Can only blit to render targets.\n");
3409 return FALSE;
3411 return TRUE;
3413 case WINED3D_BLIT_OP_COLOR_FILL:
3414 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
3416 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
3417 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
3418 return FALSE;
3420 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
3422 TRACE("Color fill not supported\n");
3423 return FALSE;
3426 /* FIXME: We should reject color fills on formats with fixups,
3427 * but this would break P8 color fills for example. */
3429 return TRUE;
3431 case WINED3D_BLIT_OP_DEPTH_FILL:
3432 return TRUE;
3434 default:
3435 TRACE("Unsupported blit_op=%d\n", blit_op);
3436 return FALSE;
3440 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
3441 const RECT *rect, const struct wined3d_color *color)
3443 const RECT draw_rect = {0, 0, view->width, view->height};
3444 struct wined3d_fb_state fb = {&view, NULL};
3446 device_clear_render_targets(device, 1, &fb, 1, rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
3448 return WINED3D_OK;
3451 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
3452 struct wined3d_rendertarget_view *view, const RECT *rect, DWORD clear_flags,
3453 float depth, DWORD stencil)
3455 const RECT draw_rect = {0, 0, view->width, view->height};
3456 struct wined3d_fb_state fb = {NULL, view};
3458 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, clear_flags, NULL, depth, stencil);
3460 return WINED3D_OK;
3463 static void ffp_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
3464 struct wined3d_surface *src_surface, const RECT *src_rect,
3465 struct wined3d_surface *dst_surface, const RECT *dst_rect,
3466 const struct wined3d_color_key *color_key)
3468 struct wined3d_context *context;
3470 /* Blit from offscreen surface to render target */
3471 struct wined3d_color_key old_blt_key = src_surface->container->async.src_blt_color_key;
3472 DWORD old_color_key_flags = src_surface->container->async.color_key_flags;
3474 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
3476 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT, color_key);
3478 context = context_acquire(device, dst_surface);
3480 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
3481 glEnable(GL_ALPHA_TEST);
3483 surface_blt_to_drawable(device, context, filter,
3484 !!color_key, src_surface, src_rect, dst_surface, dst_rect);
3486 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
3487 glDisable(GL_ALPHA_TEST);
3489 context_release(context);
3491 /* Restore the color key parameters */
3492 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT,
3493 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
3495 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
3496 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
3499 const struct blit_shader ffp_blit = {
3500 ffp_blit_alloc,
3501 ffp_blit_free,
3502 ffp_blit_set,
3503 ffp_blit_unset,
3504 ffp_blit_supported,
3505 ffp_blit_color_fill,
3506 ffp_blit_depth_fill,
3507 ffp_blit_blit_surface,
3510 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
3512 return WINED3D_OK;
3515 /* Context activation is done by the caller. */
3516 static void cpu_blit_free(struct wined3d_device *device)
3520 /* Context activation is done by the caller. */
3521 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
3522 const struct wined3d_color_key *color_key)
3524 return WINED3D_OK;
3527 /* Context activation is done by the caller. */
3528 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
3532 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info,
3533 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
3534 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
3535 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
3537 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
3539 return TRUE;
3542 return FALSE;
3545 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
3546 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
3547 const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx)
3549 UINT row_block_count;
3550 const BYTE *src_row;
3551 BYTE *dst_row;
3552 UINT x, y;
3554 src_row = src_data;
3555 dst_row = dst_data;
3557 row_block_count = (update_w + format->block_width - 1) / format->block_width;
3559 if (!flags)
3561 for (y = 0; y < update_h; y += format->block_height)
3563 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
3564 src_row += src_pitch;
3565 dst_row += dst_pitch;
3568 return WINED3D_OK;
3571 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
3573 src_row += (((update_h / format->block_height) - 1) * src_pitch);
3575 switch (format->id)
3577 case WINED3DFMT_DXT1:
3578 for (y = 0; y < update_h; y += format->block_height)
3580 struct block
3582 WORD color[2];
3583 BYTE control_row[4];
3586 const struct block *s = (const struct block *)src_row;
3587 struct block *d = (struct block *)dst_row;
3589 for (x = 0; x < row_block_count; ++x)
3591 d[x].color[0] = s[x].color[0];
3592 d[x].color[1] = s[x].color[1];
3593 d[x].control_row[0] = s[x].control_row[3];
3594 d[x].control_row[1] = s[x].control_row[2];
3595 d[x].control_row[2] = s[x].control_row[1];
3596 d[x].control_row[3] = s[x].control_row[0];
3598 src_row -= src_pitch;
3599 dst_row += dst_pitch;
3601 return WINED3D_OK;
3603 case WINED3DFMT_DXT2:
3604 case WINED3DFMT_DXT3:
3605 for (y = 0; y < update_h; y += format->block_height)
3607 struct block
3609 WORD alpha_row[4];
3610 WORD color[2];
3611 BYTE control_row[4];
3614 const struct block *s = (const struct block *)src_row;
3615 struct block *d = (struct block *)dst_row;
3617 for (x = 0; x < row_block_count; ++x)
3619 d[x].alpha_row[0] = s[x].alpha_row[3];
3620 d[x].alpha_row[1] = s[x].alpha_row[2];
3621 d[x].alpha_row[2] = s[x].alpha_row[1];
3622 d[x].alpha_row[3] = s[x].alpha_row[0];
3623 d[x].color[0] = s[x].color[0];
3624 d[x].color[1] = s[x].color[1];
3625 d[x].control_row[0] = s[x].control_row[3];
3626 d[x].control_row[1] = s[x].control_row[2];
3627 d[x].control_row[2] = s[x].control_row[1];
3628 d[x].control_row[3] = s[x].control_row[0];
3630 src_row -= src_pitch;
3631 dst_row += dst_pitch;
3633 return WINED3D_OK;
3635 default:
3636 FIXME("Compressed flip not implemented for format %s.\n",
3637 debug_d3dformat(format->id));
3638 return E_NOTIMPL;
3642 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
3643 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
3645 return E_NOTIMPL;
3648 static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
3649 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
3650 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
3651 enum wined3d_texture_filter_type filter)
3653 unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count;
3654 const struct wined3d_format *src_format, *dst_format;
3655 struct wined3d_texture *converted_texture = NULL;
3656 unsigned int src_fmt_flags, dst_fmt_flags;
3657 struct wined3d_map_desc dst_map, src_map;
3658 const BYTE *sbase = NULL;
3659 HRESULT hr = WINED3D_OK;
3660 BOOL same_sub_resource;
3661 const BYTE *sbuf;
3662 BYTE *dbuf;
3663 int x, y;
3665 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
3666 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
3667 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture,
3668 src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
3670 if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx)
3672 same_sub_resource = TRUE;
3673 wined3d_resource_map(&dst_texture->resource, dst_sub_resource_idx, &dst_map, NULL, 0);
3674 src_map = dst_map;
3675 src_format = dst_texture->resource.format;
3676 dst_format = src_format;
3677 dst_fmt_flags = dst_texture->resource.format_flags;
3678 src_fmt_flags = dst_fmt_flags;
3680 else
3682 same_sub_resource = FALSE;
3683 dst_format = dst_texture->resource.format;
3684 dst_fmt_flags = dst_texture->resource.format_flags;
3685 if (src_texture)
3687 if (dst_texture->resource.format->id != src_texture->resource.format->id)
3689 if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format->id)))
3691 /* The conv function writes a FIXME */
3692 WARN("Cannot convert source surface format to dest format.\n");
3693 goto release;
3695 src_texture = converted_texture;
3696 src_sub_resource_idx = 0;
3698 wined3d_resource_map(&src_texture->resource, src_sub_resource_idx, &src_map, NULL, WINED3D_MAP_READONLY);
3699 src_format = src_texture->resource.format;
3700 src_fmt_flags = src_texture->resource.format_flags;
3702 else
3704 src_format = dst_format;
3705 src_fmt_flags = dst_fmt_flags;
3708 wined3d_resource_map(&dst_texture->resource, dst_sub_resource_idx, &dst_map, dst_box, 0);
3711 bpp = dst_format->byte_count;
3712 src_height = src_box->bottom - src_box->top;
3713 src_width = src_box->right - src_box->left;
3714 dst_height = dst_box->bottom - dst_box->top;
3715 dst_width = dst_box->right - dst_box->left;
3716 row_byte_count = dst_width * bpp;
3718 if (src_texture)
3719 sbase = (BYTE *)src_map.data
3720 + ((src_box->top / src_format->block_height) * src_map.row_pitch)
3721 + ((src_box->left / src_format->block_width) * src_format->block_byte_count);
3722 if (same_sub_resource)
3723 dbuf = (BYTE *)dst_map.data
3724 + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch)
3725 + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count);
3726 else
3727 dbuf = dst_map.data;
3729 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
3731 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
3733 if (same_sub_resource)
3735 FIXME("Only plain blits supported on compressed surfaces.\n");
3736 hr = E_NOTIMPL;
3737 goto release;
3740 if (src_height != dst_height || src_width != dst_width)
3742 WARN("Stretching not supported on compressed surfaces.\n");
3743 hr = WINED3DERR_INVALIDCALL;
3744 goto release;
3747 if (!wined3d_texture_check_block_align(src_texture,
3748 src_sub_resource_idx % src_texture->level_count, src_box))
3750 WARN("Source rectangle not block-aligned.\n");
3751 hr = WINED3DERR_INVALIDCALL;
3752 goto release;
3755 if (!wined3d_texture_check_block_align(dst_texture,
3756 dst_sub_resource_idx % dst_texture->level_count, dst_box))
3758 WARN("Destination rectangle not block-aligned.\n");
3759 hr = WINED3DERR_INVALIDCALL;
3760 goto release;
3763 hr = surface_cpu_blt_compressed(sbase, dbuf,
3764 src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height,
3765 src_format, flags, fx);
3766 goto release;
3769 /* First, all the 'source-less' blits */
3770 if (flags & WINED3D_BLT_COLOR_FILL)
3772 hr = _Blt_ColorFill(dbuf, dst_width, dst_height, bpp, dst_map.row_pitch, fx->fill_color);
3773 flags &= ~WINED3D_BLT_COLOR_FILL;
3776 if (flags & WINED3D_BLT_DEPTH_FILL)
3777 FIXME("WINED3D_BLT_DEPTH_FILL needs to be implemented!\n");
3779 /* Now the 'with source' blits. */
3780 if (src_texture)
3782 int sx, xinc, sy, yinc;
3784 if (!dst_width || !dst_height) /* Hmm... stupid program? */
3785 goto release;
3787 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
3788 && (src_width != dst_width || src_height != dst_height))
3790 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
3791 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
3794 xinc = (src_width << 16) / dst_width;
3795 yinc = (src_height << 16) / dst_height;
3797 if (!flags)
3799 /* No effects, we can cheat here. */
3800 if (dst_width == src_width)
3802 if (dst_height == src_height)
3804 /* No stretching in either direction. This needs to be as
3805 * fast as possible. */
3806 sbuf = sbase;
3808 /* Check for overlapping surfaces. */
3809 if (!same_sub_resource || dst_box->top < src_box->top
3810 || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
3812 /* No overlap, or dst above src, so copy from top downwards. */
3813 for (y = 0; y < dst_height; ++y)
3815 memcpy(dbuf, sbuf, row_byte_count);
3816 sbuf += src_map.row_pitch;
3817 dbuf += dst_map.row_pitch;
3820 else if (dst_box->top > src_box->top)
3822 /* Copy from bottom upwards. */
3823 sbuf += src_map.row_pitch * dst_height;
3824 dbuf += dst_map.row_pitch * dst_height;
3825 for (y = 0; y < dst_height; ++y)
3827 sbuf -= src_map.row_pitch;
3828 dbuf -= dst_map.row_pitch;
3829 memcpy(dbuf, sbuf, row_byte_count);
3832 else
3834 /* Src and dst overlapping on the same line, use memmove. */
3835 for (y = 0; y < dst_height; ++y)
3837 memmove(dbuf, sbuf, row_byte_count);
3838 sbuf += src_map.row_pitch;
3839 dbuf += dst_map.row_pitch;
3843 else
3845 /* Stretching in y direction only. */
3846 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3848 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3849 memcpy(dbuf, sbuf, row_byte_count);
3850 dbuf += dst_map.row_pitch;
3854 else
3856 /* Stretching in X direction. */
3857 int last_sy = -1;
3858 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
3860 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
3862 if ((sy >> 16) == (last_sy >> 16))
3864 /* This source row is the same as last source row -
3865 * Copy the already stretched row. */
3866 memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count);
3868 else
3870 #define STRETCH_ROW(type) \
3871 do { \
3872 const type *s = (const type *)sbuf; \
3873 type *d = (type *)dbuf; \
3874 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
3875 d[x] = s[sx >> 16]; \
3876 } while(0)
3878 switch(bpp)
3880 case 1:
3881 STRETCH_ROW(BYTE);
3882 break;
3883 case 2:
3884 STRETCH_ROW(WORD);
3885 break;
3886 case 4:
3887 STRETCH_ROW(DWORD);
3888 break;
3889 case 3:
3891 const BYTE *s;
3892 BYTE *d = dbuf;
3893 for (x = sx = 0; x < dst_width; x++, sx+= xinc)
3895 DWORD pixel;
3897 s = sbuf + 3 * (sx >> 16);
3898 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
3899 d[0] = (pixel ) & 0xff;
3900 d[1] = (pixel >> 8) & 0xff;
3901 d[2] = (pixel >> 16) & 0xff;
3902 d += 3;
3904 break;
3906 default:
3907 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
3908 hr = WINED3DERR_NOTAVAILABLE;
3909 goto error;
3911 #undef STRETCH_ROW
3913 dbuf += dst_map.row_pitch;
3914 last_sy = sy;
3918 else
3920 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
3921 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
3922 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
3923 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3924 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
3926 /* The color keying flags are checked for correctness in ddraw */
3927 if (flags & WINED3D_BLT_SRC_CKEY)
3929 keylow = src_texture->async.src_blt_color_key.color_space_low_value;
3930 keyhigh = src_texture->async.src_blt_color_key.color_space_high_value;
3932 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
3934 keylow = fx->src_color_key.color_space_low_value;
3935 keyhigh = fx->src_color_key.color_space_high_value;
3938 if (flags & WINED3D_BLT_DST_CKEY)
3940 /* Destination color keys are taken from the source surface! */
3941 destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value;
3942 destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value;
3944 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
3946 destkeylow = fx->dst_color_key.color_space_low_value;
3947 destkeyhigh = fx->dst_color_key.color_space_high_value;
3950 if (bpp == 1)
3952 keymask = 0xff;
3954 else
3956 DWORD masks[3];
3957 get_color_masks(src_format, masks);
3958 keymask = masks[0]
3959 | masks[1]
3960 | masks[2];
3962 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
3963 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
3966 if (flags & WINED3D_BLT_FX)
3968 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
3969 LONG tmpxy;
3970 dTopLeft = dbuf;
3971 dTopRight = dbuf + ((dst_width - 1) * bpp);
3972 dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
3973 dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
3975 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
3977 /* I don't think we need to do anything about this flag */
3978 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
3980 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
3982 tmp = dTopRight;
3983 dTopRight = dTopLeft;
3984 dTopLeft = tmp;
3985 tmp = dBottomRight;
3986 dBottomRight = dBottomLeft;
3987 dBottomLeft = tmp;
3988 dstxinc = dstxinc * -1;
3990 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
3992 tmp = dTopLeft;
3993 dTopLeft = dBottomLeft;
3994 dBottomLeft = tmp;
3995 tmp = dTopRight;
3996 dTopRight = dBottomRight;
3997 dBottomRight = tmp;
3998 dstyinc = dstyinc * -1;
4000 if (fx->fx & WINEDDBLTFX_NOTEARING)
4002 /* I don't think we need to do anything about this flag */
4003 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
4005 if (fx->fx & WINEDDBLTFX_ROTATE180)
4007 tmp = dBottomRight;
4008 dBottomRight = dTopLeft;
4009 dTopLeft = tmp;
4010 tmp = dBottomLeft;
4011 dBottomLeft = dTopRight;
4012 dTopRight = tmp;
4013 dstxinc = dstxinc * -1;
4014 dstyinc = dstyinc * -1;
4016 if (fx->fx & WINEDDBLTFX_ROTATE270)
4018 tmp = dTopLeft;
4019 dTopLeft = dBottomLeft;
4020 dBottomLeft = dBottomRight;
4021 dBottomRight = dTopRight;
4022 dTopRight = tmp;
4023 tmpxy = dstxinc;
4024 dstxinc = dstyinc;
4025 dstyinc = tmpxy;
4026 dstxinc = dstxinc * -1;
4028 if (fx->fx & WINEDDBLTFX_ROTATE90)
4030 tmp = dTopLeft;
4031 dTopLeft = dTopRight;
4032 dTopRight = dBottomRight;
4033 dBottomRight = dBottomLeft;
4034 dBottomLeft = tmp;
4035 tmpxy = dstxinc;
4036 dstxinc = dstyinc;
4037 dstyinc = tmpxy;
4038 dstyinc = dstyinc * -1;
4040 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
4042 /* I don't think we need to do anything about this flag */
4043 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
4045 dbuf = dTopLeft;
4046 flags &= ~(WINED3D_BLT_FX);
4049 #define COPY_COLORKEY_FX(type) \
4050 do { \
4051 const type *s; \
4052 type *d = (type *)dbuf, *dx, tmp; \
4053 for (y = sy = 0; y < dst_height; ++y, sy += yinc) \
4055 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
4056 dx = d; \
4057 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
4059 tmp = s[sx >> 16]; \
4060 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
4061 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
4063 dx[0] = tmp; \
4065 dx = (type *)(((BYTE *)dx) + dstxinc); \
4067 d = (type *)(((BYTE *)d) + dstyinc); \
4069 } while(0)
4071 switch (bpp)
4073 case 1:
4074 COPY_COLORKEY_FX(BYTE);
4075 break;
4076 case 2:
4077 COPY_COLORKEY_FX(WORD);
4078 break;
4079 case 4:
4080 COPY_COLORKEY_FX(DWORD);
4081 break;
4082 case 3:
4084 const BYTE *s;
4085 BYTE *d = dbuf, *dx;
4086 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
4088 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4089 dx = d;
4090 for (x = sx = 0; x < dst_width; ++x, sx+= xinc)
4092 DWORD pixel, dpixel = 0;
4093 s = sbuf + 3 * (sx>>16);
4094 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4095 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
4096 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
4097 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
4099 dx[0] = (pixel ) & 0xff;
4100 dx[1] = (pixel >> 8) & 0xff;
4101 dx[2] = (pixel >> 16) & 0xff;
4103 dx += dstxinc;
4105 d += dstyinc;
4107 break;
4109 default:
4110 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
4111 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
4112 hr = WINED3DERR_NOTAVAILABLE;
4113 goto error;
4114 #undef COPY_COLORKEY_FX
4119 error:
4120 if (flags && FIXME_ON(d3d_surface))
4122 FIXME("\tUnsupported flags: %#x.\n", flags);
4125 release:
4126 wined3d_resource_unmap(&dst_texture->resource, dst_sub_resource_idx);
4127 if (src_texture && !same_sub_resource)
4128 wined3d_resource_unmap(&src_texture->resource, src_sub_resource_idx);
4129 if (converted_texture)
4130 wined3d_texture_decref(converted_texture);
4132 return hr;
4135 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
4136 const RECT *rect, const struct wined3d_color *color)
4138 const struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
4139 static const struct wined3d_box src_box;
4140 struct wined3d_blt_fx fx;
4142 fx.fill_color = wined3d_format_convert_from_float(view->format, color);
4143 return surface_cpu_blt(wined3d_texture_from_resource(view->resource), view->sub_resource_idx,
4144 &box, NULL, 0, &src_box, WINED3D_BLT_COLOR_FILL, &fx, WINED3D_TEXF_POINT);
4147 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
4148 struct wined3d_rendertarget_view *view, const RECT *rect, DWORD clear_flags,
4149 float depth, DWORD stencil)
4151 FIXME("Depth/stencil filling not implemented by cpu_blit.\n");
4152 return WINED3DERR_INVALIDCALL;
4155 static void cpu_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
4156 struct wined3d_surface *src_surface, const RECT *src_rect,
4157 struct wined3d_surface *dst_surface, const RECT *dst_rect,
4158 const struct wined3d_color_key *color_key)
4160 /* FIXME: Remove error returns from surface_blt_cpu. */
4161 ERR("Blit method not implemented by cpu_blit.\n");
4164 const struct blit_shader cpu_blit = {
4165 cpu_blit_alloc,
4166 cpu_blit_free,
4167 cpu_blit_set,
4168 cpu_blit_unset,
4169 cpu_blit_supported,
4170 cpu_blit_color_fill,
4171 cpu_blit_depth_fill,
4172 cpu_blit_blit_surface,
4175 HRESULT wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4176 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
4177 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
4179 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
4180 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
4181 struct wined3d_texture *dst_texture = dst_surface->container;
4182 struct wined3d_device *device = dst_texture->resource.device;
4183 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
4184 struct wined3d_texture *src_texture = NULL;
4185 DWORD src_ds_flags, dst_ds_flags;
4186 BOOL scale, convert;
4188 static const DWORD simple_blit = WINED3D_BLT_ASYNC
4189 | WINED3D_BLT_COLOR_FILL
4190 | WINED3D_BLT_SRC_CKEY
4191 | WINED3D_BLT_SRC_CKEY_OVERRIDE
4192 | WINED3D_BLT_WAIT
4193 | WINED3D_BLT_DEPTH_FILL
4194 | WINED3D_BLT_DO_NOT_WAIT
4195 | WINED3D_BLT_ALPHA_TEST;
4197 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
4198 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4199 flags, fx, debug_d3dtexturefiltertype(filter));
4200 TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage));
4202 if (fx)
4204 TRACE("fx %#x.\n", fx->fx);
4205 TRACE("fill_color 0x%08x.\n", fx->fill_color);
4206 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
4207 fx->dst_color_key.color_space_low_value,
4208 fx->dst_color_key.color_space_high_value);
4209 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
4210 fx->src_color_key.color_space_low_value,
4211 fx->src_color_key.color_space_high_value);
4214 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
4216 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
4217 return WINEDDERR_SURFACEBUSY;
4220 if (dst_rect->left >= dst_rect->right || dst_rect->top >= dst_rect->bottom
4221 || dst_rect->left > dst_surface->resource.width || dst_rect->left < 0
4222 || dst_rect->top > dst_surface->resource.height || dst_rect->top < 0
4223 || dst_rect->right > dst_surface->resource.width || dst_rect->right < 0
4224 || dst_rect->bottom > dst_surface->resource.height || dst_rect->bottom < 0)
4226 WARN("The application gave us a bad destination rectangle.\n");
4227 return WINEDDERR_INVALIDRECT;
4230 if (src_surface)
4232 if (src_rect->left >= src_rect->right || src_rect->top >= src_rect->bottom
4233 || src_rect->left > src_surface->resource.width || src_rect->left < 0
4234 || src_rect->top > src_surface->resource.height || src_rect->top < 0
4235 || src_rect->right > src_surface->resource.width || src_rect->right < 0
4236 || src_rect->bottom > src_surface->resource.height || src_rect->bottom < 0)
4238 WARN("The application gave us a bad source rectangle.\n");
4239 return WINEDDERR_INVALIDRECT;
4241 src_texture = src_surface->container;
4244 if (!fx || !(fx->fx))
4245 flags &= ~WINED3D_BLT_FX;
4247 if (flags & WINED3D_BLT_WAIT)
4248 flags &= ~WINED3D_BLT_WAIT;
4250 if (flags & WINED3D_BLT_ASYNC)
4252 static unsigned int once;
4254 if (!once++)
4255 FIXME("Can't handle WINED3D_BLT_ASYNC flag.\n");
4256 flags &= ~WINED3D_BLT_ASYNC;
4259 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
4260 if (flags & WINED3D_BLT_DO_NOT_WAIT)
4262 static unsigned int once;
4264 if (!once++)
4265 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
4266 flags &= ~WINED3D_BLT_DO_NOT_WAIT;
4269 if (!device->d3d_initialized)
4271 WARN("D3D not initialized, using fallback.\n");
4272 goto cpu;
4275 /* We want to avoid invalidating the sysmem location for converted
4276 * surfaces, since otherwise we'd have to convert the data back when
4277 * locking them. */
4278 if (dst_texture->flags & WINED3D_TEXTURE_CONVERTED || dst_texture->resource.format->convert
4279 || wined3d_format_get_color_key_conversion(dst_texture, TRUE))
4281 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
4282 goto cpu;
4285 if (flags & ~simple_blit)
4287 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
4288 goto fallback;
4291 if (src_surface)
4292 src_swapchain = src_texture->swapchain;
4293 else
4294 src_swapchain = NULL;
4296 dst_swapchain = dst_texture->swapchain;
4298 /* This isn't strictly needed. FBO blits for example could deal with
4299 * cross-swapchain blits by first downloading the source to a texture
4300 * before switching to the destination context. We just have this here to
4301 * not have to deal with the issue, since cross-swapchain blits should be
4302 * rare. */
4303 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
4305 FIXME("Using fallback for cross-swapchain blit.\n");
4306 goto fallback;
4309 scale = src_surface
4310 && (src_rect->right - src_rect->left != dst_rect->right - dst_rect->left
4311 || src_rect->bottom - src_rect->top != dst_rect->bottom - dst_rect->top);
4312 convert = src_surface && src_texture->resource.format->id != dst_texture->resource.format->id;
4314 dst_ds_flags = dst_texture->resource.format_flags
4315 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
4316 if (src_surface)
4317 src_ds_flags = src_texture->resource.format_flags
4318 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
4319 else
4320 src_ds_flags = 0;
4322 if (src_ds_flags || dst_ds_flags)
4324 if (flags & WINED3D_BLT_DEPTH_FILL)
4326 float depth;
4328 TRACE("Depth fill.\n");
4330 if (!surface_convert_depth_to_float(dst_surface, fx->fill_color, &depth))
4331 return WINED3DERR_INVALIDCALL;
4333 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, dst_rect, depth)))
4334 return WINED3D_OK;
4336 else
4338 if (src_ds_flags != dst_ds_flags)
4340 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
4341 return WINED3DERR_INVALIDCALL;
4344 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_texture->resource.draw_binding,
4345 src_rect, dst_surface, dst_texture->resource.draw_binding, dst_rect)))
4346 return WINED3D_OK;
4349 else
4351 const struct blit_shader *blitter;
4353 /* In principle this would apply to depth blits as well, but we don't
4354 * implement those in the CPU blitter at the moment. */
4355 if ((dst_surface->locations & dst_surface->resource.map_binding)
4356 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
4358 if (scale)
4359 TRACE("Not doing sysmem blit because of scaling.\n");
4360 else if (convert)
4361 TRACE("Not doing sysmem blit because of format conversion.\n");
4362 else
4363 goto cpu;
4366 if (flags & WINED3D_BLT_COLOR_FILL)
4368 struct wined3d_color color;
4369 const struct wined3d_palette *palette = dst_swapchain ? dst_swapchain->palette : NULL;
4371 TRACE("Color fill.\n");
4373 if (!wined3d_format_convert_color_to_float(dst_texture->resource.format,
4374 palette, fx->fill_color, &color))
4375 goto fallback;
4377 if (SUCCEEDED(surface_color_fill(dst_surface, dst_rect, &color)))
4378 return WINED3D_OK;
4380 else
4382 enum wined3d_blit_op blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
4383 const struct wined3d_color_key *color_key = NULL;
4385 TRACE("Color blit.\n");
4386 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
4388 color_key = &fx->src_color_key;
4389 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
4391 else if (flags & WINED3D_BLT_SRC_CKEY)
4393 color_key = &src_texture->async.src_blt_color_key;
4394 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
4396 else if (flags & WINED3D_BLT_ALPHA_TEST)
4398 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
4400 else if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
4401 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
4403 /* Upload */
4404 if (scale)
4405 TRACE("Not doing upload because of scaling.\n");
4406 else if (convert)
4407 TRACE("Not doing upload because of format conversion.\n");
4408 else
4410 POINT dst_point = {dst_rect->left, dst_rect->top};
4412 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, src_rect)))
4414 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
4416 struct wined3d_context *context = context_acquire(device, dst_surface);
4417 surface_load_location(dst_surface, context, dst_texture->resource.draw_binding);
4418 context_release(context);
4420 return WINED3D_OK;
4424 else if (dst_swapchain && dst_swapchain->back_buffers
4425 && dst_texture == dst_swapchain->front_buffer
4426 && src_texture == dst_swapchain->back_buffers[0])
4428 /* Use present for back -> front blits. The idea behind this is
4429 * that present is potentially faster than a blit, in particular
4430 * when FBO blits aren't available. Some ddraw applications like
4431 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
4432 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
4433 * applications can't blit directly to the frontbuffer. */
4434 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
4436 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
4438 /* Set the swap effect to COPY, we don't want the backbuffer
4439 * to become undefined. */
4440 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
4441 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, 0);
4442 dst_swapchain->desc.swap_effect = swap_effect;
4444 return WINED3D_OK;
4447 if (fbo_blit_supported(&device->adapter->gl_info, blit_op,
4448 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
4449 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format))
4451 struct wined3d_context *context;
4452 TRACE("Using FBO blit.\n");
4454 context = context_acquire(device, NULL);
4455 surface_blt_fbo(device, context, filter,
4456 src_surface, src_texture->resource.draw_binding, src_rect,
4457 dst_surface, dst_texture->resource.draw_binding, dst_rect);
4458 context_release(context);
4460 surface_validate_location(dst_surface, dst_texture->resource.draw_binding);
4461 surface_invalidate_location(dst_surface, ~dst_texture->resource.draw_binding);
4463 return WINED3D_OK;
4466 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, blit_op,
4467 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
4468 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format);
4469 if (blitter)
4471 blitter->blit_surface(device, blit_op, filter, src_surface,
4472 src_rect, dst_surface, dst_rect, color_key);
4473 return WINED3D_OK;
4478 fallback:
4479 /* Special cases for render targets. */
4480 if (SUCCEEDED(surface_blt_special(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter)))
4481 return WINED3D_OK;
4483 cpu:
4484 return surface_cpu_blt(dst_texture, surface_get_sub_resource_idx(dst_surface), &dst_box,
4485 src_texture, src_texture ? surface_get_sub_resource_idx(src_surface) : 0, &src_box, flags, fx, filter);
4488 HRESULT wined3d_surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
4489 const struct wined3d_resource_desc *desc, GLenum target, unsigned int level, unsigned int layer, DWORD flags)
4491 struct wined3d_device *device = container->resource.device;
4492 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4493 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
4494 BOOL lockable = flags & WINED3D_TEXTURE_CREATE_MAPPABLE;
4495 UINT multisample_quality = desc->multisample_quality;
4496 unsigned int resource_size;
4497 HRESULT hr;
4499 if (container->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
4501 unsigned int pow2_width = 1, pow2_height = 1;
4503 /* Find the nearest pow2 match. */
4504 while (pow2_width < desc->width)
4505 pow2_width <<= 1;
4506 while (pow2_height < desc->height)
4507 pow2_height <<= 1;
4509 surface->pow2Width = pow2_width;
4510 surface->pow2Height = pow2_height;
4512 else
4514 surface->pow2Width = desc->width;
4515 surface->pow2Height = desc->height;
4518 /* Quick lockable sanity check.
4519 * TODO: remove this after surfaces, usage and lockability have been debugged properly
4520 * this function is too deep to need to care about things like this.
4521 * Levels need to be checked too, since they all affect what can be done. */
4522 switch (desc->pool)
4524 case WINED3D_POOL_MANAGED:
4525 if (desc->usage & WINED3DUSAGE_DYNAMIC)
4526 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
4527 break;
4529 case WINED3D_POOL_DEFAULT:
4530 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
4531 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
4532 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
4533 break;
4535 case WINED3D_POOL_SCRATCH:
4536 case WINED3D_POOL_SYSTEM_MEM:
4537 break;
4539 default:
4540 FIXME("Unknown pool %#x.\n", desc->pool);
4541 break;
4544 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
4545 FIXME("Trying to create a render target that isn't in the default pool.\n");
4547 /* FIXME: Check that the format is supported by the device. */
4549 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
4550 if (!resource_size)
4551 return WINED3DERR_INVALIDCALL;
4553 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE,
4554 format, desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height,
4555 1, resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
4557 WARN("Failed to initialize resource, returning %#x.\n", hr);
4558 return hr;
4561 surface->container = container;
4562 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
4563 list_init(&surface->renderbuffers);
4564 list_init(&surface->overlays);
4566 /* Flags */
4567 if (flags & WINED3D_TEXTURE_CREATE_DISCARD)
4568 surface->flags |= SFLAG_DISCARD;
4569 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
4570 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
4572 surface->texture_target = target;
4573 surface->texture_level = level;
4574 surface->texture_layer = layer;
4576 if (container->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
4577 surface->locations = WINED3D_LOCATION_DISCARDED;
4579 if (wined3d_texture_use_pbo(container, gl_info))
4580 surface->resource.map_binding = WINED3D_LOCATION_BUFFER;
4582 /* Similar to lockable rendertargets above, creating the DIB section
4583 * during surface initialization prevents the sysmem pointer from changing
4584 * after a wined3d_texture_get_dc() call. */
4585 if ((desc->usage & WINED3DUSAGE_OWNDC) || (device->wined3d->flags & WINED3D_NO3D))
4587 if (FAILED(hr = surface_create_dib_section(surface)))
4589 wined3d_surface_cleanup(surface);
4590 return hr;
4592 surface->resource.map_binding = WINED3D_LOCATION_DIB;
4595 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
4597 wined3d_resource_free_sysmem(&surface->resource);
4598 surface_validate_location(surface, WINED3D_LOCATION_DIB);
4599 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
4602 return hr;
4605 /* Context activation is done by the caller. Context may be NULL in
4606 * WINED3D_NO3D mode. */
4607 void wined3d_surface_prepare(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
4609 struct wined3d_texture *texture = surface->container;
4611 switch (location)
4613 case WINED3D_LOCATION_SYSMEM:
4614 surface_prepare_system_memory(surface);
4615 break;
4617 case WINED3D_LOCATION_USER_MEMORY:
4618 if (!texture->user_memory)
4619 ERR("Map binding is set to WINED3D_LOCATION_USER_MEMORY but surface->user_memory is NULL.\n");
4620 break;
4622 case WINED3D_LOCATION_DIB:
4623 if (!surface->dib.bitmap_data)
4624 ERR("Map binding is set to WINED3D_LOCATION_DIB but surface->dib.bitmap_data is NULL.\n");
4625 break;
4627 case WINED3D_LOCATION_BUFFER:
4628 wined3d_texture_prepare_buffer_object(texture,
4629 surface_get_sub_resource_idx(surface), context->gl_info);
4630 break;
4632 case WINED3D_LOCATION_TEXTURE_RGB:
4633 wined3d_texture_prepare_texture(texture, context, FALSE);
4634 break;
4636 case WINED3D_LOCATION_TEXTURE_SRGB:
4637 wined3d_texture_prepare_texture(texture, context, TRUE);
4638 break;
4640 case WINED3D_LOCATION_RB_MULTISAMPLE:
4641 surface_prepare_rb(surface, context->gl_info, TRUE);
4642 break;
4644 case WINED3D_LOCATION_RB_RESOLVED:
4645 surface_prepare_rb(surface, context->gl_info, FALSE);
4646 break;