regedit: Allow importing strings with escaped NULL.
[wine.git] / dlls / wined3d / surface.c
blob092cbe680c5249bc01ca72ecf6dc210908378d0b
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 void surface_cleanup(struct wined3d_surface *surface)
45 struct wined3d_surface *overlay, *cur;
47 TRACE("surface %p.\n", surface);
49 if (surface->pbo || surface->rb_multisample
50 || surface->rb_resolved || !list_empty(&surface->renderbuffers))
52 struct wined3d_renderbuffer_entry *entry, *entry2;
53 const struct wined3d_gl_info *gl_info;
54 struct wined3d_context *context;
56 context = context_acquire(surface->resource.device, NULL);
57 gl_info = context->gl_info;
59 if (surface->pbo)
61 TRACE("Deleting PBO %u.\n", surface->pbo);
62 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
65 if (surface->rb_multisample)
67 TRACE("Deleting multisample renderbuffer %u.\n", surface->rb_multisample);
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 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
77 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
79 TRACE("Deleting renderbuffer %u.\n", entry->id);
80 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
81 HeapFree(GetProcessHeap(), 0, entry);
84 context_release(context);
87 if (surface->flags & SFLAG_DIBSECTION)
89 DeleteDC(surface->hDC);
90 DeleteObject(surface->dib.DIBsection);
91 surface->dib.bitmap_data = NULL;
94 if (surface->overlay_dest)
95 list_remove(&surface->overlay_entry);
97 LIST_FOR_EACH_ENTRY_SAFE(overlay, cur, &surface->overlays, struct wined3d_surface, overlay_entry)
99 list_remove(&overlay->overlay_entry);
100 overlay->overlay_dest = NULL;
103 resource_cleanup(&surface->resource);
106 void wined3d_surface_destroy(struct wined3d_surface *surface)
108 TRACE("surface %p.\n", surface);
110 surface_cleanup(surface);
111 surface->resource.parent_ops->wined3d_object_destroyed(surface->resource.parent);
112 HeapFree(GetProcessHeap(), 0, surface);
115 void surface_get_drawable_size(const struct wined3d_surface *surface, const struct wined3d_context *context,
116 unsigned int *width, unsigned int *height)
118 if (surface->container->swapchain)
120 /* The drawable size of an onscreen drawable is the surface size.
121 * (Actually: The window size, but the surface is created in window
122 * size.) */
123 *width = context->current_rt->resource.width;
124 *height = context->current_rt->resource.height;
126 else if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER)
128 const struct wined3d_swapchain *swapchain = context->swapchain;
130 /* The drawable size of a backbuffer / aux buffer offscreen target is
131 * the size of the current context's drawable, which is the size of
132 * the back buffer of the swapchain the active context belongs to. */
133 *width = swapchain->desc.backbuffer_width;
134 *height = swapchain->desc.backbuffer_height;
136 else
138 /* The drawable size of an FBO target is the OpenGL texture size,
139 * which is the power of two size. */
140 *width = context->current_rt->pow2Width;
141 *height = context->current_rt->pow2Height;
145 struct blt_info
147 GLenum binding;
148 GLenum bind_target;
149 enum tex_types tex_type;
150 GLfloat coords[4][3];
153 struct float_rect
155 float l;
156 float t;
157 float r;
158 float b;
161 static inline void cube_coords_float(const RECT *r, UINT w, UINT h, struct float_rect *f)
163 f->l = ((r->left * 2.0f) / w) - 1.0f;
164 f->t = ((r->top * 2.0f) / h) - 1.0f;
165 f->r = ((r->right * 2.0f) / w) - 1.0f;
166 f->b = ((r->bottom * 2.0f) / h) - 1.0f;
169 static void surface_get_blt_info(GLenum target, const RECT *rect, GLsizei w, GLsizei h, struct blt_info *info)
171 GLfloat (*coords)[3] = info->coords;
172 struct float_rect f;
174 switch (target)
176 default:
177 FIXME("Unsupported texture target %#x\n", target);
178 /* Fall back to GL_TEXTURE_2D */
179 case GL_TEXTURE_2D:
180 info->binding = GL_TEXTURE_BINDING_2D;
181 info->bind_target = GL_TEXTURE_2D;
182 info->tex_type = tex_2d;
183 coords[0][0] = (float)rect->left / w;
184 coords[0][1] = (float)rect->top / h;
185 coords[0][2] = 0.0f;
187 coords[1][0] = (float)rect->right / w;
188 coords[1][1] = (float)rect->top / h;
189 coords[1][2] = 0.0f;
191 coords[2][0] = (float)rect->left / w;
192 coords[2][1] = (float)rect->bottom / h;
193 coords[2][2] = 0.0f;
195 coords[3][0] = (float)rect->right / w;
196 coords[3][1] = (float)rect->bottom / h;
197 coords[3][2] = 0.0f;
198 break;
200 case GL_TEXTURE_RECTANGLE_ARB:
201 info->binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
202 info->bind_target = GL_TEXTURE_RECTANGLE_ARB;
203 info->tex_type = tex_rect;
204 coords[0][0] = rect->left; coords[0][1] = rect->top; coords[0][2] = 0.0f;
205 coords[1][0] = rect->right; coords[1][1] = rect->top; coords[1][2] = 0.0f;
206 coords[2][0] = rect->left; coords[2][1] = rect->bottom; coords[2][2] = 0.0f;
207 coords[3][0] = rect->right; coords[3][1] = rect->bottom; coords[3][2] = 0.0f;
208 break;
210 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
211 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
212 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
213 info->tex_type = tex_cube;
214 cube_coords_float(rect, w, h, &f);
216 coords[0][0] = 1.0f; coords[0][1] = -f.t; coords[0][2] = -f.l;
217 coords[1][0] = 1.0f; coords[1][1] = -f.t; coords[1][2] = -f.r;
218 coords[2][0] = 1.0f; coords[2][1] = -f.b; coords[2][2] = -f.l;
219 coords[3][0] = 1.0f; coords[3][1] = -f.b; coords[3][2] = -f.r;
220 break;
222 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
223 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
224 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
225 info->tex_type = tex_cube;
226 cube_coords_float(rect, w, h, &f);
228 coords[0][0] = -1.0f; coords[0][1] = -f.t; coords[0][2] = f.l;
229 coords[1][0] = -1.0f; coords[1][1] = -f.t; coords[1][2] = f.r;
230 coords[2][0] = -1.0f; coords[2][1] = -f.b; coords[2][2] = f.l;
231 coords[3][0] = -1.0f; coords[3][1] = -f.b; coords[3][2] = f.r;
232 break;
234 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
235 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
236 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
237 info->tex_type = tex_cube;
238 cube_coords_float(rect, w, h, &f);
240 coords[0][0] = f.l; coords[0][1] = 1.0f; coords[0][2] = f.t;
241 coords[1][0] = f.r; coords[1][1] = 1.0f; coords[1][2] = f.t;
242 coords[2][0] = f.l; coords[2][1] = 1.0f; coords[2][2] = f.b;
243 coords[3][0] = f.r; coords[3][1] = 1.0f; coords[3][2] = f.b;
244 break;
246 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
247 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
248 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
249 info->tex_type = tex_cube;
250 cube_coords_float(rect, w, h, &f);
252 coords[0][0] = f.l; coords[0][1] = -1.0f; coords[0][2] = -f.t;
253 coords[1][0] = f.r; coords[1][1] = -1.0f; coords[1][2] = -f.t;
254 coords[2][0] = f.l; coords[2][1] = -1.0f; coords[2][2] = -f.b;
255 coords[3][0] = f.r; coords[3][1] = -1.0f; coords[3][2] = -f.b;
256 break;
258 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
259 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
260 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
261 info->tex_type = tex_cube;
262 cube_coords_float(rect, w, h, &f);
264 coords[0][0] = f.l; coords[0][1] = -f.t; coords[0][2] = 1.0f;
265 coords[1][0] = f.r; coords[1][1] = -f.t; coords[1][2] = 1.0f;
266 coords[2][0] = f.l; coords[2][1] = -f.b; coords[2][2] = 1.0f;
267 coords[3][0] = f.r; coords[3][1] = -f.b; coords[3][2] = 1.0f;
268 break;
270 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
271 info->binding = GL_TEXTURE_BINDING_CUBE_MAP_ARB;
272 info->bind_target = GL_TEXTURE_CUBE_MAP_ARB;
273 info->tex_type = tex_cube;
274 cube_coords_float(rect, w, h, &f);
276 coords[0][0] = -f.l; coords[0][1] = -f.t; coords[0][2] = -1.0f;
277 coords[1][0] = -f.r; coords[1][1] = -f.t; coords[1][2] = -1.0f;
278 coords[2][0] = -f.l; coords[2][1] = -f.b; coords[2][2] = -1.0f;
279 coords[3][0] = -f.r; coords[3][1] = -f.b; coords[3][2] = -1.0f;
280 break;
284 static void surface_get_rect(const struct wined3d_surface *surface, const RECT *rect_in, RECT *rect_out)
286 if (rect_in)
287 *rect_out = *rect_in;
288 else
290 rect_out->left = 0;
291 rect_out->top = 0;
292 rect_out->right = surface->resource.width;
293 rect_out->bottom = surface->resource.height;
297 /* Context activation is done by the caller. */
298 void draw_textured_quad(const struct wined3d_surface *src_surface, struct wined3d_context *context,
299 const RECT *src_rect, const RECT *dst_rect, enum wined3d_texture_filter_type filter)
301 const struct wined3d_gl_info *gl_info = context->gl_info;
302 struct wined3d_texture *texture = src_surface->container;
303 struct blt_info info;
305 surface_get_blt_info(src_surface->texture_target, src_rect, src_surface->pow2Width, src_surface->pow2Height, &info);
307 gl_info->gl_ops.gl.p_glEnable(info.bind_target);
308 checkGLcall("glEnable(bind_target)");
310 context_bind_texture(context, info.bind_target, texture->texture_rgb.name);
312 /* Filtering for StretchRect */
313 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MAG_FILTER,
314 wined3d_gl_mag_filter(magLookup, filter));
315 checkGLcall("glTexParameteri");
316 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
317 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
318 checkGLcall("glTexParameteri");
319 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
320 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
321 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
322 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
323 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
324 checkGLcall("glTexEnvi");
326 /* Draw a quad */
327 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
328 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
329 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
331 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
332 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
334 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
335 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
337 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
338 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
339 gl_info->gl_ops.gl.p_glEnd();
341 /* Unbind the texture */
342 context_bind_texture(context, info.bind_target, 0);
344 /* We changed the filtering settings on the texture. Inform the
345 * container about this to get the filters reset properly next draw. */
346 texture->texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3D_TEXF_POINT;
347 texture->texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3D_TEXF_POINT;
348 texture->texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3D_TEXF_NONE;
349 texture->texture_rgb.states[WINED3DTEXSTA_SRGBTEXTURE] = FALSE;
352 /* Works correctly only for <= 4 bpp formats. */
353 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
355 masks[0] = ((1 << format->red_size) - 1) << format->red_offset;
356 masks[1] = ((1 << format->green_size) - 1) << format->green_offset;
357 masks[2] = ((1 << format->blue_size) - 1) << format->blue_offset;
360 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
362 const struct wined3d_format *format = surface->resource.format;
363 SYSTEM_INFO sysInfo;
364 BITMAPINFO *b_info;
365 int extraline = 0;
366 DWORD *masks;
368 TRACE("surface %p.\n", surface);
370 if (!(format->flags & WINED3DFMT_FLAG_GETDC))
372 WARN("Cannot use GetDC on a %s surface.\n", debug_d3dformat(format->id));
373 return WINED3DERR_INVALIDCALL;
376 switch (format->byte_count)
378 case 2:
379 case 4:
380 /* Allocate extra space to store the RGB bit masks. */
381 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
382 break;
384 case 3:
385 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
386 break;
388 default:
389 /* Allocate extra space for a palette. */
390 b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
391 sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << (format->byte_count * 8)));
392 break;
395 if (!b_info)
396 return E_OUTOFMEMORY;
398 /* Some applications access the surface in via DWORDs, and do not take
399 * the necessary care at the end of the surface. So we need at least
400 * 4 extra bytes at the end of the surface. Check against the page size,
401 * if the last page used for the surface has at least 4 spare bytes we're
402 * safe, otherwise add an extra line to the DIB section. */
403 GetSystemInfo(&sysInfo);
404 if( ((surface->resource.size + 3) % sysInfo.dwPageSize) < 4)
406 extraline = 1;
407 TRACE("Adding an extra line to the DIB section.\n");
410 b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
411 /* TODO: Is there a nicer way to force a specific alignment? (8 byte for ddraw) */
412 b_info->bmiHeader.biWidth = wined3d_surface_get_pitch(surface) / format->byte_count;
413 b_info->bmiHeader.biHeight = 0 - surface->resource.height - extraline;
414 b_info->bmiHeader.biSizeImage = (surface->resource.height + extraline)
415 * wined3d_surface_get_pitch(surface);
416 b_info->bmiHeader.biPlanes = 1;
417 b_info->bmiHeader.biBitCount = format->byte_count * 8;
419 b_info->bmiHeader.biXPelsPerMeter = 0;
420 b_info->bmiHeader.biYPelsPerMeter = 0;
421 b_info->bmiHeader.biClrUsed = 0;
422 b_info->bmiHeader.biClrImportant = 0;
424 /* Get the bit masks */
425 masks = (DWORD *)b_info->bmiColors;
426 switch (surface->resource.format->id)
428 case WINED3DFMT_B8G8R8_UNORM:
429 b_info->bmiHeader.biCompression = BI_RGB;
430 break;
432 case WINED3DFMT_B5G5R5X1_UNORM:
433 case WINED3DFMT_B5G5R5A1_UNORM:
434 case WINED3DFMT_B4G4R4A4_UNORM:
435 case WINED3DFMT_B4G4R4X4_UNORM:
436 case WINED3DFMT_B2G3R3_UNORM:
437 case WINED3DFMT_B2G3R3A8_UNORM:
438 case WINED3DFMT_R10G10B10A2_UNORM:
439 case WINED3DFMT_R8G8B8A8_UNORM:
440 case WINED3DFMT_R8G8B8X8_UNORM:
441 case WINED3DFMT_B10G10R10A2_UNORM:
442 case WINED3DFMT_B5G6R5_UNORM:
443 case WINED3DFMT_R16G16B16A16_UNORM:
444 b_info->bmiHeader.biCompression = BI_BITFIELDS;
445 get_color_masks(format, masks);
446 break;
448 default:
449 /* Don't know palette */
450 b_info->bmiHeader.biCompression = BI_RGB;
451 break;
454 TRACE("Creating a DIB section with size %dx%dx%d, size=%d.\n",
455 b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight,
456 b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
457 surface->dib.DIBsection = CreateDIBSection(0, b_info, DIB_RGB_COLORS, &surface->dib.bitmap_data, 0, 0);
459 if (!surface->dib.DIBsection)
461 ERR("Failed to create DIB section.\n");
462 HeapFree(GetProcessHeap(), 0, b_info);
463 return HRESULT_FROM_WIN32(GetLastError());
466 TRACE("DIBSection at %p.\n", surface->dib.bitmap_data);
467 surface->dib.bitmap_size = b_info->bmiHeader.biSizeImage;
469 HeapFree(GetProcessHeap(), 0, b_info);
471 /* Now allocate a DC. */
472 surface->hDC = CreateCompatibleDC(0);
473 SelectObject(surface->hDC, surface->dib.DIBsection);
475 surface->flags |= SFLAG_DIBSECTION;
477 return WINED3D_OK;
480 static void surface_get_memory(const struct wined3d_surface *surface, struct wined3d_bo_address *data,
481 DWORD location)
483 if (location & WINED3D_LOCATION_BUFFER)
485 data->addr = NULL;
486 data->buffer_object = surface->pbo;
487 return;
489 if (location & WINED3D_LOCATION_USER_MEMORY)
491 data->addr = surface->user_memory;
492 data->buffer_object = 0;
493 return;
495 if (location & WINED3D_LOCATION_DIB)
497 data->addr = surface->dib.bitmap_data;
498 data->buffer_object = 0;
499 return;
501 if (location & WINED3D_LOCATION_SYSMEM)
503 data->addr = surface->resource.heap_memory;
504 data->buffer_object = 0;
505 return;
508 ERR("Unexpected locations %s.\n", wined3d_debug_location(location));
509 data->addr = NULL;
510 data->buffer_object = 0;
513 static void surface_prepare_buffer(struct wined3d_surface *surface)
515 struct wined3d_context *context;
516 GLenum error;
517 const struct wined3d_gl_info *gl_info;
519 if (surface->pbo)
520 return;
522 context = context_acquire(surface->resource.device, NULL);
523 gl_info = context->gl_info;
525 GL_EXTCALL(glGenBuffersARB(1, &surface->pbo));
526 error = gl_info->gl_ops.gl.p_glGetError();
527 if (!surface->pbo || error != GL_NO_ERROR)
528 ERR("Failed to create a PBO with error %s (%#x).\n", debug_glerror(error), error);
530 TRACE("Binding PBO %u.\n", surface->pbo);
532 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
533 checkGLcall("glBindBufferARB");
535 GL_EXTCALL(glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->resource.size + 4,
536 NULL, GL_STREAM_DRAW_ARB));
537 checkGLcall("glBufferDataARB");
539 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
540 checkGLcall("glBindBufferARB");
542 context_release(context);
545 static void surface_prepare_system_memory(struct wined3d_surface *surface)
547 TRACE("surface %p.\n", surface);
549 if (surface->resource.heap_memory)
550 return;
552 /* Whatever surface we have, make sure that there is memory allocated
553 * for the downloaded copy, or a PBO to map. */
554 if (!wined3d_resource_allocate_sysmem(&surface->resource))
555 ERR("Failed to allocate system memory.\n");
557 if (surface->locations & WINED3D_LOCATION_SYSMEM)
558 ERR("Surface without system memory has WINED3D_LOCATION_SYSMEM set.\n");
561 void surface_prepare_map_memory(struct wined3d_surface *surface)
563 switch (surface->resource.map_binding)
565 case WINED3D_LOCATION_SYSMEM:
566 surface_prepare_system_memory(surface);
567 break;
569 case WINED3D_LOCATION_USER_MEMORY:
570 if (!surface->user_memory)
571 ERR("Map binding is set to WINED3D_LOCATION_USER_MEMORY but surface->user_memory is NULL.\n");
572 break;
574 case WINED3D_LOCATION_DIB:
575 if (!surface->dib.bitmap_data)
576 ERR("Map binding is set to WINED3D_LOCATION_DIB but surface->dib.bitmap_data is NULL.\n");
577 break;
579 case WINED3D_LOCATION_BUFFER:
580 surface_prepare_buffer(surface);
581 break;
583 default:
584 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
588 static void surface_evict_sysmem(struct wined3d_surface *surface)
590 /* In some conditions the surface memory must not be freed:
591 * WINED3D_TEXTURE_CONVERTED: Converting the data back would take too long
592 * WINED3D_TEXTURE_DYNAMIC_MAP: Avoid freeing the data for performance
593 * SFLAG_CLIENT: OpenGL uses our memory as backup */
594 if (surface->resource.map_count || surface->flags & SFLAG_CLIENT
595 || surface->container->flags & (WINED3D_TEXTURE_CONVERTED | WINED3D_TEXTURE_PIN_SYSMEM
596 | WINED3D_TEXTURE_DYNAMIC_MAP))
597 return;
599 wined3d_resource_free_sysmem(&surface->resource);
600 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
603 static void surface_release_client_storage(struct wined3d_surface *surface)
605 struct wined3d_context *context = context_acquire(surface->resource.device, NULL);
606 const struct wined3d_gl_info *gl_info = context->gl_info;
608 if (surface->container->texture_rgb.name)
610 wined3d_texture_bind_and_dirtify(surface->container, context, FALSE);
611 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
612 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
614 if (surface->container->texture_srgb.name)
616 wined3d_texture_bind_and_dirtify(surface->container, context, TRUE);
617 gl_info->gl_ops.gl.p_glTexImage2D(surface->texture_target, surface->texture_level,
618 GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
620 wined3d_texture_force_reload(surface->container);
622 context_release(context);
625 static BOOL surface_use_pbo(const struct wined3d_surface *surface)
627 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
628 struct wined3d_texture *texture = surface->container;
630 return texture->resource.pool == WINED3D_POOL_DEFAULT
631 && surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU
632 && gl_info->supported[ARB_PIXEL_BUFFER_OBJECT]
633 && !texture->resource.format->convert
634 && !(texture->flags & WINED3D_TEXTURE_PIN_SYSMEM)
635 && !(surface->flags & SFLAG_NONPOW2);
638 static HRESULT surface_private_setup(struct wined3d_surface *surface)
640 /* TODO: Check against the maximum texture sizes supported by the video card. */
641 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
642 unsigned int pow2Width, pow2Height;
644 TRACE("surface %p.\n", surface);
646 /* Non-power2 support */
647 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT]
648 || gl_info->supported[ARB_TEXTURE_RECTANGLE])
650 pow2Width = surface->resource.width;
651 pow2Height = surface->resource.height;
653 else
655 /* Find the nearest pow2 match */
656 pow2Width = pow2Height = 1;
657 while (pow2Width < surface->resource.width)
658 pow2Width <<= 1;
659 while (pow2Height < surface->resource.height)
660 pow2Height <<= 1;
662 surface->pow2Width = pow2Width;
663 surface->pow2Height = pow2Height;
665 if (pow2Width > surface->resource.width || pow2Height > surface->resource.height)
667 /* TODO: Add support for non power two compressed textures. */
668 if (surface->resource.format->flags & (WINED3DFMT_FLAG_COMPRESSED | WINED3DFMT_FLAG_HEIGHT_SCALE))
670 FIXME("(%p) Compressed or height scaled non-power-two textures are not supported w(%d) h(%d)\n",
671 surface, surface->resource.width, surface->resource.height);
672 return WINED3DERR_NOTAVAILABLE;
676 if (pow2Width != surface->resource.width
677 || pow2Height != surface->resource.height)
679 surface->flags |= SFLAG_NONPOW2;
682 if ((surface->pow2Width > gl_info->limits.texture_size || surface->pow2Height > gl_info->limits.texture_size)
683 && !(surface->resource.usage & (WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
685 /* One of three options:
686 * 1: Do the same as we do with NPOT and scale the texture, (any
687 * texture ops would require the texture to be scaled which is
688 * potentially slow)
689 * 2: Set the texture to the maximum size (bad idea).
690 * 3: WARN and return WINED3DERR_NOTAVAILABLE;
691 * 4: Create the surface, but allow it to be used only for DirectDraw
692 * Blts. Some apps (e.g. Swat 3) create textures with a Height of
693 * 16 and a Width > 3000 and blt 16x16 letter areas from them to
694 * the render target. */
695 if (surface->resource.pool == WINED3D_POOL_DEFAULT || surface->resource.pool == WINED3D_POOL_MANAGED)
697 WARN("Unable to allocate a surface which exceeds the maximum OpenGL texture size.\n");
698 return WINED3DERR_NOTAVAILABLE;
701 /* We should never use this surface in combination with OpenGL! */
702 TRACE("Creating an oversized surface: %ux%u.\n",
703 surface->pow2Width, surface->pow2Height);
706 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
707 surface->locations = WINED3D_LOCATION_DISCARDED;
709 if (surface_use_pbo(surface))
710 surface->resource.map_binding = WINED3D_LOCATION_BUFFER;
712 return WINED3D_OK;
715 static void surface_unmap(struct wined3d_surface *surface)
717 struct wined3d_device *device = surface->resource.device;
718 const struct wined3d_gl_info *gl_info;
719 struct wined3d_context *context;
721 TRACE("surface %p.\n", surface);
723 memset(&surface->lockedRect, 0, sizeof(surface->lockedRect));
725 switch (surface->resource.map_binding)
727 case WINED3D_LOCATION_SYSMEM:
728 case WINED3D_LOCATION_USER_MEMORY:
729 case WINED3D_LOCATION_DIB:
730 break;
732 case WINED3D_LOCATION_BUFFER:
733 context = context_acquire(device, NULL);
734 gl_info = context->gl_info;
736 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
737 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB));
738 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
739 checkGLcall("glUnmapBufferARB");
740 context_release(context);
741 break;
743 default:
744 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
747 if (surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_TEXTURE_RGB))
749 TRACE("Not dirtified, nothing to do.\n");
750 return;
753 if (surface->container->swapchain && surface->container->swapchain->front_buffer == surface->container)
754 surface_load_location(surface, surface->container->resource.draw_binding);
755 else if (surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL))
756 FIXME("Depth / stencil buffer locking is not implemented.\n");
759 static BOOL surface_is_full_rect(const struct wined3d_surface *surface, const RECT *r)
761 if ((r->left && r->right) || abs(r->right - r->left) != surface->resource.width)
762 return FALSE;
763 if ((r->top && r->bottom) || abs(r->bottom - r->top) != surface->resource.height)
764 return FALSE;
765 return TRUE;
768 static void surface_depth_blt_fbo(const struct wined3d_device *device,
769 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
770 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
772 const struct wined3d_gl_info *gl_info;
773 struct wined3d_context *context;
774 DWORD src_mask, dst_mask;
775 GLbitfield gl_mask;
777 TRACE("device %p\n", device);
778 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
779 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect));
780 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
781 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect));
783 src_mask = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
784 dst_mask = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
786 if (src_mask != dst_mask)
788 ERR("Incompatible formats %s and %s.\n",
789 debug_d3dformat(src_surface->resource.format->id),
790 debug_d3dformat(dst_surface->resource.format->id));
791 return;
794 if (!src_mask)
796 ERR("Not a depth / stencil format: %s.\n",
797 debug_d3dformat(src_surface->resource.format->id));
798 return;
801 gl_mask = 0;
802 if (src_mask & WINED3DFMT_FLAG_DEPTH)
803 gl_mask |= GL_DEPTH_BUFFER_BIT;
804 if (src_mask & WINED3DFMT_FLAG_STENCIL)
805 gl_mask |= GL_STENCIL_BUFFER_BIT;
807 /* Make sure the locations are up-to-date. Loading the destination
808 * surface isn't required if the entire surface is overwritten. */
809 surface_load_location(src_surface, src_location);
810 if (!surface_is_full_rect(dst_surface, dst_rect))
811 surface_load_location(dst_surface, dst_location);
813 context = context_acquire(device, NULL);
814 if (!context->valid)
816 context_release(context);
817 WARN("Invalid context, skipping blit.\n");
818 return;
821 gl_info = context->gl_info;
823 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, NULL, src_surface, src_location);
824 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
826 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, NULL, dst_surface, dst_location);
827 context_set_draw_buffer(context, GL_NONE);
828 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
829 context_invalidate_state(context, STATE_FRAMEBUFFER);
831 if (gl_mask & GL_DEPTH_BUFFER_BIT)
833 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
834 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_ZWRITEENABLE));
836 if (gl_mask & GL_STENCIL_BUFFER_BIT)
838 if (context->gl_info->supported[EXT_STENCIL_TWO_SIDE])
840 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
841 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_TWOSIDEDSTENCILMODE));
843 gl_info->gl_ops.gl.p_glStencilMask(~0U);
844 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
847 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
848 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
850 gl_info->fbo_ops.glBlitFramebuffer(src_rect->left, src_rect->top, src_rect->right, src_rect->bottom,
851 dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom, gl_mask, GL_NEAREST);
852 checkGLcall("glBlitFramebuffer()");
854 if (wined3d_settings.strict_draw_ordering)
855 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
857 context_release(context);
860 /* Blit between surface locations. Onscreen on different swapchains is not supported.
861 * Depth / stencil is not supported. */
862 static void surface_blt_fbo(const struct wined3d_device *device, enum wined3d_texture_filter_type filter,
863 struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect_in,
864 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect_in)
866 const struct wined3d_gl_info *gl_info;
867 struct wined3d_context *context;
868 RECT src_rect, dst_rect;
869 GLenum gl_filter;
870 GLenum buffer;
872 TRACE("device %p, filter %s,\n", device, debug_d3dtexturefiltertype(filter));
873 TRACE("src_surface %p, src_location %s, src_rect %s,\n",
874 src_surface, wined3d_debug_location(src_location), wine_dbgstr_rect(src_rect_in));
875 TRACE("dst_surface %p, dst_location %s, dst_rect %s.\n",
876 dst_surface, wined3d_debug_location(dst_location), wine_dbgstr_rect(dst_rect_in));
878 src_rect = *src_rect_in;
879 dst_rect = *dst_rect_in;
881 switch (filter)
883 case WINED3D_TEXF_LINEAR:
884 gl_filter = GL_LINEAR;
885 break;
887 default:
888 FIXME("Unsupported filter mode %s (%#x).\n", debug_d3dtexturefiltertype(filter), filter);
889 case WINED3D_TEXF_NONE:
890 case WINED3D_TEXF_POINT:
891 gl_filter = GL_NEAREST;
892 break;
895 /* Resolve the source surface first if needed. */
896 if (src_location == WINED3D_LOCATION_RB_MULTISAMPLE
897 && (src_surface->resource.format->id != dst_surface->resource.format->id
898 || abs(src_rect.bottom - src_rect.top) != abs(dst_rect.bottom - dst_rect.top)
899 || abs(src_rect.right - src_rect.left) != abs(dst_rect.right - dst_rect.left)))
900 src_location = WINED3D_LOCATION_RB_RESOLVED;
902 /* Make sure the locations are up-to-date. Loading the destination
903 * surface isn't required if the entire surface is overwritten. (And is
904 * in fact harmful if we're being called by surface_load_location() with
905 * the purpose of loading the destination surface.) */
906 surface_load_location(src_surface, src_location);
907 if (!surface_is_full_rect(dst_surface, &dst_rect))
908 surface_load_location(dst_surface, dst_location);
910 if (src_location == WINED3D_LOCATION_DRAWABLE) context = context_acquire(device, src_surface);
911 else if (dst_location == WINED3D_LOCATION_DRAWABLE) context = context_acquire(device, dst_surface);
912 else context = context_acquire(device, NULL);
914 if (!context->valid)
916 context_release(context);
917 WARN("Invalid context, skipping blit.\n");
918 return;
921 gl_info = context->gl_info;
923 if (src_location == WINED3D_LOCATION_DRAWABLE)
925 TRACE("Source surface %p is onscreen.\n", src_surface);
926 buffer = surface_get_gl_buffer(src_surface);
927 surface_translate_drawable_coords(src_surface, context->win_handle, &src_rect);
929 else
931 TRACE("Source surface %p is offscreen.\n", src_surface);
932 buffer = GL_COLOR_ATTACHMENT0;
935 context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, src_location);
936 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
937 checkGLcall("glReadBuffer()");
938 context_check_fbo_status(context, GL_READ_FRAMEBUFFER);
940 if (dst_location == WINED3D_LOCATION_DRAWABLE)
942 TRACE("Destination surface %p is onscreen.\n", dst_surface);
943 buffer = surface_get_gl_buffer(dst_surface);
944 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
946 else
948 TRACE("Destination surface %p is offscreen.\n", dst_surface);
949 buffer = GL_COLOR_ATTACHMENT0;
952 context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, dst_location);
953 context_set_draw_buffer(context, buffer);
954 context_check_fbo_status(context, GL_DRAW_FRAMEBUFFER);
955 context_invalidate_state(context, STATE_FRAMEBUFFER);
957 gl_info->gl_ops.gl.p_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
958 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE));
959 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE1));
960 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE2));
961 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_COLORWRITEENABLE3));
963 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
964 context_invalidate_state(context, STATE_RENDER(WINED3D_RS_SCISSORTESTENABLE));
966 gl_info->fbo_ops.glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom,
967 dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, GL_COLOR_BUFFER_BIT, gl_filter);
968 checkGLcall("glBlitFramebuffer()");
970 if (wined3d_settings.strict_draw_ordering
971 || (dst_location == WINED3D_LOCATION_DRAWABLE
972 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
973 gl_info->gl_ops.gl.p_glFlush();
975 context_release(context);
978 static BOOL fbo_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
979 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
980 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
982 if ((wined3d_settings.offscreen_rendering_mode != ORM_FBO) || !gl_info->fbo_ops.glBlitFramebuffer)
983 return FALSE;
985 /* Source and/or destination need to be on the GL side */
986 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
987 return FALSE;
989 switch (blit_op)
991 case WINED3D_BLIT_OP_COLOR_BLIT:
992 if (!((src_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (src_usage & WINED3DUSAGE_RENDERTARGET)))
993 return FALSE;
994 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
995 return FALSE;
996 break;
998 case WINED3D_BLIT_OP_DEPTH_BLIT:
999 if (!(src_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1000 return FALSE;
1001 if (!(dst_format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1002 return FALSE;
1003 break;
1005 default:
1006 return FALSE;
1009 if (!(src_format->id == dst_format->id
1010 || (is_identity_fixup(src_format->color_fixup)
1011 && is_identity_fixup(dst_format->color_fixup))))
1012 return FALSE;
1014 return TRUE;
1017 static BOOL surface_convert_color_to_float(const struct wined3d_surface *surface,
1018 DWORD color, struct wined3d_color *float_color)
1020 const struct wined3d_format *format = surface->resource.format;
1021 const struct wined3d_palette *palette;
1023 switch (format->id)
1025 case WINED3DFMT_P8_UINT:
1026 palette = surface->container->swapchain ? surface->container->swapchain->palette : NULL;
1028 if (palette)
1030 float_color->r = palette->colors[color].rgbRed / 255.0f;
1031 float_color->g = palette->colors[color].rgbGreen / 255.0f;
1032 float_color->b = palette->colors[color].rgbBlue / 255.0f;
1034 else
1036 float_color->r = 0.0f;
1037 float_color->g = 0.0f;
1038 float_color->b = 0.0f;
1040 float_color->a = color / 255.0f;
1041 break;
1043 case WINED3DFMT_B5G6R5_UNORM:
1044 float_color->r = ((color >> 11) & 0x1f) / 31.0f;
1045 float_color->g = ((color >> 5) & 0x3f) / 63.0f;
1046 float_color->b = (color & 0x1f) / 31.0f;
1047 float_color->a = 1.0f;
1048 break;
1050 case WINED3DFMT_B8G8R8_UNORM:
1051 case WINED3DFMT_B8G8R8X8_UNORM:
1052 float_color->r = D3DCOLOR_R(color);
1053 float_color->g = D3DCOLOR_G(color);
1054 float_color->b = D3DCOLOR_B(color);
1055 float_color->a = 1.0f;
1056 break;
1058 case WINED3DFMT_B8G8R8A8_UNORM:
1059 float_color->r = D3DCOLOR_R(color);
1060 float_color->g = D3DCOLOR_G(color);
1061 float_color->b = D3DCOLOR_B(color);
1062 float_color->a = D3DCOLOR_A(color);
1063 break;
1065 default:
1066 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1067 return FALSE;
1070 return TRUE;
1073 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1075 const struct wined3d_format *format = surface->resource.format;
1077 switch (format->id)
1079 case WINED3DFMT_S1_UINT_D15_UNORM:
1080 *float_depth = depth / (float)0x00007fff;
1081 break;
1083 case WINED3DFMT_D16_UNORM:
1084 *float_depth = depth / (float)0x0000ffff;
1085 break;
1087 case WINED3DFMT_D24_UNORM_S8_UINT:
1088 case WINED3DFMT_X8D24_UNORM:
1089 *float_depth = depth / (float)0x00ffffff;
1090 break;
1092 case WINED3DFMT_D32_UNORM:
1093 *float_depth = depth / (float)0xffffffff;
1094 break;
1096 default:
1097 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1098 return FALSE;
1101 return TRUE;
1104 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1106 const struct wined3d_resource *resource = &surface->container->resource;
1107 struct wined3d_device *device = resource->device;
1108 const struct blit_shader *blitter;
1110 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_FILL,
1111 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1112 if (!blitter)
1114 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1115 return WINED3DERR_INVALIDCALL;
1118 return blitter->depth_fill(device, surface, rect, depth);
1121 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1122 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1124 struct wined3d_device *device = src_surface->resource.device;
1126 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1127 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1128 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1129 return WINED3DERR_INVALIDCALL;
1131 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1133 surface_modify_ds_location(dst_surface, dst_location,
1134 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1136 return WINED3D_OK;
1139 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1140 struct wined3d_surface *render_target)
1142 TRACE("surface %p, render_target %p.\n", surface, render_target);
1144 /* TODO: Check surface sizes, pools, etc. */
1146 if (render_target->resource.multisample_type)
1147 return WINED3DERR_INVALIDCALL;
1149 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3D_TEXF_POINT);
1152 /* Context activation is done by the caller. */
1153 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1155 GL_EXTCALL(glDeleteBuffersARB(1, &surface->pbo));
1156 checkGLcall("glDeleteBuffersARB(1, &surface->pbo)");
1158 surface->pbo = 0;
1159 surface_invalidate_location(surface, WINED3D_LOCATION_BUFFER);
1162 static ULONG surface_resource_incref(struct wined3d_resource *resource)
1164 return wined3d_surface_incref(surface_from_resource(resource));
1167 static ULONG surface_resource_decref(struct wined3d_resource *resource)
1169 return wined3d_surface_decref(surface_from_resource(resource));
1172 static void surface_unload(struct wined3d_resource *resource)
1174 struct wined3d_surface *surface = surface_from_resource(resource);
1175 struct wined3d_renderbuffer_entry *entry, *entry2;
1176 struct wined3d_device *device = resource->device;
1177 const struct wined3d_gl_info *gl_info;
1178 struct wined3d_context *context;
1180 TRACE("surface %p.\n", surface);
1182 if (resource->pool == WINED3D_POOL_DEFAULT)
1184 /* Default pool resources are supposed to be destroyed before Reset is called.
1185 * Implicit resources stay however. So this means we have an implicit render target
1186 * or depth stencil. The content may be destroyed, but we still have to tear down
1187 * opengl resources, so we cannot leave early.
1189 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1190 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1191 * or the depth stencil into an FBO the texture or render buffer will be removed
1192 * and all flags get lost */
1193 surface_prepare_system_memory(surface);
1194 memset(surface->resource.heap_memory, 0, surface->resource.size);
1195 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
1196 surface_invalidate_location(surface, ~WINED3D_LOCATION_SYSMEM);
1198 /* We also get here when the ddraw swapchain is destroyed, for example
1199 * for a mode switch. In this case this surface won't necessarily be
1200 * an implicit surface. We have to mark it lost so that the
1201 * application can restore it after the mode switch. */
1202 surface->flags |= SFLAG_LOST;
1204 else
1206 surface_prepare_map_memory(surface);
1207 surface_load_location(surface, surface->resource.map_binding);
1208 surface_invalidate_location(surface, ~surface->resource.map_binding);
1211 context = context_acquire(device, NULL);
1212 gl_info = context->gl_info;
1214 /* Destroy PBOs, but load them into real sysmem before */
1215 if (surface->pbo)
1216 surface_remove_pbo(surface, gl_info);
1218 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1219 * all application-created targets the application has to release the surface
1220 * before calling _Reset
1222 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1224 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1225 list_remove(&entry->entry);
1226 HeapFree(GetProcessHeap(), 0, entry);
1228 list_init(&surface->renderbuffers);
1229 surface->current_renderbuffer = NULL;
1231 if (surface->rb_multisample)
1233 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1234 surface->rb_multisample = 0;
1236 if (surface->rb_resolved)
1238 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1239 surface->rb_resolved = 0;
1242 context_release(context);
1244 resource_unload(resource);
1247 static const struct wined3d_resource_ops surface_resource_ops =
1249 surface_resource_incref,
1250 surface_resource_decref,
1251 surface_unload,
1254 static const struct wined3d_surface_ops surface_ops =
1256 surface_private_setup,
1257 surface_unmap,
1260 /*****************************************************************************
1261 * Initializes the GDI surface, aka creates the DIB section we render to
1262 * The DIB section creation is done by calling GetDC, which will create the
1263 * section and releasing the dc to allow the app to use it. The dib section
1264 * will stay until the surface is released
1266 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1267 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1268 * avoid confusion in the shared surface code.
1270 * Returns:
1271 * WINED3D_OK on success
1272 * The return values of called methods on failure
1274 *****************************************************************************/
1275 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1277 HRESULT hr;
1279 TRACE("surface %p.\n", surface);
1281 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1283 ERR("Overlays not yet supported by GDI surfaces.\n");
1284 return WINED3DERR_INVALIDCALL;
1287 /* Sysmem textures have memory already allocated - release it,
1288 * this avoids an unnecessary memcpy. */
1289 hr = surface_create_dib_section(surface);
1290 if (FAILED(hr))
1291 return hr;
1292 surface->resource.map_binding = WINED3D_LOCATION_DIB;
1294 /* We don't mind the nonpow2 stuff in GDI. */
1295 surface->pow2Width = surface->resource.width;
1296 surface->pow2Height = surface->resource.height;
1298 return WINED3D_OK;
1301 static void gdi_surface_unmap(struct wined3d_surface *surface)
1303 TRACE("surface %p.\n", surface);
1305 /* Tell the swapchain to update the screen. */
1306 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
1307 x11_copy_to_screen(surface->container->swapchain, &surface->lockedRect);
1309 memset(&surface->lockedRect, 0, sizeof(RECT));
1312 static const struct wined3d_surface_ops gdi_surface_ops =
1314 gdi_surface_private_setup,
1315 gdi_surface_unmap,
1318 /* This call just downloads data, the caller is responsible for binding the
1319 * correct texture. */
1320 /* Context activation is done by the caller. */
1321 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1322 DWORD dst_location)
1324 const struct wined3d_format *format = surface->resource.format;
1325 struct wined3d_bo_address data;
1327 /* Only support read back of converted P8 surfaces. */
1328 if (surface->container->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1330 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1331 return;
1334 surface_get_memory(surface, &data, dst_location);
1336 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1338 TRACE("(%p) : Calling glGetCompressedTexImageARB level %d, format %#x, type %#x, data %p.\n",
1339 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1341 if (data.buffer_object)
1343 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
1344 checkGLcall("glBindBufferARB");
1345 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
1346 checkGLcall("glGetCompressedTexImageARB");
1347 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1348 checkGLcall("glBindBufferARB");
1350 else
1352 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target,
1353 surface->texture_level, data.addr));
1354 checkGLcall("glGetCompressedTexImageARB");
1357 else
1359 void *mem;
1360 GLenum gl_format = format->glFormat;
1361 GLenum gl_type = format->glType;
1362 int src_pitch = 0;
1363 int dst_pitch = 0;
1365 if (surface->flags & SFLAG_NONPOW2)
1367 unsigned char alignment = surface->resource.device->surface_alignment;
1368 src_pitch = format->byte_count * surface->pow2Width;
1369 dst_pitch = wined3d_surface_get_pitch(surface);
1370 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
1371 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
1373 else
1375 mem = data.addr;
1378 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1379 surface, surface->texture_level, gl_format, gl_type, mem);
1381 if (data.buffer_object)
1383 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
1384 checkGLcall("glBindBufferARB");
1386 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1387 gl_format, gl_type, NULL);
1388 checkGLcall("glGetTexImage");
1390 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
1391 checkGLcall("glBindBufferARB");
1393 else
1395 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1396 gl_format, gl_type, mem);
1397 checkGLcall("glGetTexImage");
1400 if (surface->flags & SFLAG_NONPOW2)
1402 const BYTE *src_data;
1403 BYTE *dst_data;
1404 UINT y;
1406 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1407 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1408 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1410 * We're doing this...
1412 * instead of boxing the texture :
1413 * |<-texture width ->| -->pow2width| /\
1414 * |111111111111111111| | |
1415 * |222 Texture 222222| boxed empty | texture height
1416 * |3333 Data 33333333| | |
1417 * |444444444444444444| | \/
1418 * ----------------------------------- |
1419 * | boxed empty | boxed empty | pow2height
1420 * | | | \/
1421 * -----------------------------------
1424 * we're repacking the data to the expected texture width
1426 * |<-texture width ->| -->pow2width| /\
1427 * |111111111111111111222222222222222| |
1428 * |222333333333333333333444444444444| texture height
1429 * |444444 | |
1430 * | | \/
1431 * | | |
1432 * | empty | pow2height
1433 * | | \/
1434 * -----------------------------------
1436 * == is the same as
1438 * |<-texture width ->| /\
1439 * |111111111111111111|
1440 * |222222222222222222|texture height
1441 * |333333333333333333|
1442 * |444444444444444444| \/
1443 * --------------------
1445 * This also means that any references to surface memory should work with the data as if it were a
1446 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
1448 * internally the texture is still stored in a boxed format so any references to textureName will
1449 * get a boxed texture with width pow2width and not a texture of width resource.width.
1451 * Performance should not be an issue, because applications normally do not lock the surfaces when
1452 * rendering. If an app does, the WINED3D_TEXTURE_DYNAMIC_MAP flag will kick in and the memory copy
1453 * won't be released, and doesn't have to be re-read. */
1454 src_data = mem;
1455 dst_data = data.addr;
1456 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
1457 for (y = 0; y < surface->resource.height; ++y)
1459 memcpy(dst_data, src_data, dst_pitch);
1460 src_data += src_pitch;
1461 dst_data += dst_pitch;
1464 HeapFree(GetProcessHeap(), 0, mem);
1469 /* This call just uploads data, the caller is responsible for binding the
1470 * correct texture. */
1471 /* Context activation is done by the caller. */
1472 static void surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1473 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1474 BOOL srgb, const struct wined3d_bo_address *data)
1476 UINT update_w = src_rect->right - src_rect->left;
1477 UINT update_h = src_rect->bottom - src_rect->top;
1479 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1480 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1481 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1483 if (surface->resource.map_count)
1485 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
1486 surface->container->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
1489 if (format->flags & WINED3DFMT_FLAG_HEIGHT_SCALE)
1491 update_h *= format->height_scale.numerator;
1492 update_h /= format->height_scale.denominator;
1495 if (data->buffer_object)
1497 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, data->buffer_object));
1498 checkGLcall("glBindBufferARB");
1501 if (format->flags & WINED3DFMT_FLAG_COMPRESSED)
1503 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1504 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1505 const BYTE *addr = data->addr;
1506 GLenum internal;
1508 addr += (src_rect->top / format->block_height) * src_pitch;
1509 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1511 if (srgb)
1512 internal = format->glGammaInternal;
1513 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET
1514 && wined3d_resource_is_offscreen(&surface->container->resource))
1515 internal = format->rtInternal;
1516 else
1517 internal = format->glInternal;
1519 TRACE("glCompressedTexSubImage2DARB, target %#x, level %d, x %d, y %d, w %d, h %d, "
1520 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1521 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1523 if (row_length == src_pitch)
1525 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
1526 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1528 else
1530 UINT row, y;
1532 /* glCompressedTexSubImage2DARB() ignores pixel store state, so we
1533 * can't use the unpack row length like below. */
1534 for (row = 0, y = dst_point->y; row < row_count; ++row)
1536 GL_EXTCALL(glCompressedTexSubImage2DARB(surface->texture_target, surface->texture_level,
1537 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1538 y += format->block_height;
1539 addr += src_pitch;
1542 checkGLcall("glCompressedTexSubImage2DARB");
1544 else
1546 const BYTE *addr = data->addr;
1548 addr += src_rect->top * src_pitch;
1549 addr += src_rect->left * format->byte_count;
1551 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1552 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1553 update_w, update_h, format->glFormat, format->glType, addr);
1555 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1556 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1557 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1558 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1559 checkGLcall("glTexSubImage2D");
1562 if (data->buffer_object)
1564 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
1565 checkGLcall("glBindBufferARB");
1568 if (wined3d_settings.strict_draw_ordering)
1569 gl_info->gl_ops.gl.p_glFlush();
1571 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1573 struct wined3d_device *device = surface->resource.device;
1574 unsigned int i;
1576 for (i = 0; i < device->context_count; ++i)
1578 context_surface_update(device->contexts[i], surface);
1583 static BOOL surface_check_block_align(struct wined3d_surface *surface, const RECT *rect)
1585 UINT width_mask, height_mask;
1587 if (!rect->left && !rect->top
1588 && rect->right == surface->resource.width
1589 && rect->bottom == surface->resource.height)
1590 return TRUE;
1592 /* This assumes power of two block sizes, but NPOT block sizes would be
1593 * silly anyway. */
1594 width_mask = surface->resource.format->block_width - 1;
1595 height_mask = surface->resource.format->block_height - 1;
1597 if (!(rect->left & width_mask) && !(rect->top & height_mask)
1598 && !(rect->right & width_mask) && !(rect->bottom & height_mask))
1599 return TRUE;
1601 return FALSE;
1604 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1605 struct wined3d_surface *src_surface, const RECT *src_rect)
1607 const struct wined3d_format *src_format;
1608 const struct wined3d_format *dst_format;
1609 const struct wined3d_gl_info *gl_info;
1610 struct wined3d_context *context;
1611 struct wined3d_bo_address data;
1612 UINT update_w, update_h;
1613 UINT dst_w, dst_h;
1614 RECT r, dst_rect;
1615 UINT src_pitch;
1616 POINT p;
1618 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1619 dst_surface, wine_dbgstr_point(dst_point),
1620 src_surface, wine_dbgstr_rect(src_rect));
1622 src_format = src_surface->resource.format;
1623 dst_format = dst_surface->resource.format;
1625 if (src_format->id != dst_format->id)
1627 WARN("Source and destination surfaces should have the same format.\n");
1628 return WINED3DERR_INVALIDCALL;
1631 if (!dst_point)
1633 p.x = 0;
1634 p.y = 0;
1635 dst_point = &p;
1637 else if (dst_point->x < 0 || dst_point->y < 0)
1639 WARN("Invalid destination point.\n");
1640 return WINED3DERR_INVALIDCALL;
1643 if (!src_rect)
1645 r.left = 0;
1646 r.top = 0;
1647 r.right = src_surface->resource.width;
1648 r.bottom = src_surface->resource.height;
1649 src_rect = &r;
1651 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1652 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1654 WARN("Invalid source rectangle.\n");
1655 return WINED3DERR_INVALIDCALL;
1658 dst_w = dst_surface->resource.width;
1659 dst_h = dst_surface->resource.height;
1661 update_w = src_rect->right - src_rect->left;
1662 update_h = src_rect->bottom - src_rect->top;
1664 if (update_w > dst_w || dst_point->x > dst_w - update_w
1665 || update_h > dst_h || dst_point->y > dst_h - update_h)
1667 WARN("Destination out of bounds.\n");
1668 return WINED3DERR_INVALIDCALL;
1671 if ((src_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(src_surface, src_rect))
1673 WARN("Source rectangle not block-aligned.\n");
1674 return WINED3DERR_INVALIDCALL;
1677 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1678 if ((dst_format->flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(dst_surface, &dst_rect))
1680 WARN("Destination rectangle not block-aligned.\n");
1681 return WINED3DERR_INVALIDCALL;
1684 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1685 if (dst_format->convert || wined3d_format_get_color_key_conversion(dst_surface->container, FALSE))
1686 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1688 context = context_acquire(dst_surface->resource.device, NULL);
1689 gl_info = context->gl_info;
1691 /* Only load the surface for partial updates. For newly allocated texture
1692 * the texture wouldn't be the current location, and we'd upload zeroes
1693 * just to overwrite them again. */
1694 if (update_w == dst_w && update_h == dst_h)
1695 wined3d_texture_prepare_texture(dst_surface->container, context, FALSE);
1696 else
1697 surface_load_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1698 wined3d_texture_bind(dst_surface->container, context, FALSE);
1700 surface_get_memory(src_surface, &data, src_surface->locations);
1701 src_pitch = wined3d_surface_get_pitch(src_surface);
1703 surface_upload_data(dst_surface, gl_info, src_format, src_rect, src_pitch, dst_point, FALSE, &data);
1705 context_invalidate_active_texture(context);
1707 context_release(context);
1709 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1710 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1712 return WINED3D_OK;
1715 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1716 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1717 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1718 /* Context activation is done by the caller. */
1719 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1721 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
1722 struct wined3d_renderbuffer_entry *entry;
1723 GLuint renderbuffer = 0;
1724 unsigned int src_width, src_height;
1725 unsigned int width, height;
1727 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
1729 width = rt->pow2Width;
1730 height = rt->pow2Height;
1732 else
1734 width = surface->pow2Width;
1735 height = surface->pow2Height;
1738 src_width = surface->pow2Width;
1739 src_height = surface->pow2Height;
1741 /* A depth stencil smaller than the render target is not valid */
1742 if (width > src_width || height > src_height) return;
1744 /* Remove any renderbuffer set if the sizes match */
1745 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1746 || (width == src_width && height == src_height))
1748 surface->current_renderbuffer = NULL;
1749 return;
1752 /* Look if we've already got a renderbuffer of the correct dimensions */
1753 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1755 if (entry->width == width && entry->height == height)
1757 renderbuffer = entry->id;
1758 surface->current_renderbuffer = entry;
1759 break;
1763 if (!renderbuffer)
1765 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1766 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1767 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1768 surface->resource.format->glInternal, width, height);
1770 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1771 entry->width = width;
1772 entry->height = height;
1773 entry->id = renderbuffer;
1774 list_add_head(&surface->renderbuffers, &entry->entry);
1776 surface->current_renderbuffer = entry;
1779 checkGLcall("set_compatible_renderbuffer");
1782 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
1784 const struct wined3d_swapchain *swapchain = surface->container->swapchain;
1786 TRACE("surface %p.\n", surface);
1788 if (!swapchain)
1790 ERR("Surface %p is not on a swapchain.\n", surface);
1791 return GL_NONE;
1794 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface->container)
1796 if (swapchain->render_to_fbo)
1798 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
1799 return GL_COLOR_ATTACHMENT0;
1801 TRACE("Returning GL_BACK\n");
1802 return GL_BACK;
1804 else if (surface->container == swapchain->front_buffer)
1806 TRACE("Returning GL_FRONT\n");
1807 return GL_FRONT;
1810 FIXME("Higher back buffer, returning GL_BACK\n");
1811 return GL_BACK;
1814 void surface_load(struct wined3d_surface *surface, BOOL srgb)
1816 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
1817 BOOL ck_changed;
1819 TRACE("surface %p, srgb %#x.\n", surface, srgb);
1821 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
1822 ERR("Not supported on scratch surfaces.\n");
1824 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->container->color_key_flags & WINEDDSD_CKSRCBLT);
1826 /* Reload if either the texture and sysmem have different ideas about the
1827 * color key, or the actual key values changed. */
1828 if (ck_changed || ((surface->container->color_key_flags & WINEDDSD_CKSRCBLT)
1829 && (surface->gl_color_key.color_space_low_value
1830 != surface->container->src_blt_color_key.color_space_low_value
1831 || surface->gl_color_key.color_space_high_value
1832 != surface->container->src_blt_color_key.color_space_high_value)))
1834 TRACE("Reloading because of color keying\n");
1835 /* To perform the color key conversion we need a sysmem copy of
1836 * the surface. Make sure we have it. */
1838 surface_prepare_map_memory(surface);
1839 surface_load_location(surface, surface->resource.map_binding);
1840 surface_invalidate_location(surface, ~surface->resource.map_binding);
1841 /* Switching color keying on / off may change the internal format. */
1842 if (ck_changed)
1843 wined3d_texture_force_reload(surface->container);
1845 else if (!(surface->locations & location))
1847 TRACE("Reloading because surface is dirty.\n");
1849 else
1851 TRACE("surface is already in texture\n");
1852 return;
1855 surface_load_location(surface, location);
1856 surface_evict_sysmem(surface);
1859 /* See also float_16_to_32() in wined3d_private.h */
1860 static inline unsigned short float_32_to_16(const float *in)
1862 int exp = 0;
1863 float tmp = fabsf(*in);
1864 unsigned int mantissa;
1865 unsigned short ret;
1867 /* Deal with special numbers */
1868 if (*in == 0.0f)
1869 return 0x0000;
1870 if (isnan(*in))
1871 return 0x7c01;
1872 if (isinf(*in))
1873 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1875 if (tmp < powf(2, 10))
1879 tmp = tmp * 2.0f;
1880 exp--;
1881 } while (tmp < powf(2, 10));
1883 else if (tmp >= powf(2, 11))
1887 tmp /= 2.0f;
1888 exp++;
1889 } while (tmp >= powf(2, 11));
1892 mantissa = (unsigned int)tmp;
1893 if (tmp - mantissa >= 0.5f)
1894 ++mantissa; /* Round to nearest, away from zero. */
1896 exp += 10; /* Normalize the mantissa. */
1897 exp += 15; /* Exponent is encoded with excess 15. */
1899 if (exp > 30) /* too big */
1901 ret = 0x7c00; /* INF */
1903 else if (exp <= 0)
1905 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1906 while (exp <= 0)
1908 mantissa = mantissa >> 1;
1909 ++exp;
1911 ret = mantissa & 0x3ff;
1913 else
1915 ret = (exp << 10) | (mantissa & 0x3ff);
1918 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1919 return ret;
1922 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
1924 TRACE("surface %p, container %p.\n", surface, surface->container);
1926 return wined3d_texture_incref(surface->container);
1929 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
1931 TRACE("surface %p, container %p.\n", surface, surface->container);
1933 return wined3d_texture_decref(surface->container);
1936 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
1938 TRACE("surface %p.\n", surface);
1940 if (!surface->resource.device->d3d_initialized)
1942 ERR("D3D not initialized.\n");
1943 return;
1946 wined3d_texture_preload(surface->container);
1949 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
1951 TRACE("surface %p.\n", surface);
1953 return surface->resource.parent;
1956 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
1958 TRACE("surface %p.\n", surface);
1960 return &surface->resource;
1963 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
1965 TRACE("surface %p, flags %#x.\n", surface, flags);
1967 switch (flags)
1969 case WINEDDGBS_CANBLT:
1970 case WINEDDGBS_ISBLTDONE:
1971 return WINED3D_OK;
1973 default:
1974 return WINED3DERR_INVALIDCALL;
1978 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
1980 TRACE("surface %p, flags %#x.\n", surface, flags);
1982 /* XXX: DDERR_INVALIDSURFACETYPE */
1984 switch (flags)
1986 case WINEDDGFS_CANFLIP:
1987 case WINEDDGFS_ISFLIPDONE:
1988 return WINED3D_OK;
1990 default:
1991 return WINED3DERR_INVALIDCALL;
1995 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
1997 TRACE("surface %p.\n", surface);
1999 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2000 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2003 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2005 TRACE("surface %p.\n", surface);
2007 surface->flags &= ~SFLAG_LOST;
2008 return WINED3D_OK;
2011 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
2013 unsigned int alignment;
2014 DWORD pitch;
2016 TRACE("surface %p.\n", surface);
2018 if (surface->pitch)
2019 return surface->pitch;
2021 alignment = surface->resource.device->surface_alignment;
2022 pitch = wined3d_format_calculate_pitch(surface->resource.format, surface->resource.width);
2023 pitch = (pitch + alignment - 1) & ~(alignment - 1);
2025 TRACE("Returning %u.\n", pitch);
2027 return pitch;
2030 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
2032 LONG w, h;
2034 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
2036 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2038 WARN("Not an overlay surface.\n");
2039 return WINEDDERR_NOTAOVERLAYSURFACE;
2042 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
2043 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
2044 surface->overlay_destrect.left = x;
2045 surface->overlay_destrect.top = y;
2046 surface->overlay_destrect.right = x + w;
2047 surface->overlay_destrect.bottom = y + h;
2049 return WINED3D_OK;
2052 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
2054 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
2056 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2058 TRACE("Not an overlay surface.\n");
2059 return WINEDDERR_NOTAOVERLAYSURFACE;
2062 if (!surface->overlay_dest)
2064 TRACE("Overlay not visible.\n");
2065 *x = 0;
2066 *y = 0;
2067 return WINEDDERR_OVERLAYNOTVISIBLE;
2070 *x = surface->overlay_destrect.left;
2071 *y = surface->overlay_destrect.top;
2073 TRACE("Returning position %d, %d.\n", *x, *y);
2075 return WINED3D_OK;
2078 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
2079 DWORD flags, struct wined3d_surface *ref)
2081 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
2083 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2085 TRACE("Not an overlay surface.\n");
2086 return WINEDDERR_NOTAOVERLAYSURFACE;
2089 return WINED3D_OK;
2092 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2093 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2095 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2096 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2098 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2100 WARN("Not an overlay surface.\n");
2101 return WINEDDERR_NOTAOVERLAYSURFACE;
2103 else if (!dst_surface)
2105 WARN("Dest surface is NULL.\n");
2106 return WINED3DERR_INVALIDCALL;
2109 if (src_rect)
2111 surface->overlay_srcrect = *src_rect;
2113 else
2115 surface->overlay_srcrect.left = 0;
2116 surface->overlay_srcrect.top = 0;
2117 surface->overlay_srcrect.right = surface->resource.width;
2118 surface->overlay_srcrect.bottom = surface->resource.height;
2121 if (dst_rect)
2123 surface->overlay_destrect = *dst_rect;
2125 else
2127 surface->overlay_destrect.left = 0;
2128 surface->overlay_destrect.top = 0;
2129 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
2130 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
2133 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2135 surface->overlay_dest = NULL;
2136 list_remove(&surface->overlay_entry);
2139 if (flags & WINEDDOVER_SHOW)
2141 if (surface->overlay_dest != dst_surface)
2143 surface->overlay_dest = dst_surface;
2144 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2147 else if (flags & WINEDDOVER_HIDE)
2149 /* tests show that the rectangles are erased on hide */
2150 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2151 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2152 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2153 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2154 surface->overlay_dest = NULL;
2157 return WINED3D_OK;
2160 HRESULT wined3d_surface_update_desc(struct wined3d_surface *surface,
2161 const struct wined3d_gl_info *gl_info, void *mem, unsigned int pitch)
2163 struct wined3d_resource *texture_resource = &surface->container->resource;
2164 unsigned int width, height;
2165 BOOL create_dib = FALSE;
2166 DWORD valid_location = 0;
2167 HRESULT hr;
2169 if (surface->flags & SFLAG_DIBSECTION)
2171 DeleteDC(surface->hDC);
2172 DeleteObject(surface->dib.DIBsection);
2173 surface->dib.bitmap_data = NULL;
2174 surface->flags &= ~SFLAG_DIBSECTION;
2175 create_dib = TRUE;
2178 surface->locations = 0;
2179 wined3d_resource_free_sysmem(&surface->resource);
2181 width = texture_resource->width;
2182 height = texture_resource->height;
2183 surface->resource.width = width;
2184 surface->resource.height = height;
2185 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2186 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2188 surface->pow2Width = width;
2189 surface->pow2Height = height;
2191 else
2193 surface->pow2Width = surface->pow2Height = 1;
2194 while (surface->pow2Width < width)
2195 surface->pow2Width <<= 1;
2196 while (surface->pow2Height < height)
2197 surface->pow2Height <<= 1;
2200 if (surface->pow2Width != width || surface->pow2Height != height)
2201 surface->flags |= SFLAG_NONPOW2;
2202 else
2203 surface->flags &= ~SFLAG_NONPOW2;
2205 if ((surface->user_memory = mem))
2207 surface->resource.map_binding = WINED3D_LOCATION_USER_MEMORY;
2208 valid_location = WINED3D_LOCATION_USER_MEMORY;
2210 surface->pitch = pitch;
2211 surface->resource.format = texture_resource->format;
2212 surface->resource.multisample_type = texture_resource->multisample_type;
2213 surface->resource.multisample_quality = texture_resource->multisample_quality;
2214 if (surface->pitch)
2215 surface->resource.size = height * surface->pitch;
2216 else
2217 surface->resource.size = wined3d_format_calculate_size(texture_resource->format,
2218 texture_resource->device->surface_alignment, width, height, 1);
2220 /* The format might be changed to a format that needs conversion.
2221 * If the surface didn't use PBOs previously but could now, don't
2222 * change it - whatever made us not use PBOs might come back, e.g.
2223 * color keys. */
2224 if (surface->resource.map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
2225 surface->resource.map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
2227 if (create_dib)
2229 if (FAILED(hr = surface_create_dib_section(surface)))
2231 ERR("Failed to create dib section, hr %#x.\n", hr);
2232 return hr;
2234 if (!valid_location)
2235 valid_location = WINED3D_LOCATION_DIB;
2238 if (!valid_location)
2240 surface_prepare_system_memory(surface);
2241 valid_location = WINED3D_LOCATION_SYSMEM;
2244 surface_validate_location(surface, valid_location);
2246 return WINED3D_OK;
2249 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2250 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2252 unsigned short *dst_s;
2253 const float *src_f;
2254 unsigned int x, y;
2256 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2258 for (y = 0; y < h; ++y)
2260 src_f = (const float *)(src + y * pitch_in);
2261 dst_s = (unsigned short *) (dst + y * pitch_out);
2262 for (x = 0; x < w; ++x)
2264 dst_s[x] = float_32_to_16(src_f + x);
2269 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2270 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2272 static const unsigned char convert_5to8[] =
2274 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2275 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2276 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2277 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2279 static const unsigned char convert_6to8[] =
2281 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2282 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2283 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2284 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2285 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2286 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2287 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2288 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2290 unsigned int x, y;
2292 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2294 for (y = 0; y < h; ++y)
2296 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2297 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2298 for (x = 0; x < w; ++x)
2300 WORD pixel = src_line[x];
2301 dst_line[x] = 0xff000000
2302 | convert_5to8[(pixel & 0xf800) >> 11] << 16
2303 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
2304 | convert_5to8[(pixel & 0x001f)];
2309 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2310 * in both cases we're just setting the X / Alpha channel to 0xff. */
2311 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2312 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2314 unsigned int x, y;
2316 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2318 for (y = 0; y < h; ++y)
2320 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2321 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2323 for (x = 0; x < w; ++x)
2325 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2330 static inline BYTE cliptobyte(int x)
2332 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2335 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2336 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2338 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2339 unsigned int x, y;
2341 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2343 for (y = 0; y < h; ++y)
2345 const BYTE *src_line = src + y * pitch_in;
2346 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2347 for (x = 0; x < w; ++x)
2349 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2350 * C = Y - 16; D = U - 128; E = V - 128;
2351 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2352 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2353 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2354 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2355 * U and V are shared between the pixels. */
2356 if (!(x & 1)) /* For every even pixel, read new U and V. */
2358 d = (int) src_line[1] - 128;
2359 e = (int) src_line[3] - 128;
2360 r2 = 409 * e + 128;
2361 g2 = - 100 * d - 208 * e + 128;
2362 b2 = 516 * d + 128;
2364 c2 = 298 * ((int) src_line[0] - 16);
2365 dst_line[x] = 0xff000000
2366 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2367 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2368 | cliptobyte((c2 + b2) >> 8); /* blue */
2369 /* Scale RGB values to 0..255 range,
2370 * then clip them if still not in range (may be negative),
2371 * then shift them within DWORD if necessary. */
2372 src_line += 2;
2377 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2378 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2380 unsigned int x, y;
2381 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2383 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2385 for (y = 0; y < h; ++y)
2387 const BYTE *src_line = src + y * pitch_in;
2388 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2389 for (x = 0; x < w; ++x)
2391 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2392 * C = Y - 16; D = U - 128; E = V - 128;
2393 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2394 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2395 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2396 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2397 * U and V are shared between the pixels. */
2398 if (!(x & 1)) /* For every even pixel, read new U and V. */
2400 d = (int) src_line[1] - 128;
2401 e = (int) src_line[3] - 128;
2402 r2 = 409 * e + 128;
2403 g2 = - 100 * d - 208 * e + 128;
2404 b2 = 516 * d + 128;
2406 c2 = 298 * ((int) src_line[0] - 16);
2407 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2408 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2409 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2410 /* Scale RGB values to 0..255 range,
2411 * then clip them if still not in range (may be negative),
2412 * then shift them within DWORD if necessary. */
2413 src_line += 2;
2418 struct d3dfmt_converter_desc
2420 enum wined3d_format_id from, to;
2421 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2424 static const struct d3dfmt_converter_desc converters[] =
2426 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2427 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2428 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2429 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2430 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2431 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2434 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2435 enum wined3d_format_id to)
2437 unsigned int i;
2439 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2441 if (converters[i].from == from && converters[i].to == to)
2442 return &converters[i];
2445 return NULL;
2448 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2450 struct wined3d_map_desc src_map, dst_map;
2451 const struct d3dfmt_converter_desc *conv;
2452 struct wined3d_texture *ret = NULL;
2453 struct wined3d_resource_desc desc;
2454 struct wined3d_surface *dst;
2456 conv = find_converter(source->resource.format->id, to_fmt);
2457 if (!conv)
2459 FIXME("Cannot find a conversion function from format %s to %s.\n",
2460 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2461 return NULL;
2464 /* FIXME: Multisampled conversion? */
2465 wined3d_resource_get_desc(&source->resource, &desc);
2466 desc.resource_type = WINED3D_RTYPE_TEXTURE;
2467 desc.format = to_fmt;
2468 desc.usage = 0;
2469 desc.pool = WINED3D_POOL_SCRATCH;
2470 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2471 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, &wined3d_null_parent_ops, &ret)))
2473 ERR("Failed to create a destination surface for conversion.\n");
2474 return NULL;
2476 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2478 memset(&src_map, 0, sizeof(src_map));
2479 memset(&dst_map, 0, sizeof(dst_map));
2481 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2483 ERR("Failed to lock the source surface.\n");
2484 wined3d_texture_decref(ret);
2485 return NULL;
2487 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2489 ERR("Failed to lock the destination surface.\n");
2490 wined3d_surface_unmap(source);
2491 wined3d_texture_decref(ret);
2492 return NULL;
2495 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2496 source->resource.width, source->resource.height);
2498 wined3d_surface_unmap(dst);
2499 wined3d_surface_unmap(source);
2501 return ret;
2504 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2505 unsigned int bpp, UINT pitch, DWORD color)
2507 BYTE *first;
2508 unsigned int x, y;
2510 /* Do first row */
2512 #define COLORFILL_ROW(type) \
2513 do { \
2514 type *d = (type *)buf; \
2515 for (x = 0; x < width; ++x) \
2516 d[x] = (type)color; \
2517 } while(0)
2519 switch (bpp)
2521 case 1:
2522 COLORFILL_ROW(BYTE);
2523 break;
2525 case 2:
2526 COLORFILL_ROW(WORD);
2527 break;
2529 case 3:
2531 BYTE *d = buf;
2532 for (x = 0; x < width; ++x, d += 3)
2534 d[0] = (color ) & 0xff;
2535 d[1] = (color >> 8) & 0xff;
2536 d[2] = (color >> 16) & 0xff;
2538 break;
2540 case 4:
2541 COLORFILL_ROW(DWORD);
2542 break;
2544 default:
2545 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2546 return WINED3DERR_NOTAVAILABLE;
2549 #undef COLORFILL_ROW
2551 /* Now copy first row. */
2552 first = buf;
2553 for (y = 1; y < height; ++y)
2555 buf += pitch;
2556 memcpy(buf, first, width * bpp);
2559 return WINED3D_OK;
2562 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
2564 return surface_from_resource(resource);
2567 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
2569 TRACE("surface %p.\n", surface);
2571 if (!surface->resource.map_count)
2573 WARN("Trying to unmap unmapped surface.\n");
2574 return WINEDDERR_NOTLOCKED;
2576 --surface->resource.map_count;
2578 surface->surface_ops->surface_unmap(surface);
2580 return WINED3D_OK;
2583 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
2584 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
2586 const struct wined3d_format *format = surface->resource.format;
2587 struct wined3d_device *device = surface->resource.device;
2588 struct wined3d_context *context;
2589 const struct wined3d_gl_info *gl_info;
2590 BYTE *base_memory;
2592 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
2593 surface, map_desc, wine_dbgstr_rect(rect), flags);
2595 if (surface->resource.map_count)
2597 WARN("Surface is already mapped.\n");
2598 return WINED3DERR_INVALIDCALL;
2601 if ((format->flags & WINED3DFMT_FLAG_BLOCKS) && rect
2602 && !surface_check_block_align(surface, rect))
2604 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
2605 wine_dbgstr_rect(rect), format->block_width, format->block_height);
2607 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
2608 return WINED3DERR_INVALIDCALL;
2611 ++surface->resource.map_count;
2613 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
2614 WARN("Trying to lock unlockable surface.\n");
2616 /* Performance optimization: Count how often a surface is mapped, if it is
2617 * mapped regularly do not throw away the system memory copy. This avoids
2618 * the need to download the surface from OpenGL all the time. The surface
2619 * is still downloaded if the OpenGL texture is changed. Note that this
2620 * only really makes sense for managed textures.*/
2621 if (!(surface->container->flags & WINED3D_TEXTURE_DYNAMIC_MAP)
2622 && surface->resource.map_binding == WINED3D_LOCATION_SYSMEM)
2624 if (++surface->lockCount > MAXLOCKCOUNT)
2626 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
2627 surface->container->flags |= WINED3D_TEXTURE_DYNAMIC_MAP;
2631 surface_prepare_map_memory(surface);
2632 if (flags & WINED3D_MAP_DISCARD)
2634 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
2635 wined3d_debug_location(surface->resource.map_binding));
2636 surface_validate_location(surface, surface->resource.map_binding);
2638 else
2640 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
2641 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
2643 surface_load_location(surface, surface->resource.map_binding);
2646 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
2647 surface_invalidate_location(surface, ~surface->resource.map_binding);
2649 switch (surface->resource.map_binding)
2651 case WINED3D_LOCATION_SYSMEM:
2652 base_memory = surface->resource.heap_memory;
2653 break;
2655 case WINED3D_LOCATION_USER_MEMORY:
2656 base_memory = surface->user_memory;
2657 break;
2659 case WINED3D_LOCATION_DIB:
2660 base_memory = surface->dib.bitmap_data;
2661 break;
2663 case WINED3D_LOCATION_BUFFER:
2664 context = context_acquire(device, NULL);
2665 gl_info = context->gl_info;
2667 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, surface->pbo));
2668 base_memory = GL_EXTCALL(glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_READ_WRITE_ARB));
2669 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
2670 checkGLcall("map PBO");
2672 context_release(context);
2673 break;
2675 default:
2676 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
2677 base_memory = NULL;
2680 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
2681 map_desc->row_pitch = surface->resource.width * format->byte_count;
2682 else
2683 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
2684 map_desc->slice_pitch = 0;
2686 if (!rect)
2688 map_desc->data = base_memory;
2689 surface->lockedRect.left = 0;
2690 surface->lockedRect.top = 0;
2691 surface->lockedRect.right = surface->resource.width;
2692 surface->lockedRect.bottom = surface->resource.height;
2694 else
2696 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
2698 /* Compressed textures are block based, so calculate the offset of
2699 * the block that contains the top-left pixel of the locked rectangle. */
2700 map_desc->data = base_memory
2701 + ((rect->top / format->block_height) * map_desc->row_pitch)
2702 + ((rect->left / format->block_width) * format->block_byte_count);
2704 else
2706 map_desc->data = base_memory
2707 + (map_desc->row_pitch * rect->top)
2708 + (rect->left * format->byte_count);
2710 surface->lockedRect.left = rect->left;
2711 surface->lockedRect.top = rect->top;
2712 surface->lockedRect.right = rect->right;
2713 surface->lockedRect.bottom = rect->bottom;
2716 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
2717 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
2719 return WINED3D_OK;
2722 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
2724 HRESULT hr;
2726 TRACE("surface %p, dc %p.\n", surface, dc);
2728 /* Give more detailed info for ddraw. */
2729 if (surface->flags & SFLAG_DCINUSE)
2730 return WINEDDERR_DCALREADYCREATED;
2732 /* Can't GetDC if the surface is locked. */
2733 if (surface->resource.map_count)
2734 return WINED3DERR_INVALIDCALL;
2736 /* Create a DIB section if there isn't a dc yet. */
2737 if (!surface->hDC)
2739 if (surface->flags & SFLAG_CLIENT)
2741 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
2742 surface_release_client_storage(surface);
2744 hr = surface_create_dib_section(surface);
2745 if (FAILED(hr))
2746 return WINED3DERR_INVALIDCALL;
2747 if (!(surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2748 || surface->container->flags & WINED3D_TEXTURE_PIN_SYSMEM
2749 || surface->pbo))
2750 surface->resource.map_binding = WINED3D_LOCATION_DIB;
2753 surface_load_location(surface, WINED3D_LOCATION_DIB);
2754 surface_invalidate_location(surface, ~WINED3D_LOCATION_DIB);
2756 surface->flags |= SFLAG_DCINUSE;
2757 surface->resource.map_count++;
2759 *dc = surface->hDC;
2760 TRACE("Returning dc %p.\n", *dc);
2762 return WINED3D_OK;
2765 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
2767 TRACE("surface %p, dc %p.\n", surface, dc);
2769 if (!(surface->flags & SFLAG_DCINUSE))
2770 return WINEDDERR_NODC;
2772 if (surface->hDC != dc)
2774 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
2775 dc, surface->hDC);
2776 return WINEDDERR_NODC;
2779 surface->resource.map_count--;
2780 surface->flags &= ~SFLAG_DCINUSE;
2782 if (surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2783 || (surface->container->flags & WINED3D_TEXTURE_PIN_SYSMEM
2784 && surface->resource.map_binding != WINED3D_LOCATION_DIB))
2786 /* The game Salammbo modifies the surface contents without mapping the surface between
2787 * a GetDC/ReleaseDC operation and flipping the surface. If the DIB remains the active
2788 * copy and is copied to the screen, this update, which draws the mouse pointer, is lost.
2789 * Do not only copy the DIB to the map location, but also make sure the map location is
2790 * copied back to the DIB in the next getdc call.
2792 * The same consideration applies to user memory surfaces. */
2793 surface_load_location(surface, surface->resource.map_binding);
2794 surface_invalidate_location(surface, WINED3D_LOCATION_DIB);
2797 return WINED3D_OK;
2800 static void read_from_framebuffer(struct wined3d_surface *surface, DWORD dst_location)
2802 struct wined3d_device *device = surface->resource.device;
2803 const struct wined3d_gl_info *gl_info;
2804 struct wined3d_context *context;
2805 BYTE *mem;
2806 BYTE *row, *top, *bottom;
2807 int i;
2808 BOOL srcIsUpsideDown;
2809 struct wined3d_bo_address data;
2811 surface_get_memory(surface, &data, dst_location);
2813 context = context_acquire(device, surface);
2814 context_apply_blit_state(context, device);
2815 gl_info = context->gl_info;
2817 /* Select the correct read buffer, and give some debug output.
2818 * There is no need to keep track of the current read buffer or reset it, every part of the code
2819 * that reads sets the read buffer as desired.
2821 if (wined3d_resource_is_offscreen(&surface->container->resource))
2823 /* Mapping the primary render target which is not on a swapchain.
2824 * Read from the back buffer. */
2825 TRACE("Mapping offscreen render target.\n");
2826 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2827 srcIsUpsideDown = TRUE;
2829 else
2831 /* Onscreen surfaces are always part of a swapchain */
2832 GLenum buffer = surface_get_gl_buffer(surface);
2833 TRACE("Mapping %#x buffer.\n", buffer);
2834 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
2835 checkGLcall("glReadBuffer");
2836 srcIsUpsideDown = FALSE;
2839 if (data.buffer_object)
2841 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, data.buffer_object));
2842 checkGLcall("glBindBufferARB");
2845 /* Setup pixel store pack state -- to glReadPixels into the correct place */
2846 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
2847 checkGLcall("glPixelStorei");
2849 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
2850 surface->resource.width, surface->resource.height,
2851 surface->resource.format->glFormat,
2852 surface->resource.format->glType, data.addr);
2853 checkGLcall("glReadPixels");
2855 /* Reset previous pixel store pack state */
2856 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
2857 checkGLcall("glPixelStorei");
2859 if (!srcIsUpsideDown)
2861 /* glReadPixels returns the image upside down, and there is no way to prevent this.
2862 * Flip the lines in software. */
2863 UINT pitch = wined3d_surface_get_pitch(surface);
2865 if (!(row = HeapAlloc(GetProcessHeap(), 0, pitch)))
2866 goto error;
2868 if (data.buffer_object)
2870 mem = GL_EXTCALL(glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB));
2871 checkGLcall("glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_WRITE_ARB)");
2873 else
2874 mem = data.addr;
2876 top = mem;
2877 bottom = mem + pitch * (surface->resource.height - 1);
2878 for (i = 0; i < surface->resource.height / 2; i++)
2880 memcpy(row, top, pitch);
2881 memcpy(top, bottom, pitch);
2882 memcpy(bottom, row, pitch);
2883 top += pitch;
2884 bottom -= pitch;
2886 HeapFree(GetProcessHeap(), 0, row);
2888 if (data.buffer_object)
2889 GL_EXTCALL(glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB));
2892 error:
2893 if (data.buffer_object)
2895 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
2896 checkGLcall("glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0)");
2899 context_release(context);
2902 /* Read the framebuffer contents into a texture. Note that this function
2903 * doesn't do any kind of flipping. Using this on an onscreen surface will
2904 * result in a flipped D3D texture. */
2905 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
2907 struct wined3d_device *device = surface->resource.device;
2908 const struct wined3d_gl_info *gl_info;
2909 struct wined3d_context *context;
2911 context = context_acquire(device, surface);
2912 gl_info = context->gl_info;
2913 device_invalidate_state(device, STATE_FRAMEBUFFER);
2915 wined3d_texture_prepare_texture(surface->container, context, srgb);
2916 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
2918 TRACE("Reading back offscreen render target %p.\n", surface);
2920 if (wined3d_resource_is_offscreen(&surface->container->resource))
2921 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2922 else
2923 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
2924 checkGLcall("glReadBuffer");
2926 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
2927 0, 0, 0, 0, surface->resource.width, surface->resource.height);
2928 checkGLcall("glCopyTexSubImage2D");
2930 context_release(context);
2933 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
2935 if (multisample)
2937 if (surface->rb_multisample)
2938 return;
2940 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
2941 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
2942 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
2943 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
2944 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
2946 else
2948 if (surface->rb_resolved)
2949 return;
2951 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
2952 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
2953 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
2954 surface->pow2Width, surface->pow2Height);
2955 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
2959 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
2961 if (front->container->level_count != 1 || front->container->layer_count != 1
2962 || back->container->level_count != 1 || back->container->layer_count != 1)
2963 ERR("Flip between surfaces %p and %p not supported.\n", front, back);
2965 /* Flip the surface contents */
2966 /* Flip the DC */
2968 HDC tmp;
2969 tmp = front->hDC;
2970 front->hDC = back->hDC;
2971 back->hDC = tmp;
2974 /* Flip the DIBsection */
2976 HBITMAP tmp = front->dib.DIBsection;
2977 front->dib.DIBsection = back->dib.DIBsection;
2978 back->dib.DIBsection = tmp;
2981 /* Flip the surface data */
2983 void* tmp;
2985 tmp = front->dib.bitmap_data;
2986 front->dib.bitmap_data = back->dib.bitmap_data;
2987 back->dib.bitmap_data = tmp;
2989 tmp = front->resource.heap_memory;
2990 front->resource.heap_memory = back->resource.heap_memory;
2991 back->resource.heap_memory = tmp;
2994 /* Flip the PBO */
2996 GLuint tmp_pbo = front->pbo;
2997 front->pbo = back->pbo;
2998 back->pbo = tmp_pbo;
3001 /* Flip the opengl texture */
3003 GLuint tmp;
3005 tmp = back->container->texture_rgb.name;
3006 back->container->texture_rgb.name = front->container->texture_rgb.name;
3007 front->container->texture_rgb.name = tmp;
3009 tmp = back->container->texture_srgb.name;
3010 back->container->texture_srgb.name = front->container->texture_srgb.name;
3011 front->container->texture_srgb.name = tmp;
3013 tmp = back->rb_multisample;
3014 back->rb_multisample = front->rb_multisample;
3015 front->rb_multisample = tmp;
3017 tmp = back->rb_resolved;
3018 back->rb_resolved = front->rb_resolved;
3019 front->rb_resolved = tmp;
3021 resource_unload(&back->resource);
3022 resource_unload(&front->resource);
3026 DWORD tmp_flags = back->flags;
3027 back->flags = front->flags;
3028 front->flags = tmp_flags;
3030 tmp_flags = back->locations;
3031 back->locations = front->locations;
3032 front->locations = tmp_flags;
3036 /* Does a direct frame buffer -> texture copy. Stretching is done with single
3037 * pixel copy calls. */
3038 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3039 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3041 struct wined3d_device *device = dst_surface->resource.device;
3042 const struct wined3d_gl_info *gl_info;
3043 float xrel, yrel;
3044 struct wined3d_context *context;
3045 BOOL upsidedown = FALSE;
3046 RECT dst_rect = *dst_rect_in;
3048 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3049 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3051 if(dst_rect.top > dst_rect.bottom) {
3052 UINT tmp = dst_rect.bottom;
3053 dst_rect.bottom = dst_rect.top;
3054 dst_rect.top = tmp;
3055 upsidedown = TRUE;
3058 context = context_acquire(device, src_surface);
3059 gl_info = context->gl_info;
3060 context_apply_blit_state(context, device);
3061 wined3d_texture_load(dst_surface->container, context, FALSE);
3063 /* Bind the target texture */
3064 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
3065 if (wined3d_resource_is_offscreen(&src_surface->container->resource))
3067 TRACE("Reading from an offscreen target\n");
3068 upsidedown = !upsidedown;
3069 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3071 else
3073 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3075 checkGLcall("glReadBuffer");
3077 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
3078 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
3080 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3082 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3084 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3085 ERR("Texture filtering not supported in direct blit.\n");
3087 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3088 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3090 ERR("Texture filtering not supported in direct blit\n");
3093 if (upsidedown
3094 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3095 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3097 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
3098 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3099 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
3100 src_rect->left, src_surface->resource.height - src_rect->bottom,
3101 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3103 else
3105 LONG row;
3106 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
3107 /* I have to process this row by row to swap the image,
3108 * otherwise it would be upside down, so stretching in y direction
3109 * doesn't cost extra time
3111 * However, stretching in x direction can be avoided if not necessary
3113 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
3114 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3116 /* Well, that stuff works, but it's very slow.
3117 * find a better way instead
3119 LONG col;
3121 for (col = dst_rect.left; col < dst_rect.right; ++col)
3123 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3124 dst_rect.left + col /* x offset */, row /* y offset */,
3125 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3128 else
3130 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3131 dst_rect.left /* x offset */, row /* y offset */,
3132 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
3136 checkGLcall("glCopyTexSubImage2D");
3138 context_release(context);
3140 /* The texture is now most up to date - If the surface is a render target
3141 * and has a drawable, this path is never entered. */
3142 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3143 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3146 /* Uses the hardware to stretch and flip the image */
3147 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3148 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3150 struct wined3d_device *device = dst_surface->resource.device;
3151 GLuint src, backup = 0;
3152 float left, right, top, bottom; /* Texture coordinates */
3153 UINT fbwidth = src_surface->resource.width;
3154 UINT fbheight = src_surface->resource.height;
3155 const struct wined3d_gl_info *gl_info;
3156 struct wined3d_context *context;
3157 GLenum drawBuffer = GL_BACK;
3158 GLenum texture_target;
3159 BOOL noBackBufferBackup;
3160 BOOL src_offscreen;
3161 BOOL upsidedown = FALSE;
3162 RECT dst_rect = *dst_rect_in;
3164 TRACE("Using hwstretch blit\n");
3165 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3166 context = context_acquire(device, src_surface);
3167 gl_info = context->gl_info;
3168 context_apply_blit_state(context, device);
3169 wined3d_texture_load(dst_surface->container, context, FALSE);
3171 src_offscreen = wined3d_resource_is_offscreen(&src_surface->container->resource);
3172 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3173 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
3175 /* Get it a description */
3176 wined3d_texture_load(src_surface->container, context, FALSE);
3179 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3180 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3182 if (context->aux_buffers >= 2)
3184 /* Got more than one aux buffer? Use the 2nd aux buffer */
3185 drawBuffer = GL_AUX1;
3187 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3189 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3190 drawBuffer = GL_AUX0;
3193 if (noBackBufferBackup)
3195 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
3196 checkGLcall("glGenTextures");
3197 context_bind_texture(context, GL_TEXTURE_2D, backup);
3198 texture_target = GL_TEXTURE_2D;
3200 else
3202 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3203 * we are reading from the back buffer, the backup can be used as source texture
3205 texture_target = src_surface->texture_target;
3206 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
3207 gl_info->gl_ops.gl.p_glEnable(texture_target);
3208 checkGLcall("glEnable(texture_target)");
3210 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3211 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
3214 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3215 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3217 if(dst_rect.top > dst_rect.bottom) {
3218 UINT tmp = dst_rect.bottom;
3219 dst_rect.bottom = dst_rect.top;
3220 dst_rect.top = tmp;
3221 upsidedown = TRUE;
3224 if (src_offscreen)
3226 TRACE("Reading from an offscreen target\n");
3227 upsidedown = !upsidedown;
3228 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3230 else
3232 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3235 /* TODO: Only back up the part that will be overwritten */
3236 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
3238 checkGLcall("glCopyTexSubImage2D");
3240 /* No issue with overriding these - the sampler is dirty due to blit usage */
3241 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3242 wined3d_gl_mag_filter(magLookup, filter));
3243 checkGLcall("glTexParameteri");
3244 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3245 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
3246 checkGLcall("glTexParameteri");
3248 if (!src_surface->container->swapchain
3249 || src_surface->container == src_surface->container->swapchain->back_buffers[0])
3251 src = backup ? backup : src_surface->container->texture_rgb.name;
3253 else
3255 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
3256 checkGLcall("glReadBuffer(GL_FRONT)");
3258 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
3259 checkGLcall("glGenTextures(1, &src)");
3260 context_bind_texture(context, GL_TEXTURE_2D, src);
3262 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3263 * out for power of 2 sizes
3265 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
3266 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3267 checkGLcall("glTexImage2D");
3268 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
3270 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3271 checkGLcall("glTexParameteri");
3272 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3273 checkGLcall("glTexParameteri");
3275 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
3276 checkGLcall("glReadBuffer(GL_BACK)");
3278 if (texture_target != GL_TEXTURE_2D)
3280 gl_info->gl_ops.gl.p_glDisable(texture_target);
3281 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3282 texture_target = GL_TEXTURE_2D;
3285 checkGLcall("glEnd and previous");
3287 left = src_rect->left;
3288 right = src_rect->right;
3290 if (!upsidedown)
3292 top = src_surface->resource.height - src_rect->top;
3293 bottom = src_surface->resource.height - src_rect->bottom;
3295 else
3297 top = src_surface->resource.height - src_rect->bottom;
3298 bottom = src_surface->resource.height - src_rect->top;
3301 if (src_surface->container->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
3303 left /= src_surface->pow2Width;
3304 right /= src_surface->pow2Width;
3305 top /= src_surface->pow2Height;
3306 bottom /= src_surface->pow2Height;
3309 /* draw the source texture stretched and upside down. The correct surface is bound already */
3310 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3311 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3313 context_set_draw_buffer(context, drawBuffer);
3314 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
3316 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3317 /* bottom left */
3318 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
3319 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3321 /* top left */
3322 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
3323 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
3325 /* top right */
3326 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
3327 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3329 /* bottom right */
3330 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
3331 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
3332 gl_info->gl_ops.gl.p_glEnd();
3333 checkGLcall("glEnd and previous");
3335 if (texture_target != dst_surface->texture_target)
3337 gl_info->gl_ops.gl.p_glDisable(texture_target);
3338 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
3339 texture_target = dst_surface->texture_target;
3342 /* Now read the stretched and upside down image into the destination texture */
3343 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
3344 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
3346 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
3347 0, 0, /* We blitted the image to the origin */
3348 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3349 checkGLcall("glCopyTexSubImage2D");
3351 if (drawBuffer == GL_BACK)
3353 /* Write the back buffer backup back. */
3354 if (backup)
3356 if (texture_target != GL_TEXTURE_2D)
3358 gl_info->gl_ops.gl.p_glDisable(texture_target);
3359 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3360 texture_target = GL_TEXTURE_2D;
3362 context_bind_texture(context, GL_TEXTURE_2D, backup);
3364 else
3366 if (texture_target != src_surface->texture_target)
3368 gl_info->gl_ops.gl.p_glDisable(texture_target);
3369 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
3370 texture_target = src_surface->texture_target;
3372 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
3375 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3376 /* top left */
3377 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
3378 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
3380 /* bottom left */
3381 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
3382 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3384 /* bottom right */
3385 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
3386 (float)fbheight / (float)src_surface->pow2Height);
3387 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
3389 /* top right */
3390 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
3391 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
3392 gl_info->gl_ops.gl.p_glEnd();
3394 gl_info->gl_ops.gl.p_glDisable(texture_target);
3395 checkGLcall("glDisable(texture_target)");
3397 /* Cleanup */
3398 if (src != src_surface->container->texture_rgb.name && src != backup)
3400 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
3401 checkGLcall("glDeleteTextures(1, &src)");
3403 if (backup)
3405 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
3406 checkGLcall("glDeleteTextures(1, &backup)");
3409 if (wined3d_settings.strict_draw_ordering)
3410 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3412 context_release(context);
3414 /* The texture is now most up to date - If the surface is a render target
3415 * and has a drawable, this path is never entered. */
3416 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3417 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3420 /* Front buffer coordinates are always full screen coordinates, but our GL
3421 * drawable is limited to the window's client area. The sysmem and texture
3422 * copies do have the full screen size. Note that GL has a bottom-left
3423 * origin, while D3D has a top-left origin. */
3424 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
3426 UINT drawable_height;
3428 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
3430 POINT offset = {0, 0};
3431 RECT windowsize;
3433 ScreenToClient(window, &offset);
3434 OffsetRect(rect, offset.x, offset.y);
3436 GetClientRect(window, &windowsize);
3437 drawable_height = windowsize.bottom - windowsize.top;
3439 else
3441 drawable_height = surface->resource.height;
3444 rect->top = drawable_height - rect->top;
3445 rect->bottom = drawable_height - rect->bottom;
3448 static void surface_blt_to_drawable(const struct wined3d_device *device,
3449 enum wined3d_texture_filter_type filter, BOOL alpha_test,
3450 struct wined3d_surface *src_surface, const RECT *src_rect_in,
3451 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
3453 const struct wined3d_gl_info *gl_info;
3454 struct wined3d_context *context;
3455 RECT src_rect, dst_rect;
3457 src_rect = *src_rect_in;
3458 dst_rect = *dst_rect_in;
3460 context = context_acquire(device, dst_surface);
3461 gl_info = context->gl_info;
3463 /* Make sure the surface is up-to-date. This should probably use
3464 * surface_load_location() and worry about the destination surface too,
3465 * unless we're overwriting it completely. */
3466 wined3d_texture_load(src_surface->container, context, FALSE);
3468 /* Activate the destination context, set it up for blitting */
3469 context_apply_blit_state(context, device);
3471 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
3472 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
3474 device->blitter->set_shader(device->blit_priv, context, src_surface);
3476 if (alpha_test)
3478 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
3479 checkGLcall("glEnable(GL_ALPHA_TEST)");
3481 /* For P8 surfaces, the alpha component contains the palette index.
3482 * Which means that the colorkey is one of the palette entries. In
3483 * other cases pixels that should be masked away have alpha set to 0. */
3484 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
3485 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
3486 (float)src_surface->container->src_blt_color_key.color_space_low_value / 256.0f);
3487 else
3488 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
3489 checkGLcall("glAlphaFunc");
3491 else
3493 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3494 checkGLcall("glDisable(GL_ALPHA_TEST)");
3497 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
3499 if (alpha_test)
3501 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3502 checkGLcall("glDisable(GL_ALPHA_TEST)");
3505 /* Leave the opengl state valid for blitting */
3506 device->blitter->unset_shader(context->gl_info);
3508 if (wined3d_settings.strict_draw_ordering
3509 || (dst_surface->container->swapchain
3510 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
3511 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3513 context_release(context);
3516 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
3518 struct wined3d_device *device = s->resource.device;
3519 const struct blit_shader *blitter;
3521 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
3522 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
3523 if (!blitter)
3525 FIXME("No blitter is capable of performing the requested color fill operation.\n");
3526 return WINED3DERR_INVALIDCALL;
3529 return blitter->color_fill(device, s, rect, color);
3532 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3533 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
3534 enum wined3d_texture_filter_type filter)
3536 struct wined3d_device *device = dst_surface->resource.device;
3537 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
3538 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
3539 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3541 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
3542 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3543 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
3545 /* Get the swapchain. One of the surfaces has to be a primary surface */
3546 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3548 WARN("Destination is in sysmem, rejecting gl blt\n");
3549 return WINED3DERR_INVALIDCALL;
3552 dst_swapchain = dst_surface->container->swapchain;
3554 if (src_surface)
3556 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3558 WARN("Src is in sysmem, rejecting gl blt\n");
3559 return WINED3DERR_INVALIDCALL;
3562 src_swapchain = src_surface->container->swapchain;
3564 else
3566 src_swapchain = NULL;
3569 /* Early sort out of cases where no render target is used */
3570 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
3572 TRACE("No surface is render target, not using hardware blit.\n");
3573 return WINED3DERR_INVALIDCALL;
3576 /* No destination color keying supported */
3577 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
3579 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3580 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3581 return WINED3DERR_INVALIDCALL;
3584 if (dst_swapchain && dst_swapchain == src_swapchain)
3586 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3587 return WINED3DERR_INVALIDCALL;
3590 if (dst_swapchain && src_swapchain)
3592 FIXME("Implement hardware blit between two different swapchains\n");
3593 return WINED3DERR_INVALIDCALL;
3596 if (dst_swapchain)
3598 /* Handled with regular texture -> swapchain blit */
3599 if (src_surface == rt)
3600 TRACE("Blit from active render target to a swapchain\n");
3602 else if (src_swapchain && dst_surface == rt)
3604 FIXME("Implement blit from a swapchain to the active render target\n");
3605 return WINED3DERR_INVALIDCALL;
3608 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
3610 /* Blit from render target to texture */
3611 BOOL stretchx;
3613 /* P8 read back is not implemented */
3614 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
3615 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
3617 TRACE("P8 read back not supported by frame buffer to texture blit\n");
3618 return WINED3DERR_INVALIDCALL;
3621 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3623 TRACE("Color keying not supported by frame buffer to texture blit\n");
3624 return WINED3DERR_INVALIDCALL;
3625 /* Destination color key is checked above */
3628 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
3629 stretchx = TRUE;
3630 else
3631 stretchx = FALSE;
3633 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3634 * flip the image nor scale it.
3636 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3637 * -> If the app wants an image width an unscaled width, copy it line per line
3638 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
3639 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3640 * back buffer. This is slower than reading line per line, thus not used for flipping
3641 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3642 * pixel by pixel. */
3643 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
3644 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
3646 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
3647 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
3649 else
3651 TRACE("Using hardware stretching to flip / stretch the texture.\n");
3652 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
3655 surface_evict_sysmem(dst_surface);
3657 return WINED3D_OK;
3659 else if (src_surface)
3661 /* Blit from offscreen surface to render target */
3662 struct wined3d_color_key old_blt_key = src_surface->container->src_blt_color_key;
3663 DWORD old_color_key_flags = src_surface->container->color_key_flags;
3665 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
3667 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3668 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
3669 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
3671 FIXME("Unsupported blit operation falling back to software\n");
3672 return WINED3DERR_INVALIDCALL;
3675 /* Color keying: Check if we have to do a color keyed blt,
3676 * and if not check if a color key is activated.
3678 * Just modify the color keying parameters in the surface and restore them afterwards
3679 * The surface keeps track of the color key last used to load the opengl surface.
3680 * PreLoad will catch the change to the flags and color key and reload if necessary.
3682 if (flags & WINEDDBLT_KEYSRC)
3684 /* Use color key from surface */
3686 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
3688 /* Use color key from DDBltFx */
3689 wined3d_texture_set_color_key(src_surface->container, WINEDDSD_CKSRCBLT, &DDBltFx->ddckSrcColorkey);
3691 else
3693 /* Do not use color key */
3694 wined3d_texture_set_color_key(src_surface->container, WINEDDSD_CKSRCBLT, NULL);
3697 surface_blt_to_drawable(device, filter,
3698 flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_ALPHATEST),
3699 src_surface, src_rect, dst_surface, dst_rect);
3701 /* Restore the color key parameters */
3702 wined3d_texture_set_color_key(src_surface->container, WINEDDSD_CKSRCBLT,
3703 (old_color_key_flags & WINEDDSD_CKSRCBLT) ? &old_blt_key : NULL);
3705 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
3706 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
3708 return WINED3D_OK;
3711 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3712 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3713 return WINED3DERR_INVALIDCALL;
3716 /* Context activation is done by the caller. */
3717 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
3718 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
3720 struct wined3d_device *device = surface->resource.device;
3721 const struct wined3d_gl_info *gl_info = context->gl_info;
3722 GLint compare_mode = GL_NONE;
3723 struct blt_info info;
3724 GLint old_binding = 0;
3725 RECT rect;
3727 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
3729 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
3730 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
3731 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3732 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
3733 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
3734 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
3735 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
3736 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
3737 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
3738 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
3739 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
3741 SetRect(&rect, 0, h, w, 0);
3742 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
3743 context_active_texture(context, context->gl_info, 0);
3744 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
3745 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
3746 if (gl_info->supported[ARB_SHADOW])
3748 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
3749 if (compare_mode != GL_NONE)
3750 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
3753 device->shader_backend->shader_select_depth_blt(device->shader_priv,
3754 gl_info, info.tex_type, &surface->ds_current_size);
3756 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
3757 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
3758 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
3759 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
3760 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
3761 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
3762 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
3763 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
3764 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
3765 gl_info->gl_ops.gl.p_glEnd();
3767 if (compare_mode != GL_NONE)
3768 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
3769 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
3771 gl_info->gl_ops.gl.p_glPopAttrib();
3773 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
3776 void surface_modify_ds_location(struct wined3d_surface *surface,
3777 DWORD location, UINT w, UINT h)
3779 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
3781 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
3782 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
3783 wined3d_texture_set_dirty(surface->container);
3785 surface->ds_current_size.cx = w;
3786 surface->ds_current_size.cy = h;
3787 surface->locations = location;
3790 /* Context activation is done by the caller. */
3791 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3793 const struct wined3d_gl_info *gl_info = context->gl_info;
3794 struct wined3d_device *device = surface->resource.device;
3795 GLsizei w, h;
3797 TRACE("surface %p, new location %#x.\n", surface, location);
3799 /* TODO: Make this work for modes other than FBO */
3800 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
3802 if (!(surface->locations & location))
3804 w = surface->ds_current_size.cx;
3805 h = surface->ds_current_size.cy;
3806 surface->ds_current_size.cx = 0;
3807 surface->ds_current_size.cy = 0;
3809 else
3811 w = surface->resource.width;
3812 h = surface->resource.height;
3815 if (surface->ds_current_size.cx == surface->resource.width
3816 && surface->ds_current_size.cy == surface->resource.height)
3818 TRACE("Location (%#x) is already up to date.\n", location);
3819 return;
3822 if (surface->current_renderbuffer)
3824 FIXME("Not supported with fixed up depth stencil.\n");
3825 return;
3828 if (surface->locations & WINED3D_LOCATION_DISCARDED)
3830 TRACE("Surface was discarded, no need copy data.\n");
3831 switch (location)
3833 case WINED3D_LOCATION_TEXTURE_RGB:
3834 wined3d_texture_prepare_texture(surface->container, context, FALSE);
3835 break;
3836 case WINED3D_LOCATION_RB_MULTISAMPLE:
3837 surface_prepare_rb(surface, gl_info, TRUE);
3838 break;
3839 case WINED3D_LOCATION_DRAWABLE:
3840 /* Nothing to do */
3841 break;
3842 default:
3843 FIXME("Unhandled location %#x\n", location);
3845 surface->locations &= ~WINED3D_LOCATION_DISCARDED;
3846 surface->locations |= location;
3847 surface->ds_current_size.cx = surface->resource.width;
3848 surface->ds_current_size.cy = surface->resource.height;
3849 return;
3852 if (!surface->locations)
3854 FIXME("No up to date depth stencil location.\n");
3855 surface->locations |= location;
3856 surface->ds_current_size.cx = surface->resource.width;
3857 surface->ds_current_size.cy = surface->resource.height;
3858 return;
3861 if (location == WINED3D_LOCATION_TEXTURE_RGB)
3863 GLint old_binding = 0;
3864 GLenum bind_target;
3866 /* The render target is allowed to be smaller than the depth/stencil
3867 * buffer, so the onscreen depth/stencil buffer is potentially smaller
3868 * than the offscreen surface. Don't overwrite the offscreen surface
3869 * with undefined data. */
3870 w = min(w, context->swapchain->desc.backbuffer_width);
3871 h = min(h, context->swapchain->desc.backbuffer_height);
3873 TRACE("Copying onscreen depth buffer to depth texture.\n");
3875 if (!device->depth_blt_texture)
3876 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
3878 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
3879 * directly on the FBO texture. That's because we need to flip. */
3880 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3881 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3882 NULL, WINED3D_LOCATION_DRAWABLE);
3883 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
3885 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
3886 bind_target = GL_TEXTURE_RECTANGLE_ARB;
3888 else
3890 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
3891 bind_target = GL_TEXTURE_2D;
3893 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
3894 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
3895 * internal format, because the internal format might include stencil
3896 * data. In principle we should copy stencil data as well, but unless
3897 * the driver supports stencil export it's hard to do, and doesn't
3898 * seem to be needed in practice. If the hardware doesn't support
3899 * writing stencil data, the glCopyTexImage2D() call might trigger
3900 * software fallbacks. */
3901 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
3902 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3903 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3904 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3905 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3906 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
3907 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
3908 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
3910 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3911 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
3912 context_set_draw_buffer(context, GL_NONE);
3914 /* Do the actual blit */
3915 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
3916 checkGLcall("depth_blt");
3918 context_invalidate_state(context, STATE_FRAMEBUFFER);
3920 if (wined3d_settings.strict_draw_ordering)
3921 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3923 else if (location == WINED3D_LOCATION_DRAWABLE)
3925 TRACE("Copying depth texture to onscreen depth buffer.\n");
3927 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3928 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3929 NULL, WINED3D_LOCATION_DRAWABLE);
3930 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
3931 0, surface->pow2Height - h, w, h, surface->texture_target);
3932 checkGLcall("depth_blt");
3934 context_invalidate_state(context, STATE_FRAMEBUFFER);
3936 if (wined3d_settings.strict_draw_ordering)
3937 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3939 else
3941 ERR("Invalid location (%#x) specified.\n", location);
3944 surface->locations |= location;
3945 surface->ds_current_size.cx = surface->resource.width;
3946 surface->ds_current_size.cy = surface->resource.height;
3949 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
3951 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3953 surface->locations |= location;
3956 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
3958 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3960 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3961 wined3d_texture_set_dirty(surface->container);
3962 surface->locations &= ~location;
3964 if (!surface->locations)
3965 ERR("Surface %p does not have any up to date location.\n", surface);
3968 static DWORD resource_access_from_location(DWORD location)
3970 switch (location)
3972 case WINED3D_LOCATION_SYSMEM:
3973 case WINED3D_LOCATION_USER_MEMORY:
3974 case WINED3D_LOCATION_DIB:
3975 case WINED3D_LOCATION_BUFFER:
3976 return WINED3D_RESOURCE_ACCESS_CPU;
3978 case WINED3D_LOCATION_DRAWABLE:
3979 case WINED3D_LOCATION_TEXTURE_SRGB:
3980 case WINED3D_LOCATION_TEXTURE_RGB:
3981 case WINED3D_LOCATION_RB_MULTISAMPLE:
3982 case WINED3D_LOCATION_RB_RESOLVED:
3983 return WINED3D_RESOURCE_ACCESS_GPU;
3985 default:
3986 FIXME("Unhandled location %#x.\n", location);
3987 return 0;
3991 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
3993 struct wined3d_device *device = surface->resource.device;
3994 struct wined3d_context *context;
3995 const struct wined3d_gl_info *gl_info;
3996 struct wined3d_bo_address dst, src;
3997 UINT size = surface->resource.size;
3999 surface_get_memory(surface, &dst, location);
4000 surface_get_memory(surface, &src, surface->locations);
4002 if (dst.buffer_object)
4004 context = context_acquire(device, NULL);
4005 gl_info = context->gl_info;
4006 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, dst.buffer_object));
4007 GL_EXTCALL(glBufferSubDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0, size, src.addr));
4008 GL_EXTCALL(glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0));
4009 checkGLcall("Upload PBO");
4010 context_release(context);
4011 return;
4013 if (src.buffer_object)
4015 context = context_acquire(device, NULL);
4016 gl_info = context->gl_info;
4017 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, src.buffer_object));
4018 GL_EXTCALL(glGetBufferSubDataARB(GL_PIXEL_PACK_BUFFER_ARB, 0, size, dst.addr));
4019 GL_EXTCALL(glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0));
4020 checkGLcall("Download PBO");
4021 context_release(context);
4022 return;
4024 memcpy(dst.addr, src.addr, size);
4027 static void surface_load_sysmem(struct wined3d_surface *surface,
4028 const struct wined3d_gl_info *gl_info, DWORD dst_location)
4030 if (surface->locations & surface_simple_locations)
4032 surface_copy_simple_location(surface, dst_location);
4033 return;
4036 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
4037 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
4039 /* Download the surface to system memory. */
4040 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
4042 struct wined3d_device *device = surface->resource.device;
4043 struct wined3d_context *context;
4045 /* TODO: Use already acquired context when possible. */
4046 context = context_acquire(device, NULL);
4048 wined3d_texture_bind_and_dirtify(surface->container, context,
4049 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
4050 surface_download_data(surface, gl_info, dst_location);
4052 context_release(context);
4054 return;
4057 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
4059 read_from_framebuffer(surface, dst_location);
4060 return;
4063 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
4064 surface, wined3d_debug_location(surface->locations));
4067 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
4068 const struct wined3d_gl_info *gl_info)
4070 RECT r;
4072 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
4073 && wined3d_resource_is_offscreen(&surface->container->resource))
4075 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
4076 return WINED3DERR_INVALIDCALL;
4079 surface_get_rect(surface, NULL, &r);
4080 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
4081 surface_blt_to_drawable(surface->resource.device,
4082 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
4084 return WINED3D_OK;
4087 static HRESULT surface_load_texture(struct wined3d_surface *surface,
4088 const struct wined3d_gl_info *gl_info, BOOL srgb)
4090 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
4091 struct wined3d_device *device = surface->resource.device;
4092 const struct wined3d_color_key_conversion *conversion;
4093 struct wined3d_texture *texture = surface->container;
4094 struct wined3d_context *context;
4095 UINT width, src_pitch, dst_pitch;
4096 struct wined3d_bo_address data;
4097 struct wined3d_format format;
4098 POINT dst_point = {0, 0};
4099 BYTE *mem = NULL;
4101 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
4102 && wined3d_resource_is_offscreen(&texture->resource)
4103 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
4105 surface_load_fb_texture(surface, srgb);
4107 return WINED3D_OK;
4110 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
4111 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
4112 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4113 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4114 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4116 if (srgb)
4117 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
4118 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
4119 else
4120 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
4121 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
4123 return WINED3D_OK;
4126 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
4127 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
4128 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4129 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4130 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4132 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
4133 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
4134 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
4135 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4137 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
4138 &rect, surface, dst_location, &rect);
4140 return WINED3D_OK;
4143 /* Upload from system memory */
4145 if (srgb)
4147 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
4148 == WINED3D_LOCATION_TEXTURE_RGB)
4150 /* Performance warning... */
4151 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
4152 surface_prepare_map_memory(surface);
4153 surface_load_location(surface, surface->resource.map_binding);
4156 else
4158 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
4159 == WINED3D_LOCATION_TEXTURE_SRGB)
4161 /* Performance warning... */
4162 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
4163 surface_prepare_map_memory(surface);
4164 surface_load_location(surface, surface->resource.map_binding);
4168 if (!(surface->locations & surface_simple_locations))
4170 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
4171 /* Lets hope we get it from somewhere... */
4172 surface_prepare_system_memory(surface);
4173 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
4176 /* TODO: Use already acquired context when possible. */
4177 context = context_acquire(device, NULL);
4179 wined3d_texture_prepare_texture(texture, context, srgb);
4180 wined3d_texture_bind_and_dirtify(texture, context, srgb);
4182 if (texture->color_key_flags & WINEDDSD_CKSRCBLT)
4184 surface->flags |= SFLAG_GLCKEY;
4185 surface->gl_color_key = texture->src_blt_color_key;
4187 else surface->flags &= ~SFLAG_GLCKEY;
4189 width = surface->resource.width;
4190 src_pitch = wined3d_surface_get_pitch(surface);
4192 format = *texture->resource.format;
4193 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
4194 format = *wined3d_get_format(gl_info, conversion->dst_format);
4196 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
4197 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
4198 * getting called. */
4199 if ((format.convert || conversion) && surface->pbo)
4201 TRACE("Removing the pbo attached to surface %p.\n", surface);
4203 if (surface->flags & SFLAG_DIBSECTION)
4204 surface->resource.map_binding = WINED3D_LOCATION_DIB;
4205 else
4206 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
4208 surface_prepare_map_memory(surface);
4209 surface_load_location(surface, surface->resource.map_binding);
4210 surface_remove_pbo(surface, gl_info);
4213 surface_get_memory(surface, &data, surface->locations);
4214 if (format.convert)
4216 /* This code is entered for texture formats which need a fixup. */
4217 UINT height = surface->resource.height;
4219 format.byte_count = format.conv_byte_count;
4220 dst_pitch = wined3d_format_calculate_pitch(&format, width);
4221 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4223 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4225 ERR("Out of memory (%u).\n", dst_pitch * height);
4226 context_release(context);
4227 return E_OUTOFMEMORY;
4229 format.convert(data.addr, mem, src_pitch, src_pitch * height,
4230 dst_pitch, dst_pitch * height, width, height, 1);
4231 src_pitch = dst_pitch;
4232 data.addr = mem;
4234 else if (conversion)
4236 /* This code is only entered for color keying fixups */
4237 struct wined3d_palette *palette = NULL;
4238 UINT height = surface->resource.height;
4240 dst_pitch = wined3d_format_calculate_pitch(&format, width);
4241 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4243 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4245 ERR("Out of memory (%u).\n", dst_pitch * height);
4246 context_release(context);
4247 return E_OUTOFMEMORY;
4249 if (texture->swapchain && texture->swapchain->palette)
4250 palette = texture->swapchain->palette;
4251 conversion->convert(data.addr, src_pitch, mem, dst_pitch,
4252 width, height, palette, &texture->src_blt_color_key);
4253 src_pitch = dst_pitch;
4254 data.addr = mem;
4257 surface_upload_data(surface, gl_info, &format, &src_rect, src_pitch, &dst_point, srgb, &data);
4259 context_release(context);
4261 HeapFree(GetProcessHeap(), 0, mem);
4263 return WINED3D_OK;
4266 static void surface_multisample_resolve(struct wined3d_surface *surface)
4268 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4270 if (!(surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE))
4271 ERR("Trying to resolve multisampled surface %p, but location WINED3D_LOCATION_RB_MULTISAMPLE not current.\n",
4272 surface);
4274 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
4275 surface, WINED3D_LOCATION_RB_MULTISAMPLE, &rect, surface, WINED3D_LOCATION_RB_RESOLVED, &rect);
4278 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location)
4280 struct wined3d_device *device = surface->resource.device;
4281 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4282 HRESULT hr;
4284 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4286 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
4288 if (location == WINED3D_LOCATION_TEXTURE_RGB
4289 && surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_DISCARDED))
4291 struct wined3d_context *context = context_acquire(device, NULL);
4292 surface_load_ds_location(surface, context, location);
4293 context_release(context);
4294 return WINED3D_OK;
4296 else if (location & surface->locations
4297 && surface->container->resource.draw_binding != WINED3D_LOCATION_DRAWABLE)
4299 /* Already up to date, nothing to do. */
4300 return WINED3D_OK;
4302 else
4304 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
4305 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
4306 return WINED3DERR_INVALIDCALL;
4310 if (surface->locations & location)
4312 TRACE("Location already up to date.\n");
4313 return WINED3D_OK;
4316 if (WARN_ON(d3d_surface))
4318 DWORD required_access = resource_access_from_location(location);
4319 if ((surface->resource.access_flags & required_access) != required_access)
4320 WARN("Operation requires %#x access, but surface only has %#x.\n",
4321 required_access, surface->resource.access_flags);
4324 if (!surface->locations)
4326 ERR("Surface %p does not have any up to date location.\n", surface);
4327 surface->flags |= SFLAG_LOST;
4328 return WINED3DERR_DEVICELOST;
4331 switch (location)
4333 case WINED3D_LOCATION_DIB:
4334 case WINED3D_LOCATION_USER_MEMORY:
4335 case WINED3D_LOCATION_SYSMEM:
4336 case WINED3D_LOCATION_BUFFER:
4337 surface_load_sysmem(surface, gl_info, location);
4338 break;
4340 case WINED3D_LOCATION_DRAWABLE:
4341 if (FAILED(hr = surface_load_drawable(surface, gl_info)))
4342 return hr;
4343 break;
4345 case WINED3D_LOCATION_RB_RESOLVED:
4346 surface_multisample_resolve(surface);
4347 break;
4349 case WINED3D_LOCATION_TEXTURE_RGB:
4350 case WINED3D_LOCATION_TEXTURE_SRGB:
4351 if (FAILED(hr = surface_load_texture(surface, gl_info, location == WINED3D_LOCATION_TEXTURE_SRGB)))
4352 return hr;
4353 break;
4355 default:
4356 ERR("Don't know how to handle location %#x.\n", location);
4357 break;
4360 surface_validate_location(surface, location);
4362 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
4363 surface_evict_sysmem(surface);
4365 return WINED3D_OK;
4368 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
4369 /* Context activation is done by the caller. */
4370 static void ffp_blit_free(struct wined3d_device *device) { }
4372 /* Context activation is done by the caller. */
4373 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
4375 const struct wined3d_gl_info *gl_info = context->gl_info;
4377 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
4378 checkGLcall("glEnable(target)");
4380 return WINED3D_OK;
4383 /* Context activation is done by the caller. */
4384 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
4386 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
4387 checkGLcall("glDisable(GL_TEXTURE_2D)");
4388 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
4390 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4391 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4393 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
4395 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
4396 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4400 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
4401 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4402 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4404 switch (blit_op)
4406 case WINED3D_BLIT_OP_COLOR_BLIT:
4407 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
4408 return FALSE;
4410 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4412 TRACE("Checking support for fixup:\n");
4413 dump_color_fixup_desc(src_format->color_fixup);
4416 /* We only support identity conversions. */
4417 if (!is_identity_fixup(src_format->color_fixup)
4418 || !is_identity_fixup(dst_format->color_fixup))
4420 TRACE("Fixups are not supported.\n");
4421 return FALSE;
4424 return TRUE;
4426 case WINED3D_BLIT_OP_COLOR_FILL:
4427 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
4428 return FALSE;
4430 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
4432 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
4433 return FALSE;
4435 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4437 TRACE("Color fill not supported\n");
4438 return FALSE;
4441 /* FIXME: We should reject color fills on formats with fixups,
4442 * but this would break P8 color fills for example. */
4444 return TRUE;
4446 case WINED3D_BLIT_OP_DEPTH_FILL:
4447 return TRUE;
4449 default:
4450 TRACE("Unsupported blit_op=%d\n", blit_op);
4451 return FALSE;
4455 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4456 const RECT *dst_rect, const struct wined3d_color *color)
4458 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4459 struct wined3d_rendertarget_view *view;
4460 struct wined3d_fb_state fb = {&view, NULL};
4461 HRESULT hr;
4463 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4464 NULL, &wined3d_null_parent_ops, &view)))
4466 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4467 return hr;
4470 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
4471 wined3d_rendertarget_view_decref(view);
4473 return WINED3D_OK;
4476 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4477 const RECT *dst_rect, float depth)
4479 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4480 struct wined3d_fb_state fb = {NULL, NULL};
4481 HRESULT hr;
4483 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4484 NULL, &wined3d_null_parent_ops, &fb.depth_stencil)))
4486 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4487 return hr;
4490 device_clear_render_targets(device, 0, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
4491 wined3d_rendertarget_view_decref(fb.depth_stencil);
4493 return WINED3D_OK;
4496 const struct blit_shader ffp_blit = {
4497 ffp_blit_alloc,
4498 ffp_blit_free,
4499 ffp_blit_set,
4500 ffp_blit_unset,
4501 ffp_blit_supported,
4502 ffp_blit_color_fill,
4503 ffp_blit_depth_fill,
4506 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
4508 return WINED3D_OK;
4511 /* Context activation is done by the caller. */
4512 static void cpu_blit_free(struct wined3d_device *device)
4516 /* Context activation is done by the caller. */
4517 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
4519 return WINED3D_OK;
4522 /* Context activation is done by the caller. */
4523 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
4527 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
4528 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4529 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4531 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
4533 return TRUE;
4536 return FALSE;
4539 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
4540 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
4541 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
4543 UINT row_block_count;
4544 const BYTE *src_row;
4545 BYTE *dst_row;
4546 UINT x, y;
4548 src_row = src_data;
4549 dst_row = dst_data;
4551 row_block_count = (update_w + format->block_width - 1) / format->block_width;
4553 if (!flags)
4555 for (y = 0; y < update_h; y += format->block_height)
4557 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
4558 src_row += src_pitch;
4559 dst_row += dst_pitch;
4562 return WINED3D_OK;
4565 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
4567 src_row += (((update_h / format->block_height) - 1) * src_pitch);
4569 switch (format->id)
4571 case WINED3DFMT_DXT1:
4572 for (y = 0; y < update_h; y += format->block_height)
4574 struct block
4576 WORD color[2];
4577 BYTE control_row[4];
4580 const struct block *s = (const struct block *)src_row;
4581 struct block *d = (struct block *)dst_row;
4583 for (x = 0; x < row_block_count; ++x)
4585 d[x].color[0] = s[x].color[0];
4586 d[x].color[1] = s[x].color[1];
4587 d[x].control_row[0] = s[x].control_row[3];
4588 d[x].control_row[1] = s[x].control_row[2];
4589 d[x].control_row[2] = s[x].control_row[1];
4590 d[x].control_row[3] = s[x].control_row[0];
4592 src_row -= src_pitch;
4593 dst_row += dst_pitch;
4595 return WINED3D_OK;
4597 case WINED3DFMT_DXT2:
4598 case WINED3DFMT_DXT3:
4599 for (y = 0; y < update_h; y += format->block_height)
4601 struct block
4603 WORD alpha_row[4];
4604 WORD color[2];
4605 BYTE control_row[4];
4608 const struct block *s = (const struct block *)src_row;
4609 struct block *d = (struct block *)dst_row;
4611 for (x = 0; x < row_block_count; ++x)
4613 d[x].alpha_row[0] = s[x].alpha_row[3];
4614 d[x].alpha_row[1] = s[x].alpha_row[2];
4615 d[x].alpha_row[2] = s[x].alpha_row[1];
4616 d[x].alpha_row[3] = s[x].alpha_row[0];
4617 d[x].color[0] = s[x].color[0];
4618 d[x].color[1] = s[x].color[1];
4619 d[x].control_row[0] = s[x].control_row[3];
4620 d[x].control_row[1] = s[x].control_row[2];
4621 d[x].control_row[2] = s[x].control_row[1];
4622 d[x].control_row[3] = s[x].control_row[0];
4624 src_row -= src_pitch;
4625 dst_row += dst_pitch;
4627 return WINED3D_OK;
4629 default:
4630 FIXME("Compressed flip not implemented for format %s.\n",
4631 debug_d3dformat(format->id));
4632 return E_NOTIMPL;
4636 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
4637 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
4639 return E_NOTIMPL;
4642 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4643 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
4644 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
4646 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
4647 const struct wined3d_format *src_format, *dst_format;
4648 struct wined3d_texture *src_texture = NULL;
4649 struct wined3d_map_desc dst_map, src_map;
4650 const BYTE *sbase = NULL;
4651 HRESULT hr = WINED3D_OK;
4652 const BYTE *sbuf;
4653 BYTE *dbuf;
4654 int x, y;
4656 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
4657 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4658 flags, fx, debug_d3dtexturefiltertype(filter));
4660 if (src_surface == dst_surface)
4662 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
4663 src_map = dst_map;
4664 src_format = dst_surface->resource.format;
4665 dst_format = src_format;
4667 else
4669 dst_format = dst_surface->resource.format;
4670 if (src_surface)
4672 if (dst_surface->resource.format->id != src_surface->resource.format->id)
4674 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
4676 /* The conv function writes a FIXME */
4677 WARN("Cannot convert source surface format to dest format.\n");
4678 goto release;
4680 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
4682 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
4683 src_format = src_surface->resource.format;
4685 else
4687 src_format = dst_format;
4690 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
4693 bpp = dst_surface->resource.format->byte_count;
4694 srcheight = src_rect->bottom - src_rect->top;
4695 srcwidth = src_rect->right - src_rect->left;
4696 dstheight = dst_rect->bottom - dst_rect->top;
4697 dstwidth = dst_rect->right - dst_rect->left;
4698 width = (dst_rect->right - dst_rect->left) * bpp;
4700 if (src_surface)
4701 sbase = (BYTE *)src_map.data
4702 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
4703 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
4704 if (src_surface != dst_surface)
4705 dbuf = dst_map.data;
4706 else
4707 dbuf = (BYTE *)dst_map.data
4708 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
4709 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
4711 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
4713 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
4715 if (src_surface == dst_surface)
4717 FIXME("Only plain blits supported on compressed surfaces.\n");
4718 hr = E_NOTIMPL;
4719 goto release;
4722 if (srcheight != dstheight || srcwidth != dstwidth)
4724 WARN("Stretching not supported on compressed surfaces.\n");
4725 hr = WINED3DERR_INVALIDCALL;
4726 goto release;
4729 if (!surface_check_block_align(src_surface, src_rect))
4731 WARN("Source rectangle not block-aligned.\n");
4732 hr = WINED3DERR_INVALIDCALL;
4733 goto release;
4736 if (!surface_check_block_align(dst_surface, dst_rect))
4738 WARN("Destination rectangle not block-aligned.\n");
4739 hr = WINED3DERR_INVALIDCALL;
4740 goto release;
4743 hr = surface_cpu_blt_compressed(sbase, dbuf,
4744 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
4745 src_format, flags, fx);
4746 goto release;
4749 /* First, all the 'source-less' blits */
4750 if (flags & WINEDDBLT_COLORFILL)
4752 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
4753 flags &= ~WINEDDBLT_COLORFILL;
4756 if (flags & WINEDDBLT_DEPTHFILL)
4758 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
4760 if (flags & WINEDDBLT_ROP)
4762 /* Catch some degenerate cases here. */
4763 switch (fx->dwROP)
4765 case BLACKNESS:
4766 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
4767 break;
4768 case 0xaa0029: /* No-op */
4769 break;
4770 case WHITENESS:
4771 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
4772 break;
4773 case SRCCOPY: /* Well, we do that below? */
4774 break;
4775 default:
4776 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
4777 goto error;
4779 flags &= ~WINEDDBLT_ROP;
4781 if (flags & WINEDDBLT_DDROPS)
4783 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
4785 /* Now the 'with source' blits. */
4786 if (src_surface)
4788 int sx, xinc, sy, yinc;
4790 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
4791 goto release;
4793 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
4794 && (srcwidth != dstwidth || srcheight != dstheight))
4796 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
4797 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
4800 xinc = (srcwidth << 16) / dstwidth;
4801 yinc = (srcheight << 16) / dstheight;
4803 if (!flags)
4805 /* No effects, we can cheat here. */
4806 if (dstwidth == srcwidth)
4808 if (dstheight == srcheight)
4810 /* No stretching in either direction. This needs to be as
4811 * fast as possible. */
4812 sbuf = sbase;
4814 /* Check for overlapping surfaces. */
4815 if (src_surface != dst_surface || dst_rect->top < src_rect->top
4816 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
4818 /* No overlap, or dst above src, so copy from top downwards. */
4819 for (y = 0; y < dstheight; ++y)
4821 memcpy(dbuf, sbuf, width);
4822 sbuf += src_map.row_pitch;
4823 dbuf += dst_map.row_pitch;
4826 else if (dst_rect->top > src_rect->top)
4828 /* Copy from bottom upwards. */
4829 sbuf += src_map.row_pitch * dstheight;
4830 dbuf += dst_map.row_pitch * dstheight;
4831 for (y = 0; y < dstheight; ++y)
4833 sbuf -= src_map.row_pitch;
4834 dbuf -= dst_map.row_pitch;
4835 memcpy(dbuf, sbuf, width);
4838 else
4840 /* Src and dst overlapping on the same line, use memmove. */
4841 for (y = 0; y < dstheight; ++y)
4843 memmove(dbuf, sbuf, width);
4844 sbuf += src_map.row_pitch;
4845 dbuf += dst_map.row_pitch;
4849 else
4851 /* Stretching in y direction only. */
4852 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4854 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4855 memcpy(dbuf, sbuf, width);
4856 dbuf += dst_map.row_pitch;
4860 else
4862 /* Stretching in X direction. */
4863 int last_sy = -1;
4864 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4866 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4868 if ((sy >> 16) == (last_sy >> 16))
4870 /* This source row is the same as last source row -
4871 * Copy the already stretched row. */
4872 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
4874 else
4876 #define STRETCH_ROW(type) \
4877 do { \
4878 const type *s = (const type *)sbuf; \
4879 type *d = (type *)dbuf; \
4880 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4881 d[x] = s[sx >> 16]; \
4882 } while(0)
4884 switch(bpp)
4886 case 1:
4887 STRETCH_ROW(BYTE);
4888 break;
4889 case 2:
4890 STRETCH_ROW(WORD);
4891 break;
4892 case 4:
4893 STRETCH_ROW(DWORD);
4894 break;
4895 case 3:
4897 const BYTE *s;
4898 BYTE *d = dbuf;
4899 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
4901 DWORD pixel;
4903 s = sbuf + 3 * (sx >> 16);
4904 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4905 d[0] = (pixel ) & 0xff;
4906 d[1] = (pixel >> 8) & 0xff;
4907 d[2] = (pixel >> 16) & 0xff;
4908 d += 3;
4910 break;
4912 default:
4913 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
4914 hr = WINED3DERR_NOTAVAILABLE;
4915 goto error;
4917 #undef STRETCH_ROW
4919 dbuf += dst_map.row_pitch;
4920 last_sy = sy;
4924 else
4926 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
4927 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
4928 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
4929 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
4931 /* The color keying flags are checked for correctness in ddraw */
4932 if (flags & WINEDDBLT_KEYSRC)
4934 keylow = src_surface->container->src_blt_color_key.color_space_low_value;
4935 keyhigh = src_surface->container->src_blt_color_key.color_space_high_value;
4937 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4939 keylow = fx->ddckSrcColorkey.color_space_low_value;
4940 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
4943 if (flags & WINEDDBLT_KEYDEST)
4945 /* Destination color keys are taken from the source surface! */
4946 destkeylow = src_surface->container->dst_blt_color_key.color_space_low_value;
4947 destkeyhigh = src_surface->container->dst_blt_color_key.color_space_high_value;
4949 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
4951 destkeylow = fx->ddckDestColorkey.color_space_low_value;
4952 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
4955 if (bpp == 1)
4957 keymask = 0xff;
4959 else
4961 DWORD masks[3];
4962 get_color_masks(src_format, masks);
4963 keymask = masks[0]
4964 | masks[1]
4965 | masks[2];
4967 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
4970 if (flags & WINEDDBLT_DDFX)
4972 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
4973 LONG tmpxy;
4974 dTopLeft = dbuf;
4975 dTopRight = dbuf + ((dstwidth - 1) * bpp);
4976 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
4977 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
4979 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
4981 /* I don't think we need to do anything about this flag */
4982 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
4984 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
4986 tmp = dTopRight;
4987 dTopRight = dTopLeft;
4988 dTopLeft = tmp;
4989 tmp = dBottomRight;
4990 dBottomRight = dBottomLeft;
4991 dBottomLeft = tmp;
4992 dstxinc = dstxinc * -1;
4994 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
4996 tmp = dTopLeft;
4997 dTopLeft = dBottomLeft;
4998 dBottomLeft = tmp;
4999 tmp = dTopRight;
5000 dTopRight = dBottomRight;
5001 dBottomRight = tmp;
5002 dstyinc = dstyinc * -1;
5004 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
5006 /* I don't think we need to do anything about this flag */
5007 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
5009 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
5011 tmp = dBottomRight;
5012 dBottomRight = dTopLeft;
5013 dTopLeft = tmp;
5014 tmp = dBottomLeft;
5015 dBottomLeft = dTopRight;
5016 dTopRight = tmp;
5017 dstxinc = dstxinc * -1;
5018 dstyinc = dstyinc * -1;
5020 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
5022 tmp = dTopLeft;
5023 dTopLeft = dBottomLeft;
5024 dBottomLeft = dBottomRight;
5025 dBottomRight = dTopRight;
5026 dTopRight = tmp;
5027 tmpxy = dstxinc;
5028 dstxinc = dstyinc;
5029 dstyinc = tmpxy;
5030 dstxinc = dstxinc * -1;
5032 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
5034 tmp = dTopLeft;
5035 dTopLeft = dTopRight;
5036 dTopRight = dBottomRight;
5037 dBottomRight = dBottomLeft;
5038 dBottomLeft = tmp;
5039 tmpxy = dstxinc;
5040 dstxinc = dstyinc;
5041 dstyinc = tmpxy;
5042 dstyinc = dstyinc * -1;
5044 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
5046 /* I don't think we need to do anything about this flag */
5047 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
5049 dbuf = dTopLeft;
5050 flags &= ~(WINEDDBLT_DDFX);
5053 #define COPY_COLORKEY_FX(type) \
5054 do { \
5055 const type *s; \
5056 type *d = (type *)dbuf, *dx, tmp; \
5057 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
5059 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
5060 dx = d; \
5061 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5063 tmp = s[sx >> 16]; \
5064 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
5065 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
5067 dx[0] = tmp; \
5069 dx = (type *)(((BYTE *)dx) + dstxinc); \
5071 d = (type *)(((BYTE *)d) + dstyinc); \
5073 } while(0)
5075 switch (bpp)
5077 case 1:
5078 COPY_COLORKEY_FX(BYTE);
5079 break;
5080 case 2:
5081 COPY_COLORKEY_FX(WORD);
5082 break;
5083 case 4:
5084 COPY_COLORKEY_FX(DWORD);
5085 break;
5086 case 3:
5088 const BYTE *s;
5089 BYTE *d = dbuf, *dx;
5090 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5092 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5093 dx = d;
5094 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
5096 DWORD pixel, dpixel = 0;
5097 s = sbuf + 3 * (sx>>16);
5098 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5099 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
5100 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
5101 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
5103 dx[0] = (pixel ) & 0xff;
5104 dx[1] = (pixel >> 8) & 0xff;
5105 dx[2] = (pixel >> 16) & 0xff;
5107 dx += dstxinc;
5109 d += dstyinc;
5111 break;
5113 default:
5114 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
5115 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
5116 hr = WINED3DERR_NOTAVAILABLE;
5117 goto error;
5118 #undef COPY_COLORKEY_FX
5123 error:
5124 if (flags && FIXME_ON(d3d_surface))
5126 FIXME("\tUnsupported flags: %#x.\n", flags);
5129 release:
5130 wined3d_surface_unmap(dst_surface);
5131 if (src_surface && src_surface != dst_surface)
5132 wined3d_surface_unmap(src_surface);
5133 /* Release the converted surface, if any. */
5134 if (src_texture)
5135 wined3d_texture_decref(src_texture);
5137 return hr;
5140 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5141 const RECT *dst_rect, const struct wined3d_color *color)
5143 static const RECT src_rect;
5144 WINEDDBLTFX BltFx;
5146 memset(&BltFx, 0, sizeof(BltFx));
5147 BltFx.dwSize = sizeof(BltFx);
5148 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
5149 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
5150 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
5153 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
5154 struct wined3d_surface *surface, const RECT *rect, float depth)
5156 FIXME("Depth filling not implemented by cpu_blit.\n");
5157 return WINED3DERR_INVALIDCALL;
5160 const struct blit_shader cpu_blit = {
5161 cpu_blit_alloc,
5162 cpu_blit_free,
5163 cpu_blit_set,
5164 cpu_blit_unset,
5165 cpu_blit_supported,
5166 cpu_blit_color_fill,
5167 cpu_blit_depth_fill,
5170 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
5171 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
5172 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5174 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
5175 struct wined3d_device *device = dst_surface->resource.device;
5176 DWORD src_ds_flags, dst_ds_flags;
5177 RECT src_rect, dst_rect;
5178 BOOL scale, convert;
5180 static const DWORD simple_blit = WINEDDBLT_ASYNC
5181 | WINEDDBLT_COLORFILL
5182 | WINEDDBLT_WAIT
5183 | WINEDDBLT_DEPTHFILL
5184 | WINEDDBLT_DONOTWAIT;
5186 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5187 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
5188 flags, fx, debug_d3dtexturefiltertype(filter));
5189 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
5191 if (fx)
5193 TRACE("dwSize %#x.\n", fx->dwSize);
5194 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
5195 TRACE("dwROP %#x.\n", fx->dwROP);
5196 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
5197 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
5198 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
5199 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
5200 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
5201 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
5202 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
5203 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
5204 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
5205 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
5206 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
5207 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
5208 TRACE("dwReserved %#x.\n", fx->dwReserved);
5209 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
5210 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
5211 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
5212 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
5213 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
5214 TRACE("ddckDestColorkey {%#x, %#x}.\n",
5215 fx->ddckDestColorkey.color_space_low_value,
5216 fx->ddckDestColorkey.color_space_high_value);
5217 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
5218 fx->ddckSrcColorkey.color_space_low_value,
5219 fx->ddckSrcColorkey.color_space_high_value);
5222 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
5224 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
5225 return WINEDDERR_SURFACEBUSY;
5228 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
5230 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
5231 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
5232 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
5233 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
5234 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
5236 WARN("The application gave us a bad destination rectangle.\n");
5237 return WINEDDERR_INVALIDRECT;
5240 if (src_surface)
5242 surface_get_rect(src_surface, src_rect_in, &src_rect);
5244 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
5245 || src_rect.left > src_surface->resource.width || src_rect.left < 0
5246 || src_rect.top > src_surface->resource.height || src_rect.top < 0
5247 || src_rect.right > src_surface->resource.width || src_rect.right < 0
5248 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
5250 WARN("Application gave us bad source rectangle for Blt.\n");
5251 return WINEDDERR_INVALIDRECT;
5254 else
5256 memset(&src_rect, 0, sizeof(src_rect));
5259 if (!fx || !(fx->dwDDFX))
5260 flags &= ~WINEDDBLT_DDFX;
5262 if (flags & WINEDDBLT_WAIT)
5263 flags &= ~WINEDDBLT_WAIT;
5265 if (flags & WINEDDBLT_ASYNC)
5267 static unsigned int once;
5269 if (!once++)
5270 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
5271 flags &= ~WINEDDBLT_ASYNC;
5274 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
5275 if (flags & WINEDDBLT_DONOTWAIT)
5277 static unsigned int once;
5279 if (!once++)
5280 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
5281 flags &= ~WINEDDBLT_DONOTWAIT;
5284 if (!device->d3d_initialized)
5286 WARN("D3D not initialized, using fallback.\n");
5287 goto cpu;
5290 /* We want to avoid invalidating the sysmem location for converted
5291 * surfaces, since otherwise we'd have to convert the data back when
5292 * locking them. */
5293 if (dst_surface->container->flags & WINED3D_TEXTURE_CONVERTED
5294 || dst_surface->container->resource.format->convert
5295 || wined3d_format_get_color_key_conversion(dst_surface->container, TRUE))
5297 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
5298 goto cpu;
5301 if (flags & ~simple_blit)
5303 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
5304 goto fallback;
5307 if (src_surface)
5308 src_swapchain = src_surface->container->swapchain;
5309 else
5310 src_swapchain = NULL;
5312 dst_swapchain = dst_surface->container->swapchain;
5314 /* This isn't strictly needed. FBO blits for example could deal with
5315 * cross-swapchain blits by first downloading the source to a texture
5316 * before switching to the destination context. We just have this here to
5317 * not have to deal with the issue, since cross-swapchain blits should be
5318 * rare. */
5319 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
5321 FIXME("Using fallback for cross-swapchain blit.\n");
5322 goto fallback;
5325 scale = src_surface
5326 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
5327 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
5328 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
5330 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5331 if (src_surface)
5332 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5333 else
5334 src_ds_flags = 0;
5336 if (src_ds_flags || dst_ds_flags)
5338 if (flags & WINEDDBLT_DEPTHFILL)
5340 float depth;
5342 TRACE("Depth fill.\n");
5344 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
5345 return WINED3DERR_INVALIDCALL;
5347 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
5348 return WINED3D_OK;
5350 else
5352 if (src_ds_flags != dst_ds_flags)
5354 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
5355 return WINED3DERR_INVALIDCALL;
5358 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->container->resource.draw_binding,
5359 &src_rect, dst_surface, dst_surface->container->resource.draw_binding, &dst_rect)))
5360 return WINED3D_OK;
5363 else
5365 /* In principle this would apply to depth blits as well, but we don't
5366 * implement those in the CPU blitter at the moment. */
5367 if ((dst_surface->locations & dst_surface->resource.map_binding)
5368 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
5370 if (scale)
5371 TRACE("Not doing sysmem blit because of scaling.\n");
5372 else if (convert)
5373 TRACE("Not doing sysmem blit because of format conversion.\n");
5374 else
5375 goto cpu;
5378 if (flags & WINEDDBLT_COLORFILL)
5380 struct wined3d_color color;
5382 TRACE("Color fill.\n");
5384 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
5385 goto fallback;
5387 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
5388 return WINED3D_OK;
5390 else
5392 TRACE("Color blit.\n");
5394 /* Upload */
5395 if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
5396 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
5398 if (scale)
5399 TRACE("Not doing upload because of scaling.\n");
5400 else if (convert)
5401 TRACE("Not doing upload because of format conversion.\n");
5402 else
5404 POINT dst_point = {dst_rect.left, dst_rect.top};
5406 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
5408 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
5409 surface_load_location(dst_surface, dst_surface->container->resource.draw_binding);
5410 return WINED3D_OK;
5415 /* Use present for back -> front blits. The idea behind this is
5416 * that present is potentially faster than a blit, in particular
5417 * when FBO blits aren't available. Some ddraw applications like
5418 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
5419 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
5420 * applications can't blit directly to the frontbuffer. */
5421 if (dst_swapchain && dst_swapchain->back_buffers
5422 && dst_surface->container == dst_swapchain->front_buffer
5423 && src_surface->container == dst_swapchain->back_buffers[0])
5425 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
5427 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
5429 /* Set the swap effect to COPY, we don't want the backbuffer
5430 * to become undefined. */
5431 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
5432 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
5433 dst_swapchain->desc.swap_effect = swap_effect;
5435 return WINED3D_OK;
5438 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5439 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5440 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5442 TRACE("Using FBO blit.\n");
5444 surface_blt_fbo(device, filter,
5445 src_surface, src_surface->container->resource.draw_binding, &src_rect,
5446 dst_surface, dst_surface->container->resource.draw_binding, &dst_rect);
5447 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
5448 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
5450 return WINED3D_OK;
5453 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5454 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5455 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5457 TRACE("Using arbfp blit.\n");
5459 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
5460 return WINED3D_OK;
5465 fallback:
5466 /* Special cases for render targets. */
5467 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
5468 return WINED3D_OK;
5470 cpu:
5472 /* For the rest call the X11 surface implementation. For render targets
5473 * this should be implemented OpenGL accelerated in surface_blt_special(),
5474 * other blits are rather rare. */
5475 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
5478 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
5479 const struct wined3d_resource_desc *desc, GLenum target, unsigned int level, unsigned int layer, DWORD flags)
5481 struct wined3d_device *device = container->resource.device;
5482 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5483 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
5484 UINT multisample_quality = desc->multisample_quality;
5485 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
5486 unsigned int resource_size;
5487 HRESULT hr;
5489 if (multisample_quality > 0)
5491 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
5492 multisample_quality = 0;
5495 /* Quick lockable sanity check.
5496 * TODO: remove this after surfaces, usage and lockability have been debugged properly
5497 * this function is too deep to need to care about things like this.
5498 * Levels need to be checked too, since they all affect what can be done. */
5499 switch (desc->pool)
5501 case WINED3D_POOL_MANAGED:
5502 if (desc->usage & WINED3DUSAGE_DYNAMIC)
5503 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
5504 break;
5506 case WINED3D_POOL_DEFAULT:
5507 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
5508 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
5509 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
5510 break;
5512 case WINED3D_POOL_SCRATCH:
5513 case WINED3D_POOL_SYSTEM_MEM:
5514 break;
5516 default:
5517 FIXME("Unknown pool %#x.\n", desc->pool);
5518 break;
5521 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
5522 FIXME("Trying to create a render target that isn't in the default pool.\n");
5524 /* FIXME: Check that the format is supported by the device. */
5526 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
5527 if (!resource_size)
5528 return WINED3DERR_INVALIDCALL;
5530 if (device->wined3d->flags & WINED3D_NO3D)
5531 surface->surface_ops = &gdi_surface_ops;
5532 else
5533 surface->surface_ops = &surface_ops;
5535 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
5536 desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height, 1,
5537 resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
5539 WARN("Failed to initialize resource, returning %#x.\n", hr);
5540 return hr;
5543 surface->container = container;
5544 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
5545 list_init(&surface->renderbuffers);
5546 list_init(&surface->overlays);
5548 /* Flags */
5549 if (flags & WINED3D_SURFACE_DISCARD)
5550 surface->flags |= SFLAG_DISCARD;
5551 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
5552 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
5554 surface->texture_target = target;
5555 surface->texture_level = level;
5556 surface->texture_layer = layer;
5558 /* Call the private setup routine */
5559 if (FAILED(hr = surface->surface_ops->surface_private_setup(surface)))
5561 ERR("Private setup failed, hr %#x.\n", hr);
5562 surface_cleanup(surface);
5563 return hr;
5566 /* Similar to lockable rendertargets above, creating the DIB section
5567 * during surface initialization prevents the sysmem pointer from changing
5568 * after a wined3d_surface_getdc() call. */
5569 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
5570 && SUCCEEDED(surface_create_dib_section(surface)))
5571 surface->resource.map_binding = WINED3D_LOCATION_DIB;
5573 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
5575 wined3d_resource_free_sysmem(&surface->resource);
5576 surface_validate_location(surface, WINED3D_LOCATION_DIB);
5577 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
5580 return hr;
5583 HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
5584 GLenum target, unsigned int level, unsigned int layer, DWORD flags, struct wined3d_surface **surface)
5586 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
5587 const struct wined3d_parent_ops *parent_ops;
5588 struct wined3d_surface *object;
5589 void *parent;
5590 HRESULT hr;
5592 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), pool %s, "
5593 "multisample_type %#x, multisample_quality %u, target %#x, level %u, layer %u, flags %#x, surface %p.\n",
5594 container, desc->width, desc->height, debug_d3dformat(desc->format),
5595 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
5596 desc->multisample_type, desc->multisample_quality, target, level, layer, flags, surface);
5598 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
5599 return E_OUTOFMEMORY;
5601 if (FAILED(hr = surface_init(object, container, desc, target, level, layer, flags)))
5603 WARN("Failed to initialize surface, returning %#x.\n", hr);
5604 HeapFree(GetProcessHeap(), 0, object);
5605 return hr;
5608 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
5609 wined3d_texture_get_parent(container), object, &parent, &parent_ops)))
5611 WARN("Failed to create surface parent, hr %#x.\n", hr);
5612 wined3d_surface_destroy(object);
5613 return hr;
5616 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
5618 object->resource.parent = parent;
5619 object->resource.parent_ops = parent_ops;
5620 *surface = object;
5622 return hr;