wined3d: Merge wined3d_surface_map() and wined3d_volume_map().
[wine.git] / dlls / wined3d / surface.c
blob574069b4135879422ca97f0572afc8b7327640e1
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_renderbuffer_entry *entry, *entry2;
57 const struct wined3d_gl_info *gl_info;
58 struct wined3d_context *context;
59 struct wined3d_device *device = surface->resource.device;
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 const struct wined3d_format *format = surface->resource.format;
355 unsigned int format_flags = surface->container->resource.format_flags;
356 unsigned int row_pitch, slice_pitch;
357 BITMAPINFO *b_info;
358 DWORD *masks;
360 TRACE("surface %p.\n", surface);
362 if (!(format_flags & WINED3DFMT_FLAG_GETDC))
364 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
365 return WINED3DERR_INVALIDCALL;
368 switch (format->byte_count)
370 case 2:
371 case 4:
372 /* Allocate extra space to store the RGB bit masks. */
373 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, FIELD_OFFSET(BITMAPINFO, bmiColors[3]));
374 break;
376 case 3:
377 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, FIELD_OFFSET(BITMAPINFO, bmiColors[0]));
378 break;
380 default:
381 /* Allocate extra space for a palette. */
382 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
383 FIELD_OFFSET(BITMAPINFO, bmiColors[1u << (format->byte_count * 8)]));
384 break;
387 if (!b_info)
388 return E_OUTOFMEMORY;
390 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
391 wined3d_texture_get_pitch(surface->container, surface->texture_level, &row_pitch, &slice_pitch);
392 b_info->bmiHeader.biWidth = row_pitch / format->byte_count;
393 b_info->bmiHeader.biHeight = 0 - surface->resource.height;
394 b_info->bmiHeader.biSizeImage = slice_pitch;
395 b_info->bmiHeader.biPlanes = 1;
396 b_info->bmiHeader.biBitCount = format->byte_count * 8;
398 b_info->bmiHeader.biXPelsPerMeter = 0;
399 b_info->bmiHeader.biYPelsPerMeter = 0;
400 b_info->bmiHeader.biClrUsed = 0;
401 b_info->bmiHeader.biClrImportant = 0;
403 /* Get the bit masks */
404 masks = (DWORD *)b_info->bmiColors;
405 switch (surface->resource.format->id)
407 case WINED3DFMT_B8G8R8_UNORM:
408 b_info->bmiHeader.biCompression = BI_RGB;
409 break;
411 case WINED3DFMT_B5G5R5X1_UNORM:
412 case WINED3DFMT_B5G5R5A1_UNORM:
413 case WINED3DFMT_B4G4R4A4_UNORM:
414 case WINED3DFMT_B4G4R4X4_UNORM:
415 case WINED3DFMT_B2G3R3_UNORM:
416 case WINED3DFMT_B2G3R3A8_UNORM:
417 case WINED3DFMT_R10G10B10A2_UNORM:
418 case WINED3DFMT_R8G8B8A8_UNORM:
419 case WINED3DFMT_R8G8B8X8_UNORM:
420 case WINED3DFMT_B10G10R10A2_UNORM:
421 case WINED3DFMT_B5G6R5_UNORM:
422 case WINED3DFMT_R16G16B16A16_UNORM:
423 b_info->bmiHeader.biCompression = BI_BITFIELDS;
424 get_color_masks(format, masks);
425 break;
427 default:
428 /* Don't know palette */
429 b_info->bmiHeader.biCompression = BI_RGB;
430 break;
433 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
434 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
435 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
436 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
438 if (!surface->dib.DIBsection)
440 ERR("Failed to create DIB section.\n");
441 HeapFree(GetProcessHeap(), 0, b_info);
442 return HRESULT_FROM_WIN32(GetLastError());
445 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
446 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
448 HeapFree(GetProcessHeap(), 0, b_info);
450 /* Now allocate a DC. */
451 surface->hDC = CreateCompatibleDC(0);
452 SelectObject(surface->hDC, surface->dib.DIBsection);
454 surface->flags |= SFLAG_DIBSECTION;
456 return WINED3D_OK;
459 static void surface_get_memory(const struct wined3d_surface *surface, struct wined3d_bo_address *data,
460 DWORD location)
462 if (location & WINED3D_LOCATION_BUFFER)
464 data->addr = NULL;
465 data->buffer_object = surface->container->sub_resources[surface_get_sub_resource_idx(surface)].buffer_object;
466 return;
468 if (location & WINED3D_LOCATION_USER_MEMORY)
470 data->addr = surface->container->user_memory;
471 data->buffer_object = 0;
472 return;
474 if (location & WINED3D_LOCATION_DIB)
476 data->addr = surface->dib.bitmap_data;
477 data->buffer_object = 0;
478 return;
480 if (location & WINED3D_LOCATION_SYSMEM)
482 data->addr = surface->resource.heap_memory;
483 data->buffer_object = 0;
484 return;
487 ERR("Unexpected locations %s.\n", wined3d_debug_location(location));
488 data->addr = NULL;
489 data->buffer_object = 0;
492 static void surface_prepare_system_memory(struct wined3d_surface *surface)
494 TRACE("surface %p.\n", surface);
496 if (surface->resource.heap_memory)
497 return;
499 /* Whatever surface we have, make sure that there is memory allocated
500 * for the downloaded copy, or a PBO to map. */
501 if (!wined3d_resource_allocate_sysmem(&surface->resource))
502 ERR("Failed to allocate system memory.\n");
504 if (surface->locations & WINED3D_LOCATION_SYSMEM)
505 ERR("Surface without system memory has WINED3D_LOCATION_SYSMEM set.\n");
508 static void surface_evict_sysmem(struct wined3d_surface *surface)
510 struct wined3d_texture *texture = surface->container;
512 if (surface->resource.map_count || texture->download_count > MAXLOCKCOUNT
513 || texture->flags & (WINED3D_TEXTURE_CONVERTED | WINED3D_TEXTURE_PIN_SYSMEM))
514 return;
516 wined3d_resource_free_sysmem(&surface->resource);
517 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
520 static BOOL surface_use_pbo(const struct wined3d_surface *surface)
522 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
523 struct wined3d_texture *texture = surface->container;
525 return texture->resource.pool == WINED3D_POOL_DEFAULT
526 && surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU
527 && gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]
528 && !texture->resource.format->convert
529 && !(texture->flags & WINED3D_TEXTURE_PIN_SYSMEM)
530 && !(texture->flags & WINED3D_TEXTURE_COND_NP2_EMULATED);
533 static HRESULT surface_private_setup(struct wined3d_surface *surface)
535 /* TODO: Check against the maximum texture sizes supported by the video card. */
536 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
537 unsigned int pow2Width, pow2Height;
539 TRACE("surface %p.\n", surface);
541 /* Non-power2 support */
542 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT]
543 || gl_info->supported[ARB_TEXTURE_RECTANGLE])
545 pow2Width = surface->resource.width;
546 pow2Height = surface->resource.height;
548 else
550 /* Find the nearest pow2 match */
551 pow2Width = pow2Height = 1;
552 while (pow2Width < surface->resource.width)
553 pow2Width <<= 1;
554 while (pow2Height < surface->resource.height)
555 pow2Height <<= 1;
557 surface->pow2Width = pow2Width;
558 surface->pow2Height = pow2Height;
560 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
562 /* TODO: Add support for non power two compressed textures. */
563 if (surface->container->resource.format_flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_HEIGHT_SCALE))
565 FIXME("(%p) Compressed or height scaled non-power-two textures are not supported w(%d) h(%d)\n",
566 surface, surface->resource.width, surface->resource.height);
567 return WINED3DERR_NOTAVAILABLE;
571 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
572 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
574 /* One of three options:
575 * 1: Do the same as we do with NPOT and scale the texture, (any
576 * texture ops would require the texture to be scaled which is
577 * potentially slow)
578 * 2: Set the texture to the maximum size (bad idea).
579 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
580 * 4: Create the surface, but allow it to be used only for DirectDraw
581 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
582 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
583 * the render target. */
584 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
586 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
587 return WINED3DERR_NOTAVAILABLE;
590 /* We should never use this surface in combination with OpenGL! */
591 TRACE("Creating an oversized surface: %ux%u.\n",
592 surface->pow2Width, surface->pow2Height);
595 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
596 surface->locations = WINED3D_LOCATION_DISCARDED;
598 if (surface_use_pbo(surface))
599 surface->resource.map_binding = WINED3D_LOCATION_BUFFER;
601 return WINED3D_OK;
604 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
606 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
607 return FALSE;
608 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
609 return FALSE;
610 return TRUE;
613 static void surface_depth_blt_fbo(const struct wined3d_device *device,
614 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
615 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
617 const struct wined3d_gl_info *gl_info;
618 struct wined3d_context *context;
619 DWORD src_mask, dst_mask;
620 GLbitfield gl_mask;
622 TRACE("device %p\n", device);
623 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
624 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
625 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
626 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
628 src_mask = src_surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
629 dst_mask = dst_surface->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
631 if (src_mask != dst_mask)
633 ERR("Incompatible formats %s and %s.\n",
634 debug_d3dformat(src_surface->resource.format->id),
635 debug_d3dformat(dst_surface->resource.format->id));
636 return;
639 if (!src_mask)
641 ERR("Not a depth / stencil format: %s.\n",
642 debug_d3dformat(src_surface->resource.format->id));
643 return;
646 gl_mask = 0;
647 if (src_mask & WINED3DFMT_FLAG_DEPTH)
648 gl_mask |= GL_DEPTH_BUFFER_BIT;
649 if (src_mask & WINED3DFMT_FLAG_STENCIL)
650 gl_mask |= GL_STENCIL_BUFFER_BIT;
652 context = context_acquire(device, NULL);
653 if (!context->valid)
655 context_release(context);
656 WARN("Invalid context, skipping blit.\n");
657 return;
660 /* Make sure the locations are up-to-date. Loading the destination
661 * surface isn't required if the entire surface is overwritten. */
662 surface_load_location(src_surface, context, src_location);
663 if (!surface_is_full_rect(dst_surface, dst_rect))
664 surface_load_location(dst_surface, context, dst_location);
665 else
666 wined3d_surface_prepare(dst_surface, context, dst_location);
668 gl_info = context->gl_info;
670 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
671 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
673 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
674 context_set_draw_buffer(context, GL_NONE);
675 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
676 context_invalidate_state(context, STATE_FRAMEBUFFER);
678 if (gl_mask & GL_DEPTH_BUFFER_BIT)
680 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
681 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
683 if (gl_mask & GL_STENCIL_BUFFER_BIT)
685 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
687 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
688 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
690 gl_info->gl_ops.gl.p_glStencilMask(~0U);
691 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
694 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
695 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
697 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
698 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
699 checkGLcall("glBlitFramebuffer()");
701 if (wined3d_settings.strict_draw_ordering)
702 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
704 context_release(context);
707 /* Blit between surface locations. Onscreen on different swapchains is not supported.
708 * Depth / stencil is not supported. Context activation is done by the caller. */
709 static void surface_blt_fbo(const struct wined3d_device *device,
710 struct wined3d_context *old_ctx, enum wined3d_texture_filter_type filter,
711 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
712 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
714 const struct wined3d_gl_info *gl_info;
715 struct wined3d_context *context = old_ctx;
716 struct wined3d_surface *required_rt, *restore_rt = NULL;
717 RECT src_rect, dst_rect;
718 GLenum gl_filter;
719 GLenum buffer;
721 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
722 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
723 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
724 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
725 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
727 src_rect = *src_rect_in;
728 dst_rect = *dst_rect_in;
730 switch (filter)
732 case WINED3D_TEXF_LINEAR:
733 gl_filter = GL_LINEAR;
734 break;
736 default:
737 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
738 case WINED3D_TEXF_NONE:
739 case WINED3D_TEXF_POINT:
740 gl_filter = GL_NEAREST;
741 break;
744 /* Resolve the source surface first if needed. */
745 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
746 && (src_surface->resource.format->id != dst_surface->resource.format->id
747 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
748 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
749 src_location = WINED3D_LOCATION_RB_RESOLVED;
751 /* Make sure the locations are up-to-date. Loading the destination
752 * surface isn't required if the entire surface is overwritten. (And is
753 * in fact harmful if we're being called by surface_load_location() with
754 * the purpose of loading the destination surface.) */
755 surface_load_location(src_surface, old_ctx, src_location);
756 if (!surface_is_full_rect(dst_surface, &dst_rect))
757 surface_load_location(dst_surface, old_ctx, dst_location);
758 else
759 wined3d_surface_prepare(dst_surface, old_ctx, dst_location);
762 if (src_location == WINED3D_LOCATION_DRAWABLE) required_rt = src_surface;
763 else if (dst_location == WINED3D_LOCATION_DRAWABLE) required_rt = dst_surface;
764 else required_rt = NULL;
766 if (required_rt && required_rt != old_ctx->current_rt)
768 restore_rt = old_ctx->current_rt;
769 context = context_acquire(device, required_rt);
772 if (!context->valid)
774 context_release(context);
775 WARN("Invalid context, skipping blit.\n");
776 return;
779 gl_info = context->gl_info;
781 if (src_location == WINED3D_LOCATION_DRAWABLE)
783 TRACE("Source surface %p is onscreen.\n", src_surface);
784 buffer = surface_get_gl_buffer(src_surface);
785 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
787 else
789 TRACE("Source surface %p is offscreen.\n", src_surface);
790 buffer = GL_COLOR_ATTACHMENT0;
793 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
794 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
795 checkGLcall("glReadBuffer()");
796 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
798 if (dst_location == WINED3D_LOCATION_DRAWABLE)
800 TRACE("Destination surface %p is onscreen.\n", dst_surface);
801 buffer = surface_get_gl_buffer(dst_surface);
802 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
804 else
806 TRACE("Destination surface %p is offscreen.\n", dst_surface);
807 buffer = GL_COLOR_ATTACHMENT0;
810 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
811 context_set_draw_buffer(context, buffer);
812 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
813 context_invalidate_state(context, STATE_FRAMEBUFFER);
815 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
816 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
817 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
818 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
819 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
821 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
822 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
824 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
825 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
826 checkGLcall("glBlitFramebuffer()");
828 if (wined3d_settings.strict_draw_ordering
829 || (dst_location == WINED3D_LOCATION_DRAWABLE
830 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
831 gl_info->gl_ops.gl.p_glFlush();
833 if (restore_rt)
834 context_restore(context, restore_rt);
837 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
838 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
839 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
841 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
842 return FALSE;
844 /* Source and/or destination need to be on the GL side */
845 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
846 return FALSE;
848 switch (blit_op)
850 case WINED3D_BLIT_OP_COLOR_BLIT:
851 if (!((src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
852 || (src_usage & WINED3DUSAGE_RENDERTARGET)))
853 return FALSE;
854 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
855 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
856 return FALSE;
857 if (!(src_format->id == dst_format->id
858 || (is_identity_fixup(src_format->color_fixup)
859 && is_identity_fixup(dst_format->color_fixup))))
860 return FALSE;
861 break;
863 case WINED3D_BLIT_OP_DEPTH_BLIT:
864 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
865 return FALSE;
866 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
867 return FALSE;
868 /* Accept pure swizzle fixups for depth formats. In general we
869 * ignore the stencil component (if present) at the moment and the
870 * swizzle is not relevant with just the depth component. */
871 if (is_complex_fixup(src_format->color_fixup) || is_complex_fixup(dst_format->color_fixup)
872 || is_scaling_fixup(src_format->color_fixup) || is_scaling_fixup(dst_format->color_fixup))
873 return FALSE;
874 break;
876 default:
877 return FALSE;
880 return TRUE;
883 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
885 const struct wined3d_format *format = surface->resource.format;
887 switch (format->id)
889 case WINED3DFMT_S1_UINT_D15_UNORM:
890 *float_depth = depth / (float)0x00007fff;
891 break;
893 case WINED3DFMT_D16_UNORM:
894 *float_depth = depth / (float)0x0000ffff;
895 break;
897 case WINED3DFMT_D24_UNORM_S8_UINT:
898 case WINED3DFMT_X8D24_UNORM:
899 *float_depth = depth / (float)0x00ffffff;
900 break;
902 case WINED3DFMT_D32_UNORM:
903 *float_depth = depth / (float)0xffffffff;
904 break;
906 default:
907 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
908 return FALSE;
911 return TRUE;
914 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
916 struct wined3d_resource *resource = &surface->container->resource;
917 struct wined3d_device *device = resource->device;
918 struct wined3d_rendertarget_view_desc view_desc;
919 struct wined3d_rendertarget_view *view;
920 const struct blit_shader *blitter;
921 HRESULT hr;
923 if (!(blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info,
924 WINED3D_BLIT_OP_DEPTH_FILL, NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format)))
926 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
927 return WINED3DERR_INVALIDCALL;
930 view_desc.format_id = resource->format->id;
931 view_desc.u.texture.level_idx = surface->texture_level;
932 view_desc.u.texture.layer_idx = surface->texture_layer;
933 view_desc.u.texture.layer_count = 1;
934 if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc,
935 resource, NULL, &wined3d_null_parent_ops, &view)))
937 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
938 return hr;
941 hr = blitter->depth_fill(device, view, rect, depth);
942 wined3d_rendertarget_view_decref(view);
944 return hr;
947 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
948 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
950 struct wined3d_device *device = src_surface->resource.device;
952 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
953 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
954 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
955 return WINED3DERR_INVALIDCALL;
957 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
959 surface_modify_ds_location(dst_surface, dst_location,
960 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
962 return WINED3D_OK;
965 static ULONG surface_resource_incref(struct wined3d_resource *resource)
967 struct wined3d_surface *surface = surface_from_resource(resource);
969 TRACE("surface %p, container %p.\n", surface, surface->container);
971 return wined3d_texture_incref(surface->container);
974 static ULONG surface_resource_decref(struct wined3d_resource *resource)
976 struct wined3d_surface *surface = surface_from_resource(resource);
978 TRACE("surface %p, container %p.\n", surface, surface->container);
980 return wined3d_texture_decref(surface->container);
983 static void surface_unload(struct wined3d_resource *resource)
985 struct wined3d_surface *surface = surface_from_resource(resource);
986 struct wined3d_renderbuffer_entry *entry, *entry2;
987 struct wined3d_device *device = resource->device;
988 const struct wined3d_gl_info *gl_info;
989 struct wined3d_context *context;
991 TRACE("surface %p.\n", surface);
993 context = context_acquire(device, NULL);
994 gl_info = context->gl_info;
996 if (resource->pool == WINED3D_POOL_DEFAULT)
998 /* Default pool resources are supposed to be destroyed before Reset is called.
999 * Implicit resources stay however. So this means we have an implicit render target
1000 * or depth stencil. The content may be destroyed, but we still have to tear down
1001 * opengl resources, so we cannot leave early.
1003 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1004 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1005 * or the depth stencil into an FBO the texture or render buffer will be removed
1006 * and all flags get lost */
1007 if (resource->usage & WINED3DUSAGE_DEPTHSTENCIL)
1009 surface_validate_location(surface, WINED3D_LOCATION_DISCARDED);
1010 surface_invalidate_location(surface, ~WINED3D_LOCATION_DISCARDED);
1012 else
1014 surface_prepare_system_memory(surface);
1015 memset(surface->resource.heap_memory, 0, surface->resource.size);
1016 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
1017 surface_invalidate_location(surface, ~WINED3D_LOCATION_SYSMEM);
1020 else
1022 surface_load_location(surface, context, surface->resource.map_binding);
1023 surface_invalidate_location(surface, ~surface->resource.map_binding);
1026 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1027 * all application-created targets the application has to release the surface
1028 * before calling _Reset
1030 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1032 context_gl_resource_released(device, entry->id, TRUE);
1033 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1034 list_remove(&entry->entry);
1035 HeapFree(GetProcessHeap(), 0, entry);
1037 list_init(&surface->renderbuffers);
1038 surface->current_renderbuffer = NULL;
1040 if (surface->rb_multisample)
1042 context_gl_resource_released(device, surface->rb_multisample, TRUE);
1043 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1044 surface->rb_multisample = 0;
1046 if (surface->rb_resolved)
1048 context_gl_resource_released(device, surface->rb_resolved, TRUE);
1049 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1050 surface->rb_resolved = 0;
1053 context_release(context);
1055 resource_unload(resource);
1058 static HRESULT surface_resource_sub_resource_map(struct wined3d_resource *resource, unsigned int sub_resource_idx,
1059 struct wined3d_map_desc *map_desc, const struct wined3d_box *box, DWORD flags)
1061 ERR("Not supported on sub-resources.\n");
1062 return WINED3DERR_INVALIDCALL;
1065 static HRESULT surface_resource_sub_resource_unmap(struct wined3d_resource *resource, unsigned int sub_resource_idx)
1067 ERR("Not supported on sub-resources.\n");
1068 return WINED3DERR_INVALIDCALL;
1071 static const struct wined3d_resource_ops surface_resource_ops =
1073 surface_resource_incref,
1074 surface_resource_decref,
1075 surface_unload,
1076 surface_resource_sub_resource_map,
1077 surface_resource_sub_resource_unmap,
1080 static const struct wined3d_surface_ops surface_ops =
1082 surface_private_setup,
1085 /*****************************************************************************
1086 * Initializes the GDI surface, aka creates the DIB section we render to
1087 * The DIB section creation is done by calling GetDC, which will create the
1088 * section and releasing the dc to allow the app to use it. The dib section
1089 * will stay until the surface is released
1091 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1092 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1093 * avoid confusion in the shared surface code.
1095 * Returns:
1096 * WINED3D_OK on success
1097 * The return values of called methods on failure
1099 *****************************************************************************/
1100 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1102 HRESULT hr;
1104 TRACE("surface %p.\n", surface);
1106 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1108 ERR("Overlays not yet supported by GDI surfaces.\n");
1109 return WINED3DERR_INVALIDCALL;
1112 /* Sysmem textures have memory already allocated - release it,
1113 * this avoids an unnecessary memcpy. */
1114 hr = surface_create_dib_section(surface);
1115 if (FAILED(hr))
1116 return hr;
1117 surface->resource.map_binding = WINED3D_LOCATION_DIB;
1119 /* We don't mind the nonpow2 stuff in GDI. */
1120 surface->pow2Width = surface->resource.width;
1121 surface->pow2Height = surface->resource.height;
1123 return WINED3D_OK;
1126 static const struct wined3d_surface_ops gdi_surface_ops =
1128 gdi_surface_private_setup,
1131 /* This call just downloads data, the caller is responsible for binding the
1132 * correct texture. */
1133 /* Context activation is done by the caller. */
1134 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1135 DWORD dst_location)
1137 const struct wined3d_format *format = surface->resource.format;
1138 struct wined3d_bo_address data;
1140 /* Only support read back of converted P8 surfaces. */
1141 if (surface->container->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1143 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1144 return;
1147 surface_get_memory(surface, &data, dst_location);
1149 if (surface->container->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
1151 TRACE("(%p) : Calling glGetCompressedTexImage level %d, format %#x, type %#x, data %p.\n",
1152 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1154 if (data.buffer_object)
1156 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1157 checkGLcall("glBindBuffer");
1158 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target, surface->texture_level, NULL));
1159 checkGLcall("glGetCompressedTexImage");
1160 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1161 checkGLcall("glBindBuffer");
1163 else
1165 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target,
1166 surface->texture_level, data.addr));
1167 checkGLcall("glGetCompressedTexImage");
1170 else
1172 unsigned int dst_row_pitch, dst_slice_pitch;
1173 unsigned int src_row_pitch, src_slice_pitch;
1174 GLenum gl_format = format->glFormat;
1175 GLenum gl_type = format->glType;
1176 void *mem;
1178 if (surface->container->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
1180 wined3d_texture_get_pitch(surface->container, surface->texture_level, &dst_row_pitch, &dst_slice_pitch);
1181 wined3d_format_calculate_pitch(format, surface->resource.device->surface_alignment,
1182 surface->pow2Width, surface->pow2Height, &src_row_pitch, &src_slice_pitch);
1183 mem = HeapAlloc(GetProcessHeap(), 0, src_slice_pitch);
1185 else
1187 mem = data.addr;
1190 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1191 surface, surface->texture_level, gl_format, gl_type, mem);
1193 if (data.buffer_object)
1195 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1196 checkGLcall("glBindBuffer");
1198 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1199 gl_format, gl_type, NULL);
1200 checkGLcall("glGetTexImage");
1202 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1203 checkGLcall("glBindBuffer");
1205 else
1207 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1208 gl_format, gl_type, mem);
1209 checkGLcall("glGetTexImage");
1212 if (surface->container->flags & WINED3D_TEXTURE_COND_NP2_EMULATED)
1214 const BYTE *src_data;
1215 BYTE *dst_data;
1216 UINT y;
1218 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1219 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1220 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1222 * We're doing this...
1224 * instead of boxing the texture :
1225 * |<-texture width ->| -->pow2width| /\
1226 * |111111111111111111| | |
1227 * |222 Texture 222222| boxed empty | texture height
1228 * |3333 Data 33333333| | |
1229 * |444444444444444444| | \/
1230 * ----------------------------------- |
1231 * | boxed empty | boxed empty | pow2height
1232 * | | | \/
1233 * -----------------------------------
1236 * we're repacking the data to the expected texture width
1238 * |<-texture width ->| -->pow2width| /\
1239 * |111111111111111111222222222222222| |
1240 * |222333333333333333333444444444444| texture height
1241 * |444444 | |
1242 * | | \/
1243 * | | |
1244 * | empty | pow2height
1245 * | | \/
1246 * -----------------------------------
1248 * == is the same as
1250 * |<-texture width ->| /\
1251 * |111111111111111111|
1252 * |222222222222222222|texture height
1253 * |333333333333333333|
1254 * |444444444444444444| \/
1255 * --------------------
1257 * This also means that any references to surface memory should work with the data as if it were a
1258 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
1260 * internally the texture is still stored in a boxed format so any references to textureName will
1261 * get a boxed texture with width pow2width and not a texture of width resource.width. */
1262 src_data = mem;
1263 dst_data = data.addr;
1264 TRACE("Repacking the surface data from pitch %u to pitch %u.\n", src_row_pitch, dst_row_pitch);
1265 for (y = 0; y < surface->resource.height; ++y)
1267 memcpy(dst_data, src_data, dst_row_pitch);
1268 src_data += src_row_pitch;
1269 dst_data += dst_row_pitch;
1272 HeapFree(GetProcessHeap(), 0, mem);
1277 /* This call just uploads data, the caller is responsible for binding the
1278 * correct texture. */
1279 /* Context activation is done by the caller. */
1280 void wined3d_surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1281 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1282 BOOL srgb, const struct wined3d_const_bo_address *data)
1284 UINT update_w = src_rect->right - src_rect->left;
1285 UINT update_h = src_rect->bottom - src_rect->top;
1287 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1288 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1289 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1291 if (surface->resource.map_count)
1293 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
1294 surface->container->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
1297 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE)
1299 update_h *= format->height_scale.numerator;
1300 update_h /= format->height_scale.denominator;
1303 if (data->buffer_object)
1305 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
1306 checkGLcall("glBindBuffer");
1309 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
1311 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1312 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1313 const BYTE *addr = data->addr;
1314 GLenum internal;
1316 addr += (src_rect->top / format->block_height) * src_pitch;
1317 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1319 if (srgb)
1320 internal = format->glGammaInternal;
1321 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET
1322 && wined3d_resource_is_offscreen(&surface->container->resource))
1323 internal = format->rtInternal;
1324 else
1325 internal = format->glInternal;
1327 TRACE("glCompressedTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, "
1328 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1329 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1331 if (row_length == src_pitch)
1333 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1334 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1336 else
1338 UINT row, y;
1340 /* glCompressedTexSubImage2D() ignores pixel store state, so we
1341 * can't use the unpack row length like for glTexSubImage2D. */
1342 for (row = 0, y = dst_point->y; row < row_count; ++row)
1344 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1345 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1346 y += format->block_height;
1347 addr += src_pitch;
1350 checkGLcall("glCompressedTexSubImage2D");
1352 else
1354 const BYTE *addr = data->addr;
1356 addr += src_rect->top * src_pitch;
1357 addr += src_rect->left * format->byte_count;
1359 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1360 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1361 update_w, update_h, format->glFormat, format->glType, addr);
1363 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1364 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1365 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1366 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1367 checkGLcall("glTexSubImage2D");
1370 if (data->buffer_object)
1372 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
1373 checkGLcall("glBindBuffer");
1376 if (wined3d_settings.strict_draw_ordering)
1377 gl_info->gl_ops.gl.p_glFlush();
1379 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1381 struct wined3d_device *device = surface->resource.device;
1382 unsigned int i;
1384 for (i = 0; i < device->context_count; ++i)
1386 context_surface_update(device->contexts[i], surface);
1391 static BOOL surface_check_block_align_rect(struct wined3d_surface *surface, const RECT *rect)
1393 struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
1395 return wined3d_texture_check_block_align(surface->container, surface->texture_level, &box);
1398 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1399 struct wined3d_surface *src_surface, const RECT *src_rect)
1401 unsigned int src_row_pitch, src_slice_pitch;
1402 const struct wined3d_format *src_format;
1403 const struct wined3d_format *dst_format;
1404 unsigned int src_fmt_flags, dst_fmt_flags;
1405 const struct wined3d_gl_info *gl_info;
1406 struct wined3d_context *context;
1407 struct wined3d_bo_address data;
1408 UINT update_w, update_h;
1409 UINT dst_w, dst_h;
1410 RECT r, dst_rect;
1411 POINT p;
1413 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1414 dst_surface, wine_dbgstr_point(dst_point),
1415 src_surface, wine_dbgstr_rect(src_rect));
1417 src_format = src_surface->resource.format;
1418 dst_format = dst_surface->resource.format;
1419 src_fmt_flags = src_surface->container->resource.format_flags;
1420 dst_fmt_flags = dst_surface->container->resource.format_flags;
1422 if (src_format->id != dst_format->id)
1424 WARN("Source and destination surfaces should have the same format.\n");
1425 return WINED3DERR_INVALIDCALL;
1428 if (!dst_point)
1430 p.x = 0;
1431 p.y = 0;
1432 dst_point = &p;
1434 else if (dst_point->x < 0 || dst_point->y < 0)
1436 WARN("Invalid destination point.\n");
1437 return WINED3DERR_INVALIDCALL;
1440 if (!src_rect)
1442 SetRect(&r, 0, 0, src_surface->resource.width, src_surface->resource.height);
1443 src_rect = &r;
1445 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1446 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1448 WARN("Invalid source rectangle.\n");
1449 return WINED3DERR_INVALIDCALL;
1452 dst_w = dst_surface->resource.width;
1453 dst_h = dst_surface->resource.height;
1455 update_w = src_rect->right - src_rect->left;
1456 update_h = src_rect->bottom - src_rect->top;
1458 if (update_w > dst_w || dst_point->x > dst_w - update_w
1459 || update_h > dst_h || dst_point->y > dst_h - update_h)
1461 WARN("Destination out of bounds.\n");
1462 return WINED3DERR_INVALIDCALL;
1465 if ((src_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(src_surface, src_rect))
1467 WARN("Source rectangle not block-aligned.\n");
1468 return WINED3DERR_INVALIDCALL;
1471 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1472 if ((dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align_rect(dst_surface, &dst_rect))
1474 WARN("Destination rectangle not block-aligned.\n");
1475 return WINED3DERR_INVALIDCALL;
1478 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1479 if (dst_format->convert || wined3d_format_get_color_key_conversion(dst_surface->container, FALSE))
1480 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1482 context = context_acquire(dst_surface->resource.device, NULL);
1483 gl_info = context->gl_info;
1485 /* Only load the surface for partial updates. For newly allocated texture
1486 * the texture wouldn't be the current location, and we'd upload zeroes
1487 * just to overwrite them again. */
1488 if (update_w == dst_w && update_h == dst_h)
1489 wined3d_texture_prepare_texture(dst_surface->container, context, FALSE);
1490 else
1491 surface_load_location(dst_surface, context, WINED3D_LOCATION_TEXTURE_RGB);
1492 wined3d_texture_bind_and_dirtify(dst_surface->container, context, FALSE);
1494 surface_get_memory(src_surface, &data, src_surface->locations);
1495 wined3d_texture_get_pitch(src_surface->container, src_surface->texture_level, &src_row_pitch, &src_slice_pitch);
1497 wined3d_surface_upload_data(dst_surface, gl_info, src_format, src_rect,
1498 src_row_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
1500 context_release(context);
1502 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1503 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1505 return WINED3D_OK;
1508 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1509 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1510 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1511 /* Context activation is done by the caller. */
1512 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1514 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
1515 struct wined3d_renderbuffer_entry *entry;
1516 GLuint renderbuffer = 0;
1517 unsigned int src_width, src_height;
1518 unsigned int width, height;
1520 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
1522 width = rt->pow2Width;
1523 height = rt->pow2Height;
1525 else
1527 width = surface->pow2Width;
1528 height = surface->pow2Height;
1531 src_width = surface->pow2Width;
1532 src_height = surface->pow2Height;
1534 /* A depth stencil smaller than the render target is not valid */
1535 if (width > src_width || height > src_height) return;
1537 /* Remove any renderbuffer set if the sizes match */
1538 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1539 || (width == src_width && height == src_height))
1541 surface->current_renderbuffer = NULL;
1542 return;
1545 /* Look if we've already got a renderbuffer of the correct dimensions */
1546 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1548 if (entry->width == width && entry->height == height)
1550 renderbuffer = entry->id;
1551 surface->current_renderbuffer = entry;
1552 break;
1556 if (!renderbuffer)
1558 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1559 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1560 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1561 surface->resource.format->glInternal, width, height);
1563 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1564 entry->width = width;
1565 entry->height = height;
1566 entry->id = renderbuffer;
1567 list_add_head(&surface->renderbuffers, &entry->entry);
1569 surface->current_renderbuffer = entry;
1572 checkGLcall("set_compatible_renderbuffer");
1575 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
1577 const struct wined3d_swapchain *swapchain = surface->container->swapchain;
1579 TRACE("surface %p.\n", surface);
1581 if (!swapchain)
1583 ERR("Surface %p is not on a swapchain.\n", surface);
1584 return GL_NONE;
1587 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface->container)
1589 if (swapchain->render_to_fbo)
1591 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
1592 return GL_COLOR_ATTACHMENT0;
1594 TRACE("Returning GL_BACK\n");
1595 return GL_BACK;
1597 else if (surface->container == swapchain->front_buffer)
1599 TRACE("Returning GL_FRONT\n");
1600 return GL_FRONT;
1603 FIXME("Higher back buffer, returning GL_BACK\n");
1604 return GL_BACK;
1607 /* Context activation is done by the caller. */
1608 void surface_load(struct wined3d_surface *surface, struct wined3d_context *context, BOOL srgb)
1610 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
1612 TRACE("surface %p, srgb %#x.\n", surface, srgb);
1614 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
1615 ERR("Not supported on scratch surfaces.\n");
1617 if (surface->locations & location)
1619 TRACE("surface is already in texture\n");
1620 return;
1622 TRACE("Reloading because surface is dirty.\n");
1624 surface_load_location(surface, context, location);
1625 surface_evict_sysmem(surface);
1628 /* See also float_16_to_32() in wined3d_private.h */
1629 static inline unsigned short float_32_to_16(const float *in)
1631 int exp = 0;
1632 float tmp = fabsf(*in);
1633 unsigned int mantissa;
1634 unsigned short ret;
1636 /* Deal with special numbers */
1637 if (*in == 0.0f)
1638 return 0x0000;
1639 if (isnan(*in))
1640 return 0x7c01;
1641 if (isinf(*in))
1642 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1644 if (tmp < (float)(1u << 10))
1648 tmp = tmp * 2.0f;
1649 exp--;
1650 } while (tmp < (float)(1u << 10));
1652 else if (tmp >= (float)(1u << 11))
1656 tmp /= 2.0f;
1657 exp++;
1658 } while (tmp >= (float)(1u << 11));
1661 mantissa = (unsigned int)tmp;
1662 if (tmp - mantissa >= 0.5f)
1663 ++mantissa; /* Round to nearest, away from zero. */
1665 exp += 10; /* Normalize the mantissa. */
1666 exp += 15; /* Exponent is encoded with excess 15. */
1668 if (exp > 30) /* too big */
1670 ret = 0x7c00; /* INF */
1672 else if (exp <= 0)
1674 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1675 while (exp <= 0)
1677 mantissa = mantissa >> 1;
1678 ++exp;
1680 ret = mantissa & 0x3ff;
1682 else
1684 ret = (exp << 10) | (mantissa & 0x3ff);
1687 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1688 return ret;
1691 HRESULT wined3d_surface_update_desc(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1693 struct wined3d_resource *texture_resource = &surface->container->resource;
1694 unsigned int width, height;
1695 BOOL create_dib = FALSE;
1696 DWORD valid_location = 0;
1697 HRESULT hr;
1699 if (surface->flags & SFLAG_DIBSECTION)
1701 DeleteDC(surface->hDC);
1702 DeleteObject(surface->dib.DIBsection);
1703 surface->dib.bitmap_data = NULL;
1704 surface->flags &= ~SFLAG_DIBSECTION;
1705 create_dib = TRUE;
1708 surface->locations = 0;
1709 wined3d_resource_free_sysmem(&surface->resource);
1711 width = texture_resource->width;
1712 height = texture_resource->height;
1713 surface->resource.width = width;
1714 surface->resource.height = height;
1715 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
1716 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
1718 surface->pow2Width = width;
1719 surface->pow2Height = height;
1721 else
1723 surface->pow2Width = surface->pow2Height = 1;
1724 while (surface->pow2Width < width)
1725 surface->pow2Width <<= 1;
1726 while (surface->pow2Height < height)
1727 surface->pow2Height <<= 1;
1730 if (surface->container->user_memory)
1732 surface->resource.map_binding = WINED3D_LOCATION_USER_MEMORY;
1733 valid_location = WINED3D_LOCATION_USER_MEMORY;
1735 surface->resource.format = texture_resource->format;
1736 surface->resource.multisample_type = texture_resource->multisample_type;
1737 surface->resource.multisample_quality = texture_resource->multisample_quality;
1738 surface->resource.size = surface->container->slice_pitch;
1740 /* The format might be changed to a format that needs conversion.
1741 * If the surface didn't use PBOs previously but could now, don't
1742 * change it - whatever made us not use PBOs might come back, e.g.
1743 * color keys. */
1744 if (surface->resource.map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
1745 surface->resource.map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
1747 if (create_dib)
1749 if (FAILED(hr = surface_create_dib_section(surface)))
1751 ERR("Failed to create dib section, hr %#x.\n", hr);
1752 return hr;
1754 if (!valid_location)
1755 valid_location = WINED3D_LOCATION_DIB;
1758 if (!valid_location)
1760 surface_prepare_system_memory(surface);
1761 valid_location = WINED3D_LOCATION_SYSMEM;
1764 surface_validate_location(surface, valid_location);
1766 return WINED3D_OK;
1769 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
1770 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1772 unsigned short *dst_s;
1773 const float *src_f;
1774 unsigned int x, y;
1776 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1778 for (y = 0; y < h; ++y)
1780 src_f = (const float *)(src + y * pitch_in);
1781 dst_s = (unsigned short *) (dst + y * pitch_out);
1782 for (x = 0; x < w; ++x)
1784 dst_s[x] = float_32_to_16(src_f + x);
1789 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
1790 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1792 static const unsigned char convert_5to8[] =
1794 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
1795 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
1796 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
1797 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
1799 static const unsigned char convert_6to8[] =
1801 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
1802 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
1803 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
1804 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
1805 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
1806 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
1807 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
1808 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
1810 unsigned int x, y;
1812 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1814 for (y = 0; y < h; ++y)
1816 const WORD *src_line = (const WORD *)(src + y * pitch_in);
1817 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1818 for (x = 0; x < w; ++x)
1820 WORD pixel = src_line[x];
1821 dst_line[x] = 0xff000000u
1822 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
1823 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
1824 | convert_5to8[(pixel & 0x001fu)];
1829 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
1830 * in both cases we're just setting the X / Alpha channel to 0xff. */
1831 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
1832 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1834 unsigned int x, y;
1836 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1838 for (y = 0; y < h; ++y)
1840 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
1841 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1843 for (x = 0; x < w; ++x)
1845 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
1850 static inline BYTE cliptobyte(int x)
1852 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
1855 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
1856 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1858 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1859 unsigned int x, y;
1861 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
1863 for (y = 0; y < h; ++y)
1865 const BYTE *src_line = src + y * pitch_in;
1866 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
1867 for (x = 0; x < w; ++x)
1869 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1870 * C = Y - 16; D = U - 128; E = V - 128;
1871 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1872 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1873 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1874 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1875 * U and V are shared between the pixels. */
1876 if (!(x & 1)) /* For every even pixel, read new U and V. */
1878 d = (int) src_line[1] - 128;
1879 e = (int) src_line[3] - 128;
1880 r2 = 409 * e + 128;
1881 g2 = - 100 * d - 208 * e + 128;
1882 b2 = 516 * d + 128;
1884 c2 = 298 * ((int) src_line[0] - 16);
1885 dst_line[x] = 0xff000000
1886 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
1887 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
1888 | cliptobyte((c2 + b2) >> 8); /* blue */
1889 /* Scale RGB values to 0..255 range,
1890 * then clip them if still not in range (may be negative),
1891 * then shift them within DWORD if necessary. */
1892 src_line += 2;
1897 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
1898 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
1900 unsigned int x, y;
1901 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
1903 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
1905 for (y = 0; y < h; ++y)
1907 const BYTE *src_line = src + y * pitch_in;
1908 WORD *dst_line = (WORD *)(dst + y * pitch_out);
1909 for (x = 0; x < w; ++x)
1911 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
1912 * C = Y - 16; D = U - 128; E = V - 128;
1913 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
1914 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
1915 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
1916 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
1917 * U and V are shared between the pixels. */
1918 if (!(x & 1)) /* For every even pixel, read new U and V. */
1920 d = (int) src_line[1] - 128;
1921 e = (int) src_line[3] - 128;
1922 r2 = 409 * e + 128;
1923 g2 = - 100 * d - 208 * e + 128;
1924 b2 = 516 * d + 128;
1926 c2 = 298 * ((int) src_line[0] - 16);
1927 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
1928 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
1929 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
1930 /* Scale RGB values to 0..255 range,
1931 * then clip them if still not in range (may be negative),
1932 * then shift them within DWORD if necessary. */
1933 src_line += 2;
1938 struct d3dfmt_converter_desc
1940 enum wined3d_format_id from, to;
1941 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
1944 static const struct d3dfmt_converter_desc converters[] =
1946 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
1947 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
1948 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1949 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
1950 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
1951 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
1954 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
1955 enum wined3d_format_id to)
1957 unsigned int i;
1959 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
1961 if (converters[i].from == from && converters[i].to == to)
1962 return &converters[i];
1965 return NULL;
1968 static struct wined3d_texture *surface_convert_format(struct wined3d_texture *src_texture,
1969 unsigned int sub_resource_idx, enum wined3d_format_id format)
1971 struct wined3d_map_desc src_map, dst_map;
1972 const struct d3dfmt_converter_desc *conv;
1973 struct wined3d_texture *dst_texture;
1974 struct wined3d_resource_desc desc;
1976 if (!(conv = find_converter(src_texture->resource.format->id, format)))
1978 FIXME("Cannot find a conversion function from format %s to %s.\n",
1979 debug_d3dformat(src_texture->resource.format->id), debug_d3dformat(format));
1980 return NULL;
1983 /* FIXME: Multisampled conversion? */
1984 wined3d_resource_get_desc(src_texture->sub_resources[sub_resource_idx].resource, &desc);
1985 desc.resource_type = WINED3D_RTYPE_TEXTURE_2D;
1986 desc.format = format;
1987 desc.usage = 0;
1988 desc.pool = WINED3D_POOL_SCRATCH;
1989 if (FAILED(wined3d_texture_create(src_texture->resource.device, &desc, 1,
1990 WINED3D_TEXTURE_CREATE_MAPPABLE | WINED3D_TEXTURE_CREATE_DISCARD,
1991 NULL, NULL, &wined3d_null_parent_ops, &dst_texture)))
1993 ERR("Failed to create a destination texture for conversion.\n");
1994 return NULL;
1997 memset(&src_map, 0, sizeof(src_map));
1998 memset(&dst_map, 0, sizeof(dst_map));
2000 if (FAILED(wined3d_resource_map(&src_texture->resource, sub_resource_idx,
2001 &src_map, NULL, WINED3D_MAP_READONLY)))
2003 ERR("Failed to map the source texture.\n");
2004 wined3d_texture_decref(dst_texture);
2005 return NULL;
2007 if (FAILED(wined3d_resource_map(&dst_texture->resource, 0, &dst_map, NULL, 0)))
2009 ERR("Failed to map the destination texture.\n");
2010 wined3d_resource_unmap(&src_texture->resource, sub_resource_idx);
2011 wined3d_texture_decref(dst_texture);
2012 return NULL;
2015 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch, desc.width, desc.height);
2017 wined3d_resource_unmap(&dst_texture->resource, 0);
2018 wined3d_resource_unmap(&src_texture->resource, sub_resource_idx);
2020 return dst_texture;
2023 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2024 unsigned int bpp, UINT pitch, DWORD color)
2026 BYTE *first;
2027 unsigned int x, y;
2029 /* Do first row */
2031 #define COLORFILL_ROW(type) \
2032 do { \
2033 type *d = (type *)buf; \
2034 for (x = 0; x < width; ++x) \
2035 d[x] = (type)color; \
2036 } while(0)
2038 switch (bpp)
2040 case 1:
2041 COLORFILL_ROW(BYTE);
2042 break;
2044 case 2:
2045 COLORFILL_ROW(WORD);
2046 break;
2048 case 3:
2050 BYTE *d = buf;
2051 for (x = 0; x < width; ++x, d += 3)
2053 d[0] = (color ) & 0xff;
2054 d[1] = (color >> 8) & 0xff;
2055 d[2] = (color >> 16) & 0xff;
2057 break;
2059 case 4:
2060 COLORFILL_ROW(DWORD);
2061 break;
2063 default:
2064 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2065 return WINED3DERR_NOTAVAILABLE;
2068 #undef COLORFILL_ROW
2070 /* Now copy first row. */
2071 first = buf;
2072 for (y = 1; y < height; ++y)
2074 buf += pitch;
2075 memcpy(buf, first, width * bpp);
2078 return WINED3D_OK;
2081 static void read_from_framebuffer(struct wined3d_surface *surface,
2082 struct wined3d_context *old_ctx, DWORD dst_location)
2084 struct wined3d_device *device = surface->resource.device;
2085 const struct wined3d_gl_info *gl_info;
2086 struct wined3d_context *context = old_ctx;
2087 struct wined3d_surface *restore_rt = NULL;
2088 unsigned int row_pitch, slice_pitch;
2089 BYTE *mem;
2090 BYTE *row, *top, *bottom;
2091 int i;
2092 BOOL srcIsUpsideDown;
2093 struct wined3d_bo_address data;
2095 surface_get_memory(surface, &data, dst_location);
2097 if (surface != old_ctx->current_rt)
2099 restore_rt = old_ctx->current_rt;
2100 context = context_acquire(device, surface);
2103 context_apply_blit_state(context, device);
2104 gl_info = context->gl_info;
2106 /* Select the correct read buffer, and give some debug output.
2107 * There is no need to keep track of the current read buffer or reset it, every part of the code
2108 * that reads sets the read buffer as desired.
2110 if (wined3d_resource_is_offscreen(&surface->container->resource))
2112 /* Mapping the primary render target which is not on a swapchain.
2113 * Read from the back buffer. */
2114 TRACE("Mapping offscreen render target.\n");
2115 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
2116 srcIsUpsideDown = TRUE;
2118 else
2120 /* Onscreen surfaces are always part of a swapchain */
2121 GLenum buffer = surface_get_gl_buffer(surface);
2122 TRACE("Mapping %#x buffer.\n", buffer);
2123 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
2124 checkGLcall("glReadBuffer");
2125 srcIsUpsideDown = FALSE;
2128 if (data.buffer_object)
2130 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
2131 checkGLcall("glBindBuffer");
2134 wined3d_texture_get_pitch(surface->container, surface->texture_level, &row_pitch, &slice_pitch);
2136 /* Setup pixel store pack state -- to glReadPixels into the correct place */
2137 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, row_pitch / surface->resource.format->byte_count);
2138 checkGLcall("glPixelStorei");
2140 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
2141 surface->resource.width, surface->resource.height,
2142 surface->resource.format->glFormat,
2143 surface->resource.format->glType, data.addr);
2144 checkGLcall("glReadPixels");
2146 /* Reset previous pixel store pack state */
2147 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
2148 checkGLcall("glPixelStorei");
2150 if (!srcIsUpsideDown)
2152 /* glReadPixels returns the image upside down, and there is no way to
2153 * prevent this. Flip the lines in software. */
2155 if (!(row = HeapAlloc(GetProcessHeap(), 0, row_pitch)))
2156 goto error;
2158 if (data.buffer_object)
2160 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
2161 checkGLcall("glMapBuffer");
2163 else
2164 mem = data.addr;
2166 top = mem;
2167 bottom = mem + row_pitch * (surface->resource.height - 1);
2168 for (i = 0; i < surface->resource.height / 2; i++)
2170 memcpy(row, top, row_pitch);
2171 memcpy(top, bottom, row_pitch);
2172 memcpy(bottom, row, row_pitch);
2173 top += row_pitch;
2174 bottom -= row_pitch;
2176 HeapFree(GetProcessHeap(), 0, row);
2178 if (data.buffer_object)
2179 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
2182 error:
2183 if (data.buffer_object)
2185 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
2186 checkGLcall("glBindBuffer");
2189 if (restore_rt)
2190 context_restore(context, restore_rt);
2193 /* Read the framebuffer contents into a texture. Note that this function
2194 * doesn't do any kind of flipping. Using this on an onscreen surface will
2195 * result in a flipped D3D texture.
2197 * Context activation is done by the caller. This function may temporarily
2198 * switch to a different context and restore the original one before return. */
2199 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb, struct wined3d_context *old_ctx)
2201 struct wined3d_device *device = surface->resource.device;
2202 const struct wined3d_gl_info *gl_info;
2203 struct wined3d_context *context = old_ctx;
2204 struct wined3d_surface *restore_rt = NULL;
2206 if (old_ctx->current_rt != surface)
2208 restore_rt = old_ctx->current_rt;
2209 context = context_acquire(device, surface);
2212 gl_info = context->gl_info;
2213 device_invalidate_state(device, STATE_FRAMEBUFFER);
2215 wined3d_texture_prepare_texture(surface->container, context, srgb);
2216 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
2218 TRACE("Reading back offscreen render target %p.\n", surface);
2220 if (wined3d_resource_is_offscreen(&surface->container->resource))
2221 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
2222 else
2223 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
2224 checkGLcall("glReadBuffer");
2226 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
2227 0, 0, 0, 0, surface->resource.width, surface->resource.height);
2228 checkGLcall("glCopyTexSubImage2D");
2230 if (restore_rt)
2231 context_restore(context, restore_rt);
2234 static void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
2236 if (multisample)
2238 DWORD samples;
2240 if (surface->rb_multisample)
2241 return;
2243 /* TODO: Nvidia exposes their Coverage Sample Anti-Aliasing (CSAA) feature
2244 * through type == MULTISAMPLE_XX and quality != 0. This could be mapped
2245 * to GL_NV_framebuffer_multisample_coverage.
2247 * AMD has a similar feature called Enhanced Quality Anti-Aliasing (EQAA),
2248 * but it does not have an equivalent OpenGL extension. */
2250 /* We advertise as many WINED3D_MULTISAMPLE_NON_MASKABLE quality levels
2251 * as the count of advertised multisample types for the surface format. */
2252 if (surface->resource.multisample_type == WINED3D_MULTISAMPLE_NON_MASKABLE)
2254 const struct wined3d_format *format = surface->resource.format;
2255 unsigned int i, count = 0;
2257 for (i = 0; i < sizeof(format->multisample_types) * 8; ++i)
2259 if (format->multisample_types & 1u << i)
2261 if (surface->resource.multisample_quality == count++)
2262 break;
2265 samples = i + 1;
2267 else
2269 samples = surface->resource.multisample_type;
2272 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
2273 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
2274 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
2275 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
2276 checkGLcall("glRenderbufferStorageMultisample()");
2277 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
2279 else
2281 if (surface->rb_resolved)
2282 return;
2284 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
2285 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
2286 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
2287 surface->pow2Width, surface->pow2Height);
2288 checkGLcall("glRenderbufferStorage()");
2289 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
2293 /* Does a direct frame buffer -> texture copy. Stretching is done with single
2294 * pixel copy calls. */
2295 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
2296 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
2298 struct wined3d_device *device = dst_surface->resource.device;
2299 const struct wined3d_gl_info *gl_info;
2300 float xrel, yrel;
2301 struct wined3d_context *context;
2302 BOOL upsidedown = FALSE;
2303 RECT dst_rect = *dst_rect_in;
2305 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2306 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2308 if(dst_rect.top > dst_rect.bottom) {
2309 UINT tmp = dst_rect.bottom;
2310 dst_rect.bottom = dst_rect.top;
2311 dst_rect.top = tmp;
2312 upsidedown = TRUE;
2315 context = context_acquire(device, src_surface);
2316 gl_info = context->gl_info;
2317 context_apply_blit_state(context, device);
2318 wined3d_texture_load(dst_surface->container, context, FALSE);
2320 /* Bind the target texture */
2321 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
2322 if (wined3d_resource_is_offscreen(&src_surface->container->resource))
2324 TRACE("Reading from an offscreen target\n");
2325 upsidedown = !upsidedown;
2326 gl_info->gl_ops.gl.p_glReadBuffer(context_get_offscreen_gl_buffer(context));
2328 else
2330 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
2332 checkGLcall("glReadBuffer");
2334 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
2335 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
2337 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2339 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2341 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
2342 ERR("Texture filtering not supported in direct blit.\n");
2344 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
2345 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
2347 ERR("Texture filtering not supported in direct blit\n");
2350 if (upsidedown
2351 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2352 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
2354 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
2355 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2356 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
2357 src_rect->left, src_surface->resource.height - src_rect->bottom,
2358 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2360 else
2362 LONG row;
2363 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
2364 /* I have to process this row by row to swap the image,
2365 * otherwise it would be upside down, so stretching in y direction
2366 * doesn't cost extra time
2368 * However, stretching in x direction can be avoided if not necessary
2370 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
2371 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2373 /* Well, that stuff works, but it's very slow.
2374 * find a better way instead
2376 LONG col;
2378 for (col = dst_rect.left; col < dst_rect.right; ++col)
2380 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2381 dst_rect.left + col /* x offset */, row /* y offset */,
2382 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
2385 else
2387 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2388 dst_rect.left /* x offset */, row /* y offset */,
2389 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
2393 checkGLcall("glCopyTexSubImage2D");
2395 context_release(context);
2397 /* The texture is now most up to date - If the surface is a render target
2398 * and has a drawable, this path is never entered. */
2399 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
2400 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
2403 /* Uses the hardware to stretch and flip the image */
2404 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
2405 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
2407 struct wined3d_device *device = dst_surface->resource.device;
2408 GLuint src, backup = 0;
2409 float left, right, top, bottom; /* Texture coordinates */
2410 UINT fbwidth = src_surface->resource.width;
2411 UINT fbheight = src_surface->resource.height;
2412 const struct wined3d_gl_info *gl_info;
2413 struct wined3d_context *context;
2414 GLenum drawBuffer = GL_BACK;
2415 GLenum offscreen_buffer;
2416 GLenum texture_target;
2417 BOOL noBackBufferBackup;
2418 BOOL src_offscreen;
2419 BOOL upsidedown = FALSE;
2420 RECT dst_rect = *dst_rect_in;
2422 TRACE("Using hwstretch blit\n");
2423 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2424 context = context_acquire(device, src_surface);
2425 gl_info = context->gl_info;
2426 context_apply_blit_state(context, device);
2427 wined3d_texture_load(dst_surface->container, context, FALSE);
2429 offscreen_buffer = context_get_offscreen_gl_buffer(context);
2431 src_offscreen = wined3d_resource_is_offscreen(&src_surface->container->resource);
2432 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
2433 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
2435 /* Get it a description */
2436 wined3d_texture_load(src_surface->container, context, FALSE);
2439 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2440 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2442 if (context->aux_buffers >= 2)
2444 /* Got more than one aux buffer? Use the 2nd aux buffer */
2445 drawBuffer = GL_AUX1;
2447 else if ((!src_offscreen || offscreen_buffer == GL_BACK) && context->aux_buffers >= 1)
2449 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
2450 drawBuffer = GL_AUX0;
2453 if (noBackBufferBackup)
2455 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
2456 checkGLcall("glGenTextures");
2457 context_bind_texture(context, GL_TEXTURE_2D, backup);
2458 texture_target = GL_TEXTURE_2D;
2460 else
2462 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
2463 * we are reading from the back buffer, the backup can be used as source texture
2465 texture_target = src_surface->texture_target;
2466 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
2467 gl_info->gl_ops.gl.p_glEnable(texture_target);
2468 checkGLcall("glEnable(texture_target)");
2470 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
2471 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
2474 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2475 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2477 if(dst_rect.top > dst_rect.bottom) {
2478 UINT tmp = dst_rect.bottom;
2479 dst_rect.bottom = dst_rect.top;
2480 dst_rect.top = tmp;
2481 upsidedown = TRUE;
2484 if (src_offscreen)
2486 TRACE("Reading from an offscreen target\n");
2487 upsidedown = !upsidedown;
2488 gl_info->gl_ops.gl.p_glReadBuffer(offscreen_buffer);
2490 else
2492 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
2495 /* TODO: Only back up the part that will be overwritten */
2496 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
2498 checkGLcall("glCopyTexSubImage2D");
2500 /* No issue with overriding these - the sampler is dirty due to blit usage */
2501 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
2502 checkGLcall("glTexParameteri");
2503 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
2504 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
2505 checkGLcall("glTexParameteri");
2507 if (!src_surface->container->swapchain
2508 || src_surface->container == src_surface->container->swapchain->back_buffers[0])
2510 src = backup ? backup : src_surface->container->texture_rgb.name;
2512 else
2514 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
2515 checkGLcall("glReadBuffer(GL_FRONT)");
2517 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
2518 checkGLcall("glGenTextures(1, &src)");
2519 context_bind_texture(context, GL_TEXTURE_2D, src);
2521 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
2522 * out for power of 2 sizes
2524 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
2525 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
2526 checkGLcall("glTexImage2D");
2527 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
2529 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2530 checkGLcall("glTexParameteri");
2531 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2532 checkGLcall("glTexParameteri");
2534 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
2535 checkGLcall("glReadBuffer(GL_BACK)");
2537 if (texture_target != GL_TEXTURE_2D)
2539 gl_info->gl_ops.gl.p_glDisable(texture_target);
2540 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
2541 texture_target = GL_TEXTURE_2D;
2544 checkGLcall("glEnd and previous");
2546 left = src_rect->left;
2547 right = src_rect->right;
2549 if (!upsidedown)
2551 top = src_surface->resource.height - src_rect->top;
2552 bottom = src_surface->resource.height - src_rect->bottom;
2554 else
2556 top = src_surface->resource.height - src_rect->bottom;
2557 bottom = src_surface->resource.height - src_rect->top;
2560 if (src_surface->container->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
2562 left /= src_surface->pow2Width;
2563 right /= src_surface->pow2Width;
2564 top /= src_surface->pow2Height;
2565 bottom /= src_surface->pow2Height;
2568 /* draw the source texture stretched and upside down. The correct surface is bound already */
2569 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2570 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2572 context_set_draw_buffer(context, drawBuffer);
2573 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
2575 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
2576 /* bottom left */
2577 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
2578 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
2580 /* top left */
2581 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
2582 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
2584 /* top right */
2585 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
2586 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2588 /* bottom right */
2589 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
2590 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
2591 gl_info->gl_ops.gl.p_glEnd();
2592 checkGLcall("glEnd and previous");
2594 if (texture_target != dst_surface->texture_target)
2596 gl_info->gl_ops.gl.p_glDisable(texture_target);
2597 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
2598 texture_target = dst_surface->texture_target;
2601 /* Now read the stretched and upside down image into the destination texture */
2602 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
2603 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
2605 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
2606 0, 0, /* We blitted the image to the origin */
2607 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2608 checkGLcall("glCopyTexSubImage2D");
2610 if (drawBuffer == GL_BACK)
2612 /* Write the back buffer backup back. */
2613 if (backup)
2615 if (texture_target != GL_TEXTURE_2D)
2617 gl_info->gl_ops.gl.p_glDisable(texture_target);
2618 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
2619 texture_target = GL_TEXTURE_2D;
2621 context_bind_texture(context, GL_TEXTURE_2D, backup);
2623 else
2625 if (texture_target != src_surface->texture_target)
2627 gl_info->gl_ops.gl.p_glDisable(texture_target);
2628 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
2629 texture_target = src_surface->texture_target;
2631 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
2634 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
2635 /* top left */
2636 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
2637 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
2639 /* bottom left */
2640 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
2641 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
2643 /* bottom right */
2644 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
2645 (float)fbheight / (float)src_surface->pow2Height);
2646 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
2648 /* top right */
2649 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
2650 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
2651 gl_info->gl_ops.gl.p_glEnd();
2653 gl_info->gl_ops.gl.p_glDisable(texture_target);
2654 checkGLcall("glDisable(texture_target)");
2656 /* Cleanup */
2657 if (src != src_surface->container->texture_rgb.name && src != backup)
2659 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
2660 checkGLcall("glDeleteTextures(1, &src)");
2662 if (backup)
2664 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
2665 checkGLcall("glDeleteTextures(1, &backup)");
2668 if (wined3d_settings.strict_draw_ordering)
2669 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2671 context_release(context);
2673 /* The texture is now most up to date - If the surface is a render target
2674 * and has a drawable, this path is never entered. */
2675 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
2676 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
2679 /* Front buffer coordinates are always full screen coordinates, but our GL
2680 * drawable is limited to the window's client area. The sysmem and texture
2681 * copies do have the full screen size. Note that GL has a bottom-left
2682 * origin, while D3D has a top-left origin. */
2683 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
2685 UINT drawable_height;
2687 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
2689 POINT offset = {0, 0};
2690 RECT windowsize;
2692 ScreenToClient(window, &offset);
2693 OffsetRect(rect, offset.x, offset.y);
2695 GetClientRect(window, &windowsize);
2696 drawable_height = windowsize.bottom - windowsize.top;
2698 else
2700 drawable_height = surface->resource.height;
2703 rect->top = drawable_height - rect->top;
2704 rect->bottom = drawable_height - rect->bottom;
2707 /* Context activation is done by the caller. */
2708 static void surface_blt_to_drawable(const struct wined3d_device *device,
2709 struct wined3d_context *old_ctx,
2710 enum wined3d_texture_filter_type filter, BOOL alpha_test,
2711 struct wined3d_surface *src_surface, const RECT *src_rect_in,
2712 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
2714 const struct wined3d_gl_info *gl_info;
2715 struct wined3d_context *context = old_ctx;
2716 struct wined3d_surface *restore_rt = NULL;
2717 RECT src_rect, dst_rect;
2719 src_rect = *src_rect_in;
2720 dst_rect = *dst_rect_in;
2723 if (old_ctx->current_rt != dst_surface)
2725 restore_rt = old_ctx->current_rt;
2726 context = context_acquire(device, dst_surface);
2729 gl_info = context->gl_info;
2731 /* Make sure the surface is up-to-date. This should probably use
2732 * surface_load_location() and worry about the destination surface too,
2733 * unless we're overwriting it completely. */
2734 wined3d_texture_load(src_surface->container, context, FALSE);
2736 /* Activate the destination context, set it up for blitting */
2737 context_apply_blit_state(context, device);
2739 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
2740 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
2742 device->blitter->set_shader(device->blit_priv, context, src_surface, NULL);
2744 if (alpha_test)
2746 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
2747 checkGLcall("glEnable(GL_ALPHA_TEST)");
2749 /* For P8 surfaces, the alpha component contains the palette index.
2750 * Which means that the colorkey is one of the palette entries. In
2751 * other cases pixels that should be masked away have alpha set to 0. */
2752 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
2753 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
2754 (float)src_surface->container->async.src_blt_color_key.color_space_low_value / 255.0f);
2755 else
2756 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
2757 checkGLcall("glAlphaFunc");
2759 else
2761 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2762 checkGLcall("glDisable(GL_ALPHA_TEST)");
2765 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
2767 if (alpha_test)
2769 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2770 checkGLcall("glDisable(GL_ALPHA_TEST)");
2773 /* Leave the opengl state valid for blitting */
2774 device->blitter->unset_shader(context->gl_info);
2776 if (wined3d_settings.strict_draw_ordering
2777 || (dst_surface->container->swapchain
2778 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
2779 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
2781 if (restore_rt)
2782 context_restore(context, restore_rt);
2785 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
2787 struct wined3d_resource *resource = &s->container->resource;
2788 struct wined3d_device *device = resource->device;
2789 struct wined3d_rendertarget_view_desc view_desc;
2790 struct wined3d_rendertarget_view *view;
2791 const struct blit_shader *blitter;
2792 HRESULT hr;
2794 if (!(blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info,
2795 WINED3D_BLIT_OP_COLOR_FILL, NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format)))
2797 FIXME("No blitter is capable of performing the requested color fill operation.\n");
2798 return WINED3DERR_INVALIDCALL;
2801 view_desc.format_id = resource->format->id;
2802 view_desc.u.texture.level_idx = s->texture_level;
2803 view_desc.u.texture.layer_idx = s->texture_layer;
2804 view_desc.u.texture.layer_count = 1;
2805 if (FAILED(hr = wined3d_rendertarget_view_create(&view_desc,
2806 resource, NULL, &wined3d_null_parent_ops, &view)))
2808 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
2809 return hr;
2812 hr = blitter->color_fill(device, view, rect, color);
2813 wined3d_rendertarget_view_decref(view);
2815 return hr;
2818 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
2819 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
2820 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
2822 struct wined3d_device *device = dst_surface->resource.device;
2823 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
2824 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
2826 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
2827 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
2828 flags, fx, debug_d3dtexturefiltertype(filter));
2830 /* Get the swapchain. One of the surfaces has to be a primary surface */
2831 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
2833 WARN("Destination is in sysmem, rejecting gl blt\n");
2834 return WINED3DERR_INVALIDCALL;
2837 dst_swapchain = dst_surface->container->swapchain;
2839 if (src_surface)
2841 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
2843 WARN("Src is in sysmem, rejecting gl blt\n");
2844 return WINED3DERR_INVALIDCALL;
2847 src_swapchain = src_surface->container->swapchain;
2849 else
2851 src_swapchain = NULL;
2854 /* Early sort out of cases where no render target is used */
2855 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
2857 TRACE("No surface is render target, not using hardware blit.\n");
2858 return WINED3DERR_INVALIDCALL;
2861 /* No destination color keying supported */
2862 if (flags & (WINED3D_BLT_DST_CKEY | WINED3D_BLT_DST_CKEY_OVERRIDE))
2864 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
2865 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
2866 return WINED3DERR_INVALIDCALL;
2869 if (dst_swapchain && dst_swapchain == src_swapchain)
2871 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
2872 return WINED3DERR_INVALIDCALL;
2875 if (dst_swapchain && src_swapchain)
2877 FIXME("Implement hardware blit between two different swapchains\n");
2878 return WINED3DERR_INVALIDCALL;
2881 if (dst_swapchain)
2883 /* Handled with regular texture -> swapchain blit */
2884 if (src_surface == rt)
2885 TRACE("Blit from active render target to a swapchain\n");
2887 else if (src_swapchain && dst_surface == rt)
2889 FIXME("Implement blit from a swapchain to the active render target\n");
2890 return WINED3DERR_INVALIDCALL;
2893 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
2895 /* Blit from render target to texture */
2896 BOOL stretchx;
2898 /* P8 read back is not implemented */
2899 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
2900 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
2902 TRACE("P8 read back not supported by frame buffer to texture blit\n");
2903 return WINED3DERR_INVALIDCALL;
2906 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_SRC_CKEY_OVERRIDE))
2908 TRACE("Color keying not supported by frame buffer to texture blit\n");
2909 return WINED3DERR_INVALIDCALL;
2910 /* Destination color key is checked above */
2913 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
2914 stretchx = TRUE;
2915 else
2916 stretchx = FALSE;
2918 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
2919 * flip the image nor scale it.
2921 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
2922 * -> If the app wants an image width an unscaled width, copy it line per line
2923 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
2924 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
2925 * back buffer. This is slower than reading line per line, thus not used for flipping
2926 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
2927 * pixel by pixel. */
2928 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
2929 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
2931 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
2932 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
2934 else
2936 TRACE("Using hardware stretching to flip / stretch the texture.\n");
2937 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
2940 surface_evict_sysmem(dst_surface);
2942 return WINED3D_OK;
2945 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
2946 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
2947 return WINED3DERR_INVALIDCALL;
2950 /* Context activation is done by the caller. */
2951 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
2952 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
2954 struct wined3d_device *device = surface->resource.device;
2955 const struct wined3d_gl_info *gl_info = context->gl_info;
2956 GLint compare_mode = GL_NONE;
2957 struct blt_info info;
2958 GLint old_binding = 0;
2959 RECT rect;
2961 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
2963 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
2964 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
2965 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
2966 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
2967 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
2968 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
2969 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
2970 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
2971 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
2972 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
2973 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
2975 SetRect(&rect, 0, h, w, 0);
2976 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
2977 context_active_texture(context, context->gl_info, 0);
2978 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
2979 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
2980 if (gl_info->supported[ARB_SHADOW])
2982 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
2983 if (compare_mode != GL_NONE)
2984 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
2987 device->shader_backend->shader_select_depth_blt(device->shader_priv,
2988 gl_info, info.tex_type, &surface->ds_current_size);
2990 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
2991 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
2992 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
2993 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
2994 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
2995 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
2996 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
2997 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
2998 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
2999 gl_info->gl_ops.gl.p_glEnd();
3001 if (compare_mode != GL_NONE)
3002 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
3003 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
3005 gl_info->gl_ops.gl.p_glPopAttrib();
3007 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
3010 void surface_modify_ds_location(struct wined3d_surface *surface,
3011 DWORD location, UINT w, UINT h)
3013 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
3015 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
3016 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
3017 wined3d_texture_set_dirty(surface->container);
3019 surface->ds_current_size.cx = w;
3020 surface->ds_current_size.cy = h;
3021 surface->locations = location;
3024 /* Context activation is done by the caller. */
3025 static void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3027 const struct wined3d_gl_info *gl_info = context->gl_info;
3028 struct wined3d_device *device = surface->resource.device;
3029 GLsizei w, h;
3031 TRACE("surface %p, context %p, new location %#x.\n", surface, context, location);
3033 /* TODO: Make this work for modes other than FBO */
3034 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
3036 if (!(surface->locations & location))
3038 w = surface->ds_current_size.cx;
3039 h = surface->ds_current_size.cy;
3040 surface->ds_current_size.cx = 0;
3041 surface->ds_current_size.cy = 0;
3043 else
3045 w = surface->resource.width;
3046 h = surface->resource.height;
3049 if (surface->current_renderbuffer)
3051 FIXME("Not supported with fixed up depth stencil.\n");
3052 return;
3055 wined3d_surface_prepare(surface, context, location);
3057 if (location == WINED3D_LOCATION_TEXTURE_RGB)
3059 GLint old_binding = 0;
3060 GLenum bind_target;
3062 /* The render target is allowed to be smaller than the depth/stencil
3063 * buffer, so the onscreen depth/stencil buffer is potentially smaller
3064 * than the offscreen surface. Don't overwrite the offscreen surface
3065 * with undefined data. */
3066 w = min(w, context->swapchain->desc.backbuffer_width);
3067 h = min(h, context->swapchain->desc.backbuffer_height);
3069 TRACE("Copying onscreen depth buffer to depth texture.\n");
3071 if (!device->depth_blt_texture)
3072 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
3074 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
3075 * directly on the FBO texture. That's because we need to flip. */
3076 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3077 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3078 NULL, WINED3D_LOCATION_DRAWABLE);
3079 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
3081 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
3082 bind_target = GL_TEXTURE_RECTANGLE_ARB;
3084 else
3086 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
3087 bind_target = GL_TEXTURE_2D;
3089 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
3090 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
3091 * internal format, because the internal format might include stencil
3092 * data. In principle we should copy stencil data as well, but unless
3093 * the driver supports stencil export it's hard to do, and doesn't
3094 * seem to be needed in practice. If the hardware doesn't support
3095 * writing stencil data, the glCopyTexImage2D() call might trigger
3096 * software fallbacks. */
3097 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
3098 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3099 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3100 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3101 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3102 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
3103 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
3105 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3106 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
3107 context_set_draw_buffer(context, GL_NONE);
3109 /* Do the actual blit */
3110 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
3111 checkGLcall("depth_blt");
3113 context_invalidate_state(context, STATE_FRAMEBUFFER);
3115 if (wined3d_settings.strict_draw_ordering)
3116 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3118 else if (location == WINED3D_LOCATION_DRAWABLE)
3120 TRACE("Copying depth texture to onscreen depth buffer.\n");
3122 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3123 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3124 NULL, WINED3D_LOCATION_DRAWABLE);
3125 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
3126 0, surface->pow2Height - h, w, h, surface->texture_target);
3127 checkGLcall("depth_blt");
3129 context_invalidate_state(context, STATE_FRAMEBUFFER);
3131 if (wined3d_settings.strict_draw_ordering)
3132 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3134 else
3136 ERR("Invalid location (%#x) specified.\n", location);
3140 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
3142 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3144 surface->locations |= location;
3147 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
3149 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3151 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3152 wined3d_texture_set_dirty(surface->container);
3153 surface->locations &= ~location;
3155 if (!surface->locations)
3156 ERR("Surface %p does not have any up to date location.\n", surface);
3159 static DWORD resource_access_from_location(DWORD location)
3161 switch (location)
3163 case WINED3D_LOCATION_SYSMEM:
3164 case WINED3D_LOCATION_USER_MEMORY:
3165 case WINED3D_LOCATION_DIB:
3166 case WINED3D_LOCATION_BUFFER:
3167 return WINED3D_RESOURCE_ACCESS_CPU;
3169 case WINED3D_LOCATION_DRAWABLE:
3170 case WINED3D_LOCATION_TEXTURE_SRGB:
3171 case WINED3D_LOCATION_TEXTURE_RGB:
3172 case WINED3D_LOCATION_RB_MULTISAMPLE:
3173 case WINED3D_LOCATION_RB_RESOLVED:
3174 return WINED3D_RESOURCE_ACCESS_GPU;
3176 default:
3177 FIXME("Unhandled location %#x.\n", location);
3178 return 0;
3182 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
3184 struct wined3d_device *device = surface->resource.device;
3185 struct wined3d_context *context;
3186 const struct wined3d_gl_info *gl_info;
3187 struct wined3d_bo_address dst, src;
3188 UINT size = surface->resource.size;
3190 surface_get_memory(surface, &dst, location);
3191 surface_get_memory(surface, &src, surface->locations);
3193 if (dst.buffer_object)
3195 context = context_acquire(device, NULL);
3196 gl_info = context->gl_info;
3197 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, dst.buffer_object));
3198 GL_EXTCALL(glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, src.addr));
3199 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
3200 checkGLcall("Upload PBO");
3201 context_release(context);
3202 return;
3204 if (src.buffer_object)
3206 context = context_acquire(device, NULL);
3207 gl_info = context->gl_info;
3208 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, src.buffer_object));
3209 GL_EXTCALL(glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, size, dst.addr));
3210 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
3211 checkGLcall("Download PBO");
3212 context_release(context);
3213 return;
3215 memcpy(dst.addr, src.addr, size);
3218 /* Context activation is done by the caller. */
3219 static void surface_load_sysmem(struct wined3d_surface *surface,
3220 struct wined3d_context *context, DWORD dst_location)
3222 const struct wined3d_gl_info *gl_info = context->gl_info;
3224 wined3d_surface_prepare(surface, context, dst_location);
3226 if (surface->locations & surface_simple_locations)
3228 surface_copy_simple_location(surface, dst_location);
3229 return;
3232 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
3233 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
3235 /* Download the surface to system memory. */
3236 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3238 struct wined3d_texture *texture = surface->container;
3240 wined3d_texture_bind_and_dirtify(texture, context,
3241 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
3242 surface_download_data(surface, gl_info, dst_location);
3243 ++texture->download_count;
3245 return;
3248 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
3250 read_from_framebuffer(surface, context, dst_location);
3251 return;
3254 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
3255 surface, wined3d_debug_location(surface->locations));
3258 /* Context activation is done by the caller. */
3259 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
3260 struct wined3d_context *context)
3262 RECT r;
3264 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
3265 && wined3d_resource_is_offscreen(&surface->container->resource))
3267 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
3268 return WINED3DERR_INVALIDCALL;
3271 surface_get_rect(surface, NULL, &r);
3272 surface_load_location(surface, context, WINED3D_LOCATION_TEXTURE_RGB);
3273 surface_blt_to_drawable(surface->resource.device, context,
3274 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
3276 return WINED3D_OK;
3279 static HRESULT surface_load_texture(struct wined3d_surface *surface,
3280 struct wined3d_context *context, BOOL srgb)
3282 unsigned int width, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch;
3283 const RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
3284 const struct wined3d_gl_info *gl_info = context->gl_info;
3285 struct wined3d_device *device = surface->resource.device;
3286 const struct wined3d_color_key_conversion *conversion;
3287 struct wined3d_texture *texture = surface->container;
3288 struct wined3d_bo_address data;
3289 struct wined3d_format format;
3290 POINT dst_point = {0, 0};
3291 BYTE *mem = NULL;
3293 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
3294 && wined3d_resource_is_offscreen(&texture->resource)
3295 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
3297 surface_load_fb_texture(surface, srgb, context);
3299 return WINED3D_OK;
3302 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
3303 && (surface->container->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
3304 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3305 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3306 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3308 if (srgb)
3309 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
3310 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
3311 else
3312 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
3313 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
3315 return WINED3D_OK;
3318 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
3319 && (!srgb || (surface->container->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
3320 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3321 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3322 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3324 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
3325 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
3326 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
3327 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
3329 surface_blt_fbo(device, context, WINED3D_TEXF_POINT, surface, src_location,
3330 &rect, surface, dst_location, &rect);
3332 return WINED3D_OK;
3335 /* Upload from system memory */
3337 if (srgb)
3339 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
3340 == WINED3D_LOCATION_TEXTURE_RGB)
3342 /* Performance warning... */
3343 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
3344 surface_load_location(surface, context, surface->resource.map_binding);
3347 else
3349 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
3350 == WINED3D_LOCATION_TEXTURE_SRGB)
3352 /* Performance warning... */
3353 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
3354 surface_load_location(surface, context, surface->resource.map_binding);
3358 if (!(surface->locations & surface_simple_locations))
3360 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
3361 /* Lets hope we get it from somewhere... */
3362 surface_load_location(surface, context, WINED3D_LOCATION_SYSMEM);
3365 wined3d_texture_prepare_texture(texture, context, srgb);
3366 wined3d_texture_bind_and_dirtify(texture, context, srgb);
3367 wined3d_texture_get_pitch(texture, surface->texture_level, &src_row_pitch, &src_slice_pitch);
3369 width = surface->resource.width;
3371 format = *texture->resource.format;
3372 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
3373 format = *wined3d_get_format(gl_info, conversion->dst_format);
3375 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
3376 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
3377 * getting called. */
3378 if ((format.convert || conversion)
3379 && texture->sub_resources[surface_get_sub_resource_idx(surface)].buffer_object)
3381 TRACE("Removing the pbo attached to surface %p.\n", surface);
3383 if (surface->flags & SFLAG_DIBSECTION)
3384 surface->resource.map_binding = WINED3D_LOCATION_DIB;
3385 else
3386 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
3388 surface_load_location(surface, context, surface->resource.map_binding);
3389 wined3d_texture_remove_buffer_object(texture, surface_get_sub_resource_idx(surface), gl_info);
3392 surface_get_memory(surface, &data, surface->locations);
3393 if (format.convert)
3395 /* This code is entered for texture formats which need a fixup. */
3396 UINT height = surface->resource.height;
3398 format.byte_count = format.conv_byte_count;
3399 wined3d_format_calculate_pitch(&format, 1, width, height, &dst_row_pitch, &dst_slice_pitch);
3401 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
3403 ERR("Out of memory (%u).\n", dst_slice_pitch);
3404 context_release(context);
3405 return E_OUTOFMEMORY;
3407 format.convert(data.addr, mem, src_row_pitch, src_slice_pitch,
3408 dst_row_pitch, dst_slice_pitch, width, height, 1);
3409 src_row_pitch = dst_row_pitch;
3410 data.addr = mem;
3412 else if (conversion)
3414 /* This code is only entered for color keying fixups */
3415 struct wined3d_palette *palette = NULL;
3416 UINT height = surface->resource.height;
3418 wined3d_format_calculate_pitch(&format, device->surface_alignment,
3419 width, height, &dst_row_pitch, &dst_slice_pitch);
3421 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_slice_pitch)))
3423 ERR("Out of memory (%u).\n", dst_slice_pitch);
3424 context_release(context);
3425 return E_OUTOFMEMORY;
3427 if (texture->swapchain && texture->swapchain->palette)
3428 palette = texture->swapchain->palette;
3429 conversion->convert(data.addr, src_row_pitch, mem, dst_row_pitch,
3430 width, height, palette, &texture->async.gl_color_key);
3431 src_row_pitch = dst_row_pitch;
3432 data.addr = mem;
3435 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
3436 src_row_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
3438 HeapFree(GetProcessHeap(), 0, mem);
3440 return WINED3D_OK;
3443 /* Context activation is done by the caller. */
3444 static void surface_load_renderbuffer(struct wined3d_surface *surface, struct wined3d_context *context,
3445 DWORD dst_location)
3447 const RECT rect = {0, 0, surface->resource.width, surface->resource.height};
3448 DWORD src_location;
3450 if (surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE)
3451 src_location = WINED3D_LOCATION_RB_MULTISAMPLE;
3452 else if (surface->locations & WINED3D_LOCATION_RB_RESOLVED)
3453 src_location = WINED3D_LOCATION_RB_RESOLVED;
3454 else if (surface->locations & WINED3D_LOCATION_TEXTURE_SRGB)
3455 src_location = WINED3D_LOCATION_TEXTURE_SRGB;
3456 else /* surface_blt_fbo will load the source location if necessary. */
3457 src_location = WINED3D_LOCATION_TEXTURE_RGB;
3459 surface_blt_fbo(surface->resource.device, context, WINED3D_TEXF_POINT,
3460 surface, src_location, &rect, surface, dst_location, &rect);
3463 /* Context activation is done by the caller. Context may be NULL in ddraw-only mode. */
3464 HRESULT surface_load_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3466 HRESULT hr;
3468 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3470 if (surface->locations & location && (!(surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
3471 || (surface->ds_current_size.cx == surface->resource.width
3472 && surface->ds_current_size.cy == surface->resource.height)))
3474 TRACE("Location (%#x) is already up to date.\n", location);
3475 return WINED3D_OK;
3478 if (WARN_ON(d3d_surface))
3480 DWORD required_access = resource_access_from_location(location);
3481 if ((surface->resource.access_flags & required_access) != required_access)
3482 WARN("Operation requires %#x access, but surface only has %#x.\n",
3483 required_access, surface->resource.access_flags);
3486 if (surface->locations & WINED3D_LOCATION_DISCARDED)
3488 TRACE("Surface previously discarded, nothing to do.\n");
3489 wined3d_surface_prepare(surface, context, location);
3490 surface_validate_location(surface, location);
3491 surface_invalidate_location(surface, WINED3D_LOCATION_DISCARDED);
3492 goto done;
3495 if (!surface->locations)
3497 ERR("Surface %p does not have any up to date location.\n", surface);
3498 surface_validate_location(surface, WINED3D_LOCATION_DISCARDED);
3499 return surface_load_location(surface, context, location);
3502 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
3504 if ((location == WINED3D_LOCATION_TEXTURE_RGB && surface->locations & WINED3D_LOCATION_DRAWABLE)
3505 || (location == WINED3D_LOCATION_DRAWABLE && surface->locations & WINED3D_LOCATION_TEXTURE_RGB))
3507 surface_load_ds_location(surface, context, location);
3508 goto done;
3511 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
3512 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
3513 return WINED3DERR_INVALIDCALL;
3516 switch (location)
3518 case WINED3D_LOCATION_DIB:
3519 case WINED3D_LOCATION_USER_MEMORY:
3520 case WINED3D_LOCATION_SYSMEM:
3521 case WINED3D_LOCATION_BUFFER:
3522 surface_load_sysmem(surface, context, location);
3523 break;
3525 case WINED3D_LOCATION_DRAWABLE:
3526 if (FAILED(hr = surface_load_drawable(surface, context)))
3527 return hr;
3528 break;
3530 case WINED3D_LOCATION_RB_RESOLVED:
3531 case WINED3D_LOCATION_RB_MULTISAMPLE:
3532 surface_load_renderbuffer(surface, context, location);
3533 break;
3535 case WINED3D_LOCATION_TEXTURE_RGB:
3536 case WINED3D_LOCATION_TEXTURE_SRGB:
3537 if (FAILED(hr = surface_load_texture(surface, context,
3538 location == WINED3D_LOCATION_TEXTURE_SRGB)))
3539 return hr;
3540 break;
3542 default:
3543 ERR("Don't know how to handle location %#x.\n", location);
3544 break;
3547 done:
3548 surface_validate_location(surface, location);
3550 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
3552 surface->ds_current_size.cx = surface->resource.width;
3553 surface->ds_current_size.cy = surface->resource.height;
3556 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
3557 surface_evict_sysmem(surface);
3559 return WINED3D_OK;
3562 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
3563 /* Context activation is done by the caller. */
3564 static void ffp_blit_free(struct wined3d_device *device) { }
3566 /* Context activation is done by the caller. */
3567 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
3568 const struct wined3d_color_key *color_key)
3570 const struct wined3d_gl_info *gl_info = context->gl_info;
3572 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
3573 checkGLcall("glEnable(target)");
3575 return WINED3D_OK;
3578 /* Context activation is done by the caller. */
3579 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
3581 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
3582 checkGLcall("glDisable(GL_TEXTURE_2D)");
3583 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
3585 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
3586 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
3588 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
3590 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
3591 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
3595 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info,
3596 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
3597 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
3598 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
3600 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
3602 TRACE("Source or destination is in system memory.\n");
3603 return FALSE;
3606 switch (blit_op)
3608 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
3609 if (d3d_info->shader_color_key)
3611 TRACE("Color keying requires converted textures.\n");
3612 return FALSE;
3614 case WINED3D_BLIT_OP_COLOR_BLIT:
3615 case WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST:
3616 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
3618 TRACE("Checking support for fixup:\n");
3619 dump_color_fixup_desc(src_format->color_fixup);
3622 /* We only support identity conversions. */
3623 if (!is_identity_fixup(src_format->color_fixup)
3624 || !is_identity_fixup(dst_format->color_fixup))
3626 TRACE("Fixups are not supported.\n");
3627 return FALSE;
3630 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
3632 TRACE("Can only blit to render targets.\n");
3633 return FALSE;
3635 return TRUE;
3637 case WINED3D_BLIT_OP_COLOR_FILL:
3638 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
3640 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
3641 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
3642 return FALSE;
3644 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
3646 TRACE("Color fill not supported\n");
3647 return FALSE;
3650 /* FIXME: We should reject color fills on formats with fixups,
3651 * but this would break P8 color fills for example. */
3653 return TRUE;
3655 case WINED3D_BLIT_OP_DEPTH_FILL:
3656 return TRUE;
3658 default:
3659 TRACE("Unsupported blit_op=%d\n", blit_op);
3660 return FALSE;
3664 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
3665 const RECT *rect, const struct wined3d_color *color)
3667 const RECT draw_rect = {0, 0, view->width, view->height};
3668 struct wined3d_fb_state fb = {&view, NULL};
3670 device_clear_render_targets(device, 1, &fb, 1, rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
3672 return WINED3D_OK;
3675 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device,
3676 struct wined3d_rendertarget_view *view, const RECT *rect, float depth)
3678 const RECT draw_rect = {0, 0, view->width, view->height};
3679 struct wined3d_fb_state fb = {NULL, view};
3681 device_clear_render_targets(device, 0, &fb, 1, rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
3683 return WINED3D_OK;
3686 static void ffp_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
3687 struct wined3d_surface *src_surface, const RECT *src_rect,
3688 struct wined3d_surface *dst_surface, const RECT *dst_rect,
3689 const struct wined3d_color_key *color_key)
3691 struct wined3d_context *context;
3693 /* Blit from offscreen surface to render target */
3694 struct wined3d_color_key old_blt_key = src_surface->container->async.src_blt_color_key;
3695 DWORD old_color_key_flags = src_surface->container->async.color_key_flags;
3697 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
3699 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT, color_key);
3701 context = context_acquire(device, dst_surface);
3703 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
3704 glEnable(GL_ALPHA_TEST);
3706 surface_blt_to_drawable(device, context, filter,
3707 !!color_key, src_surface, src_rect, dst_surface, dst_rect);
3709 if (op == WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST)
3710 glDisable(GL_ALPHA_TEST);
3712 context_release(context);
3714 /* Restore the color key parameters */
3715 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT,
3716 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
3718 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
3719 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
3722 const struct blit_shader ffp_blit = {
3723 ffp_blit_alloc,
3724 ffp_blit_free,
3725 ffp_blit_set,
3726 ffp_blit_unset,
3727 ffp_blit_supported,
3728 ffp_blit_color_fill,
3729 ffp_blit_depth_fill,
3730 ffp_blit_blit_surface,
3733 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
3735 return WINED3D_OK;
3738 /* Context activation is done by the caller. */
3739 static void cpu_blit_free(struct wined3d_device *device)
3743 /* Context activation is done by the caller. */
3744 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
3745 const struct wined3d_color_key *color_key)
3747 return WINED3D_OK;
3750 /* Context activation is done by the caller. */
3751 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
3755 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info,
3756 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
3757 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
3758 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
3760 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
3762 return TRUE;
3765 return FALSE;
3768 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
3769 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
3770 const struct wined3d_format *format, DWORD flags, const struct wined3d_blt_fx *fx)
3772 UINT row_block_count;
3773 const BYTE *src_row;
3774 BYTE *dst_row;
3775 UINT x, y;
3777 src_row = src_data;
3778 dst_row = dst_data;
3780 row_block_count = (update_w + format->block_width - 1) / format->block_width;
3782 if (!flags)
3784 for (y = 0; y < update_h; y += format->block_height)
3786 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
3787 src_row += src_pitch;
3788 dst_row += dst_pitch;
3791 return WINED3D_OK;
3794 if (flags == WINED3D_BLT_FX && fx->fx == WINEDDBLTFX_MIRRORUPDOWN)
3796 src_row += (((update_h / format->block_height) - 1) * src_pitch);
3798 switch (format->id)
3800 case WINED3DFMT_DXT1:
3801 for (y = 0; y < update_h; y += format->block_height)
3803 struct block
3805 WORD color[2];
3806 BYTE control_row[4];
3809 const struct block *s = (const struct block *)src_row;
3810 struct block *d = (struct block *)dst_row;
3812 for (x = 0; x < row_block_count; ++x)
3814 d[x].color[0] = s[x].color[0];
3815 d[x].color[1] = s[x].color[1];
3816 d[x].control_row[0] = s[x].control_row[3];
3817 d[x].control_row[1] = s[x].control_row[2];
3818 d[x].control_row[2] = s[x].control_row[1];
3819 d[x].control_row[3] = s[x].control_row[0];
3821 src_row -= src_pitch;
3822 dst_row += dst_pitch;
3824 return WINED3D_OK;
3826 case WINED3DFMT_DXT2:
3827 case WINED3DFMT_DXT3:
3828 for (y = 0; y < update_h; y += format->block_height)
3830 struct block
3832 WORD alpha_row[4];
3833 WORD color[2];
3834 BYTE control_row[4];
3837 const struct block *s = (const struct block *)src_row;
3838 struct block *d = (struct block *)dst_row;
3840 for (x = 0; x < row_block_count; ++x)
3842 d[x].alpha_row[0] = s[x].alpha_row[3];
3843 d[x].alpha_row[1] = s[x].alpha_row[2];
3844 d[x].alpha_row[2] = s[x].alpha_row[1];
3845 d[x].alpha_row[3] = s[x].alpha_row[0];
3846 d[x].color[0] = s[x].color[0];
3847 d[x].color[1] = s[x].color[1];
3848 d[x].control_row[0] = s[x].control_row[3];
3849 d[x].control_row[1] = s[x].control_row[2];
3850 d[x].control_row[2] = s[x].control_row[1];
3851 d[x].control_row[3] = s[x].control_row[0];
3853 src_row -= src_pitch;
3854 dst_row += dst_pitch;
3856 return WINED3D_OK;
3858 default:
3859 FIXME("Compressed flip not implemented for format %s.\n",
3860 debug_d3dformat(format->id));
3861 return E_NOTIMPL;
3865 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
3866 debug_d3dformat(format->id), flags, flags & WINED3D_BLT_FX ? fx->fx : 0);
3868 return E_NOTIMPL;
3871 static HRESULT surface_cpu_blt(struct wined3d_texture *dst_texture, unsigned int dst_sub_resource_idx,
3872 const struct wined3d_box *dst_box, struct wined3d_texture *src_texture, unsigned int src_sub_resource_idx,
3873 const struct wined3d_box *src_box, DWORD flags, const struct wined3d_blt_fx *fx,
3874 enum wined3d_texture_filter_type filter)
3876 unsigned int bpp, src_height, src_width, dst_height, dst_width, row_byte_count;
3877 const struct wined3d_format *src_format, *dst_format;
3878 struct wined3d_texture *converted_texture = NULL;
3879 unsigned int src_fmt_flags, dst_fmt_flags;
3880 struct wined3d_map_desc dst_map, src_map;
3881 const BYTE *sbase = NULL;
3882 HRESULT hr = WINED3D_OK;
3883 BOOL same_sub_resource;
3884 const BYTE *sbuf;
3885 BYTE *dbuf;
3886 int x, y;
3888 TRACE("dst_texture %p, dst_sub_resource_idx %u, dst_box %s, src_texture %p, "
3889 "src_sub_resource_idx %u, src_box %s, flags %#x, fx %p, filter %s.\n",
3890 dst_texture, dst_sub_resource_idx, debug_box(dst_box), src_texture,
3891 src_sub_resource_idx, debug_box(src_box), flags, fx, debug_d3dtexturefiltertype(filter));
3893 if (src_texture == dst_texture && src_sub_resource_idx == dst_sub_resource_idx)
3895 same_sub_resource = TRUE;
3896 wined3d_resource_map(&dst_texture->resource, dst_sub_resource_idx, &dst_map, NULL, 0);
3897 src_map = dst_map;
3898 src_format = dst_texture->resource.format;
3899 dst_format = src_format;
3900 dst_fmt_flags = dst_texture->resource.format_flags;
3901 src_fmt_flags = dst_fmt_flags;
3903 else
3905 same_sub_resource = FALSE;
3906 dst_format = dst_texture->resource.format;
3907 dst_fmt_flags = dst_texture->resource.format_flags;
3908 if (src_texture)
3910 if (dst_texture->resource.format->id != src_texture->resource.format->id)
3912 if (!(converted_texture = surface_convert_format(src_texture, src_sub_resource_idx, dst_format->id)))
3914 /* The conv function writes a FIXME */
3915 WARN("Cannot convert source surface format to dest format.\n");
3916 goto release;
3918 src_texture = converted_texture;
3919 src_sub_resource_idx = 0;
3921 wined3d_resource_map(&src_texture->resource, src_sub_resource_idx, &src_map, NULL, WINED3D_MAP_READONLY);
3922 src_format = src_texture->resource.format;
3923 src_fmt_flags = src_texture->resource.format_flags;
3925 else
3927 src_format = dst_format;
3928 src_fmt_flags = dst_fmt_flags;
3931 wined3d_resource_map(&dst_texture->resource, dst_sub_resource_idx, &dst_map, dst_box, 0);
3934 bpp = dst_format->byte_count;
3935 src_height = src_box->bottom - src_box->top;
3936 src_width = src_box->right - src_box->left;
3937 dst_height = dst_box->bottom - dst_box->top;
3938 dst_width = dst_box->right - dst_box->left;
3939 row_byte_count = dst_width * bpp;
3941 if (src_texture)
3942 sbase = (BYTE *)src_map.data
3943 + ((src_box->top / src_format->block_height) * src_map.row_pitch)
3944 + ((src_box->left / src_format->block_width) * src_format->block_byte_count);
3945 if (same_sub_resource)
3946 dbuf = (BYTE *)dst_map.data
3947 + ((dst_box->top / dst_format->block_height) * dst_map.row_pitch)
3948 + ((dst_box->left / dst_format->block_width) * dst_format->block_byte_count);
3949 else
3950 dbuf = dst_map.data;
3952 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
3954 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
3956 if (same_sub_resource)
3958 FIXME("Only plain blits supported on compressed surfaces.\n");
3959 hr = E_NOTIMPL;
3960 goto release;
3963 if (src_height != dst_height || src_width != dst_width)
3965 WARN("Stretching not supported on compressed surfaces.\n");
3966 hr = WINED3DERR_INVALIDCALL;
3967 goto release;
3970 if (!wined3d_texture_check_block_align(src_texture,
3971 src_sub_resource_idx % src_texture->level_count, src_box))
3973 WARN("Source rectangle not block-aligned.\n");
3974 hr = WINED3DERR_INVALIDCALL;
3975 goto release;
3978 if (!wined3d_texture_check_block_align(dst_texture,
3979 dst_sub_resource_idx % dst_texture->level_count, dst_box))
3981 WARN("Destination rectangle not block-aligned.\n");
3982 hr = WINED3DERR_INVALIDCALL;
3983 goto release;
3986 hr = surface_cpu_blt_compressed(sbase, dbuf,
3987 src_map.row_pitch, dst_map.row_pitch, dst_width, dst_height,
3988 src_format, flags, fx);
3989 goto release;
3992 /* First, all the 'source-less' blits */
3993 if (flags & WINED3D_BLT_COLOR_FILL)
3995 hr = _Blt_ColorFill(dbuf, dst_width, dst_height, bpp, dst_map.row_pitch, fx->fill_color);
3996 flags &= ~WINED3D_BLT_COLOR_FILL;
3999 if (flags & WINED3D_BLT_DEPTH_FILL)
4000 FIXME("WINED3D_BLT_DEPTH_FILL needs to be implemented!\n");
4002 /* Now the 'with source' blits. */
4003 if (src_texture)
4005 int sx, xinc, sy, yinc;
4007 if (!dst_width || !dst_height) /* Hmm... stupid program? */
4008 goto release;
4010 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
4011 && (src_width != dst_width || src_height != dst_height))
4013 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
4014 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
4017 xinc = (src_width << 16) / dst_width;
4018 yinc = (src_height << 16) / dst_height;
4020 if (!flags)
4022 /* No effects, we can cheat here. */
4023 if (dst_width == src_width)
4025 if (dst_height == src_height)
4027 /* No stretching in either direction. This needs to be as
4028 * fast as possible. */
4029 sbuf = sbase;
4031 /* Check for overlapping surfaces. */
4032 if (!same_sub_resource || dst_box->top < src_box->top
4033 || dst_box->right <= src_box->left || src_box->right <= dst_box->left)
4035 /* No overlap, or dst above src, so copy from top downwards. */
4036 for (y = 0; y < dst_height; ++y)
4038 memcpy(dbuf, sbuf, row_byte_count);
4039 sbuf += src_map.row_pitch;
4040 dbuf += dst_map.row_pitch;
4043 else if (dst_box->top > src_box->top)
4045 /* Copy from bottom upwards. */
4046 sbuf += src_map.row_pitch * dst_height;
4047 dbuf += dst_map.row_pitch * dst_height;
4048 for (y = 0; y < dst_height; ++y)
4050 sbuf -= src_map.row_pitch;
4051 dbuf -= dst_map.row_pitch;
4052 memcpy(dbuf, sbuf, row_byte_count);
4055 else
4057 /* Src and dst overlapping on the same line, use memmove. */
4058 for (y = 0; y < dst_height; ++y)
4060 memmove(dbuf, sbuf, row_byte_count);
4061 sbuf += src_map.row_pitch;
4062 dbuf += dst_map.row_pitch;
4066 else
4068 /* Stretching in y direction only. */
4069 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
4071 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4072 memcpy(dbuf, sbuf, row_byte_count);
4073 dbuf += dst_map.row_pitch;
4077 else
4079 /* Stretching in X direction. */
4080 int last_sy = -1;
4081 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
4083 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4085 if ((sy >> 16) == (last_sy >> 16))
4087 /* This source row is the same as last source row -
4088 * Copy the already stretched row. */
4089 memcpy(dbuf, dbuf - dst_map.row_pitch, row_byte_count);
4091 else
4093 #define STRETCH_ROW(type) \
4094 do { \
4095 const type *s = (const type *)sbuf; \
4096 type *d = (type *)dbuf; \
4097 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
4098 d[x] = s[sx >> 16]; \
4099 } while(0)
4101 switch(bpp)
4103 case 1:
4104 STRETCH_ROW(BYTE);
4105 break;
4106 case 2:
4107 STRETCH_ROW(WORD);
4108 break;
4109 case 4:
4110 STRETCH_ROW(DWORD);
4111 break;
4112 case 3:
4114 const BYTE *s;
4115 BYTE *d = dbuf;
4116 for (x = sx = 0; x < dst_width; x++, sx+= xinc)
4118 DWORD pixel;
4120 s = sbuf + 3 * (sx >> 16);
4121 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4122 d[0] = (pixel ) & 0xff;
4123 d[1] = (pixel >> 8) & 0xff;
4124 d[2] = (pixel >> 16) & 0xff;
4125 d += 3;
4127 break;
4129 default:
4130 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
4131 hr = WINED3DERR_NOTAVAILABLE;
4132 goto error;
4134 #undef STRETCH_ROW
4136 dbuf += dst_map.row_pitch;
4137 last_sy = sy;
4141 else
4143 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
4144 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
4145 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
4146 if (flags & (WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
4147 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE))
4149 /* The color keying flags are checked for correctness in ddraw */
4150 if (flags & WINED3D_BLT_SRC_CKEY)
4152 keylow = src_texture->async.src_blt_color_key.color_space_low_value;
4153 keyhigh = src_texture->async.src_blt_color_key.color_space_high_value;
4155 else if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
4157 keylow = fx->src_color_key.color_space_low_value;
4158 keyhigh = fx->src_color_key.color_space_high_value;
4161 if (flags & WINED3D_BLT_DST_CKEY)
4163 /* Destination color keys are taken from the source surface! */
4164 destkeylow = src_texture->async.dst_blt_color_key.color_space_low_value;
4165 destkeyhigh = src_texture->async.dst_blt_color_key.color_space_high_value;
4167 else if (flags & WINED3D_BLT_DST_CKEY_OVERRIDE)
4169 destkeylow = fx->dst_color_key.color_space_low_value;
4170 destkeyhigh = fx->dst_color_key.color_space_high_value;
4173 if (bpp == 1)
4175 keymask = 0xff;
4177 else
4179 DWORD masks[3];
4180 get_color_masks(src_format, masks);
4181 keymask = masks[0]
4182 | masks[1]
4183 | masks[2];
4185 flags &= ~(WINED3D_BLT_SRC_CKEY | WINED3D_BLT_DST_CKEY
4186 | WINED3D_BLT_SRC_CKEY_OVERRIDE | WINED3D_BLT_DST_CKEY_OVERRIDE);
4189 if (flags & WINED3D_BLT_FX)
4191 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
4192 LONG tmpxy;
4193 dTopLeft = dbuf;
4194 dTopRight = dbuf + ((dst_width - 1) * bpp);
4195 dBottomLeft = dTopLeft + ((dst_height - 1) * dst_map.row_pitch);
4196 dBottomRight = dBottomLeft + ((dst_width - 1) * bpp);
4198 if (fx->fx & WINEDDBLTFX_ARITHSTRETCHY)
4200 /* I don't think we need to do anything about this flag */
4201 WARN("Nothing done for WINEDDBLTFX_ARITHSTRETCHY.\n");
4203 if (fx->fx & WINEDDBLTFX_MIRRORLEFTRIGHT)
4205 tmp = dTopRight;
4206 dTopRight = dTopLeft;
4207 dTopLeft = tmp;
4208 tmp = dBottomRight;
4209 dBottomRight = dBottomLeft;
4210 dBottomLeft = tmp;
4211 dstxinc = dstxinc * -1;
4213 if (fx->fx & WINEDDBLTFX_MIRRORUPDOWN)
4215 tmp = dTopLeft;
4216 dTopLeft = dBottomLeft;
4217 dBottomLeft = tmp;
4218 tmp = dTopRight;
4219 dTopRight = dBottomRight;
4220 dBottomRight = tmp;
4221 dstyinc = dstyinc * -1;
4223 if (fx->fx & WINEDDBLTFX_NOTEARING)
4225 /* I don't think we need to do anything about this flag */
4226 WARN("Nothing done for WINEDDBLTFX_NOTEARING.\n");
4228 if (fx->fx & WINEDDBLTFX_ROTATE180)
4230 tmp = dBottomRight;
4231 dBottomRight = dTopLeft;
4232 dTopLeft = tmp;
4233 tmp = dBottomLeft;
4234 dBottomLeft = dTopRight;
4235 dTopRight = tmp;
4236 dstxinc = dstxinc * -1;
4237 dstyinc = dstyinc * -1;
4239 if (fx->fx & WINEDDBLTFX_ROTATE270)
4241 tmp = dTopLeft;
4242 dTopLeft = dBottomLeft;
4243 dBottomLeft = dBottomRight;
4244 dBottomRight = dTopRight;
4245 dTopRight = tmp;
4246 tmpxy = dstxinc;
4247 dstxinc = dstyinc;
4248 dstyinc = tmpxy;
4249 dstxinc = dstxinc * -1;
4251 if (fx->fx & WINEDDBLTFX_ROTATE90)
4253 tmp = dTopLeft;
4254 dTopLeft = dTopRight;
4255 dTopRight = dBottomRight;
4256 dBottomRight = dBottomLeft;
4257 dBottomLeft = tmp;
4258 tmpxy = dstxinc;
4259 dstxinc = dstyinc;
4260 dstyinc = tmpxy;
4261 dstyinc = dstyinc * -1;
4263 if (fx->fx & WINEDDBLTFX_ZBUFFERBASEDEST)
4265 /* I don't think we need to do anything about this flag */
4266 WARN("Nothing done for WINEDDBLTFX_ZBUFFERBASEDEST.\n");
4268 dbuf = dTopLeft;
4269 flags &= ~(WINED3D_BLT_FX);
4272 #define COPY_COLORKEY_FX(type) \
4273 do { \
4274 const type *s; \
4275 type *d = (type *)dbuf, *dx, tmp; \
4276 for (y = sy = 0; y < dst_height; ++y, sy += yinc) \
4278 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
4279 dx = d; \
4280 for (x = sx = 0; x < dst_width; ++x, sx += xinc) \
4282 tmp = s[sx >> 16]; \
4283 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
4284 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
4286 dx[0] = tmp; \
4288 dx = (type *)(((BYTE *)dx) + dstxinc); \
4290 d = (type *)(((BYTE *)d) + dstyinc); \
4292 } while(0)
4294 switch (bpp)
4296 case 1:
4297 COPY_COLORKEY_FX(BYTE);
4298 break;
4299 case 2:
4300 COPY_COLORKEY_FX(WORD);
4301 break;
4302 case 4:
4303 COPY_COLORKEY_FX(DWORD);
4304 break;
4305 case 3:
4307 const BYTE *s;
4308 BYTE *d = dbuf, *dx;
4309 for (y = sy = 0; y < dst_height; ++y, sy += yinc)
4311 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4312 dx = d;
4313 for (x = sx = 0; x < dst_width; ++x, sx+= xinc)
4315 DWORD pixel, dpixel = 0;
4316 s = sbuf + 3 * (sx>>16);
4317 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4318 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
4319 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
4320 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
4322 dx[0] = (pixel ) & 0xff;
4323 dx[1] = (pixel >> 8) & 0xff;
4324 dx[2] = (pixel >> 16) & 0xff;
4326 dx += dstxinc;
4328 d += dstyinc;
4330 break;
4332 default:
4333 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
4334 (flags & WINED3D_BLT_SRC_CKEY) ? "Source" : "Destination", bpp * 8);
4335 hr = WINED3DERR_NOTAVAILABLE;
4336 goto error;
4337 #undef COPY_COLORKEY_FX
4342 error:
4343 if (flags && FIXME_ON(d3d_surface))
4345 FIXME("\tUnsupported flags: %#x.\n", flags);
4348 release:
4349 wined3d_resource_unmap(&dst_texture->resource, dst_sub_resource_idx);
4350 if (src_texture && !same_sub_resource)
4351 wined3d_resource_unmap(&src_texture->resource, src_sub_resource_idx);
4352 if (converted_texture)
4353 wined3d_texture_decref(converted_texture);
4355 return hr;
4358 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_rendertarget_view *view,
4359 const RECT *rect, const struct wined3d_color *color)
4361 const struct wined3d_box box = {rect->left, rect->top, rect->right, rect->bottom, 0, 1};
4362 static const struct wined3d_box src_box;
4363 struct wined3d_blt_fx fx;
4365 fx.fill_color = wined3d_format_convert_from_float(view->format, color);
4366 return surface_cpu_blt(wined3d_texture_from_resource(view->resource), view->sub_resource_idx,
4367 &box, NULL, 0, &src_box, WINED3D_BLT_COLOR_FILL, &fx, WINED3D_TEXF_POINT);
4370 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
4371 struct wined3d_rendertarget_view *view, const RECT *rect, float depth)
4373 FIXME("Depth filling not implemented by cpu_blit.\n");
4374 return WINED3DERR_INVALIDCALL;
4377 static void cpu_blit_blit_surface(struct wined3d_device *device, enum wined3d_blit_op op, DWORD filter,
4378 struct wined3d_surface *src_surface, const RECT *src_rect,
4379 struct wined3d_surface *dst_surface, const RECT *dst_rect,
4380 const struct wined3d_color_key *color_key)
4382 /* FIXME: Remove error returns from surface_blt_cpu. */
4383 ERR("Blit method not implemented by cpu_blit.\n");
4386 const struct blit_shader cpu_blit = {
4387 cpu_blit_alloc,
4388 cpu_blit_free,
4389 cpu_blit_set,
4390 cpu_blit_unset,
4391 cpu_blit_supported,
4392 cpu_blit_color_fill,
4393 cpu_blit_depth_fill,
4394 cpu_blit_blit_surface,
4397 HRESULT wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4398 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
4399 const struct wined3d_blt_fx *fx, enum wined3d_texture_filter_type filter)
4401 struct wined3d_box dst_box = {dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, 0, 1};
4402 struct wined3d_box src_box = {src_rect->left, src_rect->top, src_rect->right, src_rect->bottom, 0, 1};
4403 struct wined3d_texture *dst_texture = dst_surface->container;
4404 struct wined3d_device *device = dst_texture->resource.device;
4405 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
4406 struct wined3d_texture *src_texture = NULL;
4407 DWORD src_ds_flags, dst_ds_flags;
4408 BOOL scale, convert;
4410 static const DWORD simple_blit = WINED3D_BLT_ASYNC
4411 | WINED3D_BLT_COLOR_FILL
4412 | WINED3D_BLT_SRC_CKEY
4413 | WINED3D_BLT_SRC_CKEY_OVERRIDE
4414 | WINED3D_BLT_WAIT
4415 | WINED3D_BLT_DEPTH_FILL
4416 | WINED3D_BLT_DO_NOT_WAIT
4417 | WINED3D_BLT_ALPHA_TEST;
4419 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
4420 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4421 flags, fx, debug_d3dtexturefiltertype(filter));
4422 TRACE("Usage is %s.\n", debug_d3dusage(dst_texture->resource.usage));
4424 if (fx)
4426 TRACE("fx %#x.\n", fx->fx);
4427 TRACE("fill_color 0x%08x.\n", fx->fill_color);
4428 TRACE("dst_color_key {0x%08x, 0x%08x}.\n",
4429 fx->dst_color_key.color_space_low_value,
4430 fx->dst_color_key.color_space_high_value);
4431 TRACE("src_color_key {0x%08x, 0x%08x}.\n",
4432 fx->src_color_key.color_space_low_value,
4433 fx->src_color_key.color_space_high_value);
4436 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
4438 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
4439 return WINEDDERR_SURFACEBUSY;
4442 if (dst_rect->left >= dst_rect->right || dst_rect->top >= dst_rect->bottom
4443 || dst_rect->left > dst_surface->resource.width || dst_rect->left < 0
4444 || dst_rect->top > dst_surface->resource.height || dst_rect->top < 0
4445 || dst_rect->right > dst_surface->resource.width || dst_rect->right < 0
4446 || dst_rect->bottom > dst_surface->resource.height || dst_rect->bottom < 0)
4448 WARN("The application gave us a bad destination rectangle.\n");
4449 return WINEDDERR_INVALIDRECT;
4452 if (src_surface)
4454 if (src_rect->left >= src_rect->right || src_rect->top >= src_rect->bottom
4455 || src_rect->left > src_surface->resource.width || src_rect->left < 0
4456 || src_rect->top > src_surface->resource.height || src_rect->top < 0
4457 || src_rect->right > src_surface->resource.width || src_rect->right < 0
4458 || src_rect->bottom > src_surface->resource.height || src_rect->bottom < 0)
4460 WARN("The application gave us a bad source rectangle.\n");
4461 return WINEDDERR_INVALIDRECT;
4463 src_texture = src_surface->container;
4466 if (!fx || !(fx->fx))
4467 flags &= ~WINED3D_BLT_FX;
4469 if (flags & WINED3D_BLT_WAIT)
4470 flags &= ~WINED3D_BLT_WAIT;
4472 if (flags & WINED3D_BLT_ASYNC)
4474 static unsigned int once;
4476 if (!once++)
4477 FIXME("Can't handle WINED3D_BLT_ASYNC flag.\n");
4478 flags &= ~WINED3D_BLT_ASYNC;
4481 /* WINED3D_BLT_DO_NOT_WAIT appeared in DX7. */
4482 if (flags & WINED3D_BLT_DO_NOT_WAIT)
4484 static unsigned int once;
4486 if (!once++)
4487 FIXME("Can't handle WINED3D_BLT_DO_NOT_WAIT flag.\n");
4488 flags &= ~WINED3D_BLT_DO_NOT_WAIT;
4491 if (!device->d3d_initialized)
4493 WARN("D3D not initialized, using fallback.\n");
4494 goto cpu;
4497 /* We want to avoid invalidating the sysmem location for converted
4498 * surfaces, since otherwise we'd have to convert the data back when
4499 * locking them. */
4500 if (dst_texture->flags & WINED3D_TEXTURE_CONVERTED || dst_texture->resource.format->convert
4501 || wined3d_format_get_color_key_conversion(dst_texture, TRUE))
4503 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
4504 goto cpu;
4507 if (flags & ~simple_blit)
4509 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
4510 goto fallback;
4513 if (src_surface)
4514 src_swapchain = src_texture->swapchain;
4515 else
4516 src_swapchain = NULL;
4518 dst_swapchain = dst_texture->swapchain;
4520 /* This isn't strictly needed. FBO blits for example could deal with
4521 * cross-swapchain blits by first downloading the source to a texture
4522 * before switching to the destination context. We just have this here to
4523 * not have to deal with the issue, since cross-swapchain blits should be
4524 * rare. */
4525 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
4527 FIXME("Using fallback for cross-swapchain blit.\n");
4528 goto fallback;
4531 scale = src_surface
4532 && (src_rect->right - src_rect->left != dst_rect->right - dst_rect->left
4533 || src_rect->bottom - src_rect->top != dst_rect->bottom - dst_rect->top);
4534 convert = src_surface && src_texture->resource.format->id != dst_texture->resource.format->id;
4536 dst_ds_flags = dst_texture->resource.format_flags
4537 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
4538 if (src_surface)
4539 src_ds_flags = src_texture->resource.format_flags
4540 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
4541 else
4542 src_ds_flags = 0;
4544 if (src_ds_flags || dst_ds_flags)
4546 if (flags & WINED3D_BLT_DEPTH_FILL)
4548 float depth;
4550 TRACE("Depth fill.\n");
4552 if (!surface_convert_depth_to_float(dst_surface, fx->fill_color, &depth))
4553 return WINED3DERR_INVALIDCALL;
4555 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, dst_rect, depth)))
4556 return WINED3D_OK;
4558 else
4560 if (src_ds_flags != dst_ds_flags)
4562 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
4563 return WINED3DERR_INVALIDCALL;
4566 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_texture->resource.draw_binding,
4567 src_rect, dst_surface, dst_texture->resource.draw_binding, dst_rect)))
4568 return WINED3D_OK;
4571 else
4573 const struct blit_shader *blitter;
4575 /* In principle this would apply to depth blits as well, but we don't
4576 * implement those in the CPU blitter at the moment. */
4577 if ((dst_surface->locations & dst_surface->resource.map_binding)
4578 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
4580 if (scale)
4581 TRACE("Not doing sysmem blit because of scaling.\n");
4582 else if (convert)
4583 TRACE("Not doing sysmem blit because of format conversion.\n");
4584 else
4585 goto cpu;
4588 if (flags & WINED3D_BLT_COLOR_FILL)
4590 struct wined3d_color color;
4591 const struct wined3d_palette *palette = dst_swapchain ? dst_swapchain->palette : NULL;
4593 TRACE("Color fill.\n");
4595 if (!wined3d_format_convert_color_to_float(dst_texture->resource.format,
4596 palette, fx->fill_color, &color))
4597 goto fallback;
4599 if (SUCCEEDED(surface_color_fill(dst_surface, dst_rect, &color)))
4600 return WINED3D_OK;
4602 else
4604 enum wined3d_blit_op blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
4605 const struct wined3d_color_key *color_key = NULL;
4607 TRACE("Color blit.\n");
4608 if (flags & WINED3D_BLT_SRC_CKEY_OVERRIDE)
4610 color_key = &fx->src_color_key;
4611 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
4613 else if (flags & WINED3D_BLT_SRC_CKEY)
4615 color_key = &src_texture->async.src_blt_color_key;
4616 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
4618 else if (flags & WINED3D_BLT_ALPHA_TEST)
4620 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_ALPHATEST;
4622 else if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
4623 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
4625 /* Upload */
4626 if (scale)
4627 TRACE("Not doing upload because of scaling.\n");
4628 else if (convert)
4629 TRACE("Not doing upload because of format conversion.\n");
4630 else
4632 POINT dst_point = {dst_rect->left, dst_rect->top};
4634 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, src_rect)))
4636 if (!wined3d_resource_is_offscreen(&dst_texture->resource))
4638 struct wined3d_context *context = context_acquire(device, dst_surface);
4639 surface_load_location(dst_surface, context, dst_texture->resource.draw_binding);
4640 context_release(context);
4642 return WINED3D_OK;
4646 else if (dst_swapchain && dst_swapchain->back_buffers
4647 && dst_texture == dst_swapchain->front_buffer
4648 && src_texture == dst_swapchain->back_buffers[0])
4650 /* Use present for back -> front blits. The idea behind this is
4651 * that present is potentially faster than a blit, in particular
4652 * when FBO blits aren't available. Some ddraw applications like
4653 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
4654 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
4655 * applications can't blit directly to the frontbuffer. */
4656 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
4658 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
4660 /* Set the swap effect to COPY, we don't want the backbuffer
4661 * to become undefined. */
4662 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
4663 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
4664 dst_swapchain->desc.swap_effect = swap_effect;
4666 return WINED3D_OK;
4669 if (fbo_blit_supported(&device->adapter->gl_info, blit_op,
4670 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
4671 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format))
4673 struct wined3d_context *context;
4674 TRACE("Using FBO blit.\n");
4676 context = context_acquire(device, NULL);
4677 surface_blt_fbo(device, context, filter,
4678 src_surface, src_texture->resource.draw_binding, src_rect,
4679 dst_surface, dst_texture->resource.draw_binding, dst_rect);
4680 context_release(context);
4682 surface_validate_location(dst_surface, dst_texture->resource.draw_binding);
4683 surface_invalidate_location(dst_surface, ~dst_texture->resource.draw_binding);
4685 return WINED3D_OK;
4688 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, blit_op,
4689 src_rect, src_texture->resource.usage, src_texture->resource.pool, src_texture->resource.format,
4690 dst_rect, dst_texture->resource.usage, dst_texture->resource.pool, dst_texture->resource.format);
4691 if (blitter)
4693 blitter->blit_surface(device, blit_op, filter, src_surface,
4694 src_rect, dst_surface, dst_rect, color_key);
4695 return WINED3D_OK;
4700 fallback:
4701 /* Special cases for render targets. */
4702 if (SUCCEEDED(surface_blt_special(dst_surface, dst_rect, src_surface, src_rect, flags, fx, filter)))
4703 return WINED3D_OK;
4705 cpu:
4706 return surface_cpu_blt(dst_texture, surface_get_sub_resource_idx(dst_surface), &dst_box,
4707 src_texture, src_texture ? surface_get_sub_resource_idx(src_surface) : 0, &src_box, flags, fx, filter);
4710 HRESULT wined3d_surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
4711 const struct wined3d_resource_desc *desc, GLenum target, unsigned int level, unsigned int layer, DWORD flags)
4713 struct wined3d_device *device = container->resource.device;
4714 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4715 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
4716 BOOL lockable = flags & WINED3D_TEXTURE_CREATE_MAPPABLE;
4717 UINT multisample_quality = desc->multisample_quality;
4718 unsigned int resource_size;
4719 HRESULT hr;
4721 /* Quick lockable sanity check.
4722 * TODO: remove this after surfaces, usage and lockability have been debugged properly
4723 * this function is too deep to need to care about things like this.
4724 * Levels need to be checked too, since they all affect what can be done. */
4725 switch (desc->pool)
4727 case WINED3D_POOL_MANAGED:
4728 if (desc->usage & WINED3DUSAGE_DYNAMIC)
4729 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
4730 break;
4732 case WINED3D_POOL_DEFAULT:
4733 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
4734 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
4735 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
4736 break;
4738 case WINED3D_POOL_SCRATCH:
4739 case WINED3D_POOL_SYSTEM_MEM:
4740 break;
4742 default:
4743 FIXME("Unknown pool %#x.\n", desc->pool);
4744 break;
4747 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
4748 FIXME("Trying to create a render target that isn't in the default pool.\n");
4750 /* FIXME: Check that the format is supported by the device. */
4752 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
4753 if (!resource_size)
4754 return WINED3DERR_INVALIDCALL;
4756 if (device->wined3d->flags & WINED3D_NO3D)
4757 surface->surface_ops = &gdi_surface_ops;
4758 else
4759 surface->surface_ops = &surface_ops;
4761 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE,
4762 format, desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height,
4763 1, resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
4765 WARN("Failed to initialize resource, returning %#x.\n", hr);
4766 return hr;
4769 surface->container = container;
4770 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
4771 list_init(&surface->renderbuffers);
4772 list_init(&surface->overlays);
4774 /* Flags */
4775 if (flags & WINED3D_TEXTURE_CREATE_DISCARD)
4776 surface->flags |= SFLAG_DISCARD;
4777 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
4778 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
4780 surface->texture_target = target;
4781 surface->texture_level = level;
4782 surface->texture_layer = layer;
4784 /* Call the private setup routine */
4785 if (FAILED(hr = surface->surface_ops->surface_private_setup(surface)))
4787 ERR("Private setup failed, hr %#x.\n", hr);
4788 wined3d_surface_cleanup(surface);
4789 return hr;
4792 /* Similar to lockable rendertargets above, creating the DIB section
4793 * during surface initialization prevents the sysmem pointer from changing
4794 * after a wined3d_texture_get_dc() call. */
4795 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
4796 && SUCCEEDED(surface_create_dib_section(surface)))
4797 surface->resource.map_binding = WINED3D_LOCATION_DIB;
4799 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
4801 wined3d_resource_free_sysmem(&surface->resource);
4802 surface_validate_location(surface, WINED3D_LOCATION_DIB);
4803 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
4806 return hr;
4809 /* Context activation is done by the caller. Context may be NULL in
4810 * WINED3D_NO3D mode. */
4811 void wined3d_surface_prepare(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
4813 struct wined3d_texture *texture = surface->container;
4815 switch (location)
4817 case WINED3D_LOCATION_SYSMEM:
4818 surface_prepare_system_memory(surface);
4819 break;
4821 case WINED3D_LOCATION_USER_MEMORY:
4822 if (!texture->user_memory)
4823 ERR("Map binding is set to WINED3D_LOCATION_USER_MEMORY but surface->user_memory is NULL.\n");
4824 break;
4826 case WINED3D_LOCATION_DIB:
4827 if (!surface->dib.bitmap_data)
4828 ERR("Map binding is set to WINED3D_LOCATION_DIB but surface->dib.bitmap_data is NULL.\n");
4829 break;
4831 case WINED3D_LOCATION_BUFFER:
4832 wined3d_texture_prepare_buffer_object(texture,
4833 surface_get_sub_resource_idx(surface), context->gl_info);
4834 break;
4836 case WINED3D_LOCATION_TEXTURE_RGB:
4837 wined3d_texture_prepare_texture(texture, context, FALSE);
4838 break;
4840 case WINED3D_LOCATION_TEXTURE_SRGB:
4841 wined3d_texture_prepare_texture(texture, context, TRUE);
4842 break;
4844 case WINED3D_LOCATION_RB_MULTISAMPLE:
4845 surface_prepare_rb(surface, context->gl_info, TRUE);
4846 break;
4848 case WINED3D_LOCATION_RB_RESOLVED:
4849 surface_prepare_rb(surface, context->gl_info, FALSE);
4850 break;