wined3d: Use the core version of the gl*Buffer* functions.
[wine/multimedia.git] / dlls / wined3d / surface.c
blobe6d7427d6982c034a6c75f86152f15f12d2e29a7
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(glDeleteBuffers(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(glGenBuffers(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(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
533 checkGLcall("glBindBuffer");
535 GL_EXTCALL(glBufferData(GL_PIXEL_UNPACK_BUFFER, surface->resource.size + 4,
536 NULL, GL_STREAM_DRAW));
537 checkGLcall("glBufferData");
539 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
540 checkGLcall("glBindBuffer");
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(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
737 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER));
738 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
739 checkGLcall("glUnmapBuffer");
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(glDeleteBuffers(1, &surface->pbo));
1156 checkGLcall("glDeleteBuffers(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(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1344 checkGLcall("glBindBuffer");
1345 GL_EXTCALL(glGetCompressedTexImageARB(surface->texture_target, surface->texture_level, NULL));
1346 checkGLcall("glGetCompressedTexImageARB");
1347 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1348 checkGLcall("glBindBuffer");
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(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1384 checkGLcall("glBindBuffer");
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(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1391 checkGLcall("glBindBuffer");
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 void wined3d_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_const_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(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
1498 checkGLcall("glBindBuffer");
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(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
1565 checkGLcall("glBindBuffer");
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 wined3d_surface_upload_data(dst_surface, gl_info, src_format, src_rect,
1704 src_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
1706 context_invalidate_active_texture(context);
1708 context_release(context);
1710 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1711 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1713 return WINED3D_OK;
1716 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1717 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1718 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1719 /* Context activation is done by the caller. */
1720 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1722 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
1723 struct wined3d_renderbuffer_entry *entry;
1724 GLuint renderbuffer = 0;
1725 unsigned int src_width, src_height;
1726 unsigned int width, height;
1728 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
1730 width = rt->pow2Width;
1731 height = rt->pow2Height;
1733 else
1735 width = surface->pow2Width;
1736 height = surface->pow2Height;
1739 src_width = surface->pow2Width;
1740 src_height = surface->pow2Height;
1742 /* A depth stencil smaller than the render target is not valid */
1743 if (width > src_width || height > src_height) return;
1745 /* Remove any renderbuffer set if the sizes match */
1746 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1747 || (width == src_width && height == src_height))
1749 surface->current_renderbuffer = NULL;
1750 return;
1753 /* Look if we've already got a renderbuffer of the correct dimensions */
1754 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1756 if (entry->width == width && entry->height == height)
1758 renderbuffer = entry->id;
1759 surface->current_renderbuffer = entry;
1760 break;
1764 if (!renderbuffer)
1766 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1767 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1768 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1769 surface->resource.format->glInternal, width, height);
1771 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1772 entry->width = width;
1773 entry->height = height;
1774 entry->id = renderbuffer;
1775 list_add_head(&surface->renderbuffers, &entry->entry);
1777 surface->current_renderbuffer = entry;
1780 checkGLcall("set_compatible_renderbuffer");
1783 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
1785 const struct wined3d_swapchain *swapchain = surface->container->swapchain;
1787 TRACE("surface %p.\n", surface);
1789 if (!swapchain)
1791 ERR("Surface %p is not on a swapchain.\n", surface);
1792 return GL_NONE;
1795 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface->container)
1797 if (swapchain->render_to_fbo)
1799 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
1800 return GL_COLOR_ATTACHMENT0;
1802 TRACE("Returning GL_BACK\n");
1803 return GL_BACK;
1805 else if (surface->container == swapchain->front_buffer)
1807 TRACE("Returning GL_FRONT\n");
1808 return GL_FRONT;
1811 FIXME("Higher back buffer, returning GL_BACK\n");
1812 return GL_BACK;
1815 void surface_load(struct wined3d_surface *surface, BOOL srgb)
1817 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
1818 BOOL ck_changed;
1820 TRACE("surface %p, srgb %#x.\n", surface, srgb);
1822 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
1823 ERR("Not supported on scratch surfaces.\n");
1825 ck_changed = !(surface->flags & SFLAG_GLCKEY) != !(surface->container->color_key_flags & WINEDDSD_CKSRCBLT);
1827 /* Reload if either the texture and sysmem have different ideas about the
1828 * color key, or the actual key values changed. */
1829 if (ck_changed || ((surface->container->color_key_flags & WINEDDSD_CKSRCBLT)
1830 && (surface->gl_color_key.color_space_low_value
1831 != surface->container->src_blt_color_key.color_space_low_value
1832 || surface->gl_color_key.color_space_high_value
1833 != surface->container->src_blt_color_key.color_space_high_value)))
1835 TRACE("Reloading because of color keying\n");
1836 /* To perform the color key conversion we need a sysmem copy of
1837 * the surface. Make sure we have it. */
1839 surface_prepare_map_memory(surface);
1840 surface_load_location(surface, surface->resource.map_binding);
1841 surface_invalidate_location(surface, ~surface->resource.map_binding);
1842 /* Switching color keying on / off may change the internal format. */
1843 if (ck_changed)
1844 wined3d_texture_force_reload(surface->container);
1846 else if (!(surface->locations & location))
1848 TRACE("Reloading because surface is dirty.\n");
1850 else
1852 TRACE("surface is already in texture\n");
1853 return;
1856 surface_load_location(surface, location);
1857 surface_evict_sysmem(surface);
1860 /* See also float_16_to_32() in wined3d_private.h */
1861 static inline unsigned short float_32_to_16(const float *in)
1863 int exp = 0;
1864 float tmp = fabsf(*in);
1865 unsigned int mantissa;
1866 unsigned short ret;
1868 /* Deal with special numbers */
1869 if (*in == 0.0f)
1870 return 0x0000;
1871 if (isnan(*in))
1872 return 0x7c01;
1873 if (isinf(*in))
1874 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1876 if (tmp < powf(2, 10))
1880 tmp = tmp * 2.0f;
1881 exp--;
1882 } while (tmp < powf(2, 10));
1884 else if (tmp >= powf(2, 11))
1888 tmp /= 2.0f;
1889 exp++;
1890 } while (tmp >= powf(2, 11));
1893 mantissa = (unsigned int)tmp;
1894 if (tmp - mantissa >= 0.5f)
1895 ++mantissa; /* Round to nearest, away from zero. */
1897 exp += 10; /* Normalize the mantissa. */
1898 exp += 15; /* Exponent is encoded with excess 15. */
1900 if (exp > 30) /* too big */
1902 ret = 0x7c00; /* INF */
1904 else if (exp <= 0)
1906 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1907 while (exp <= 0)
1909 mantissa = mantissa >> 1;
1910 ++exp;
1912 ret = mantissa & 0x3ff;
1914 else
1916 ret = (exp << 10) | (mantissa & 0x3ff);
1919 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1920 return ret;
1923 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
1925 TRACE("surface %p, container %p.\n", surface, surface->container);
1927 return wined3d_texture_incref(surface->container);
1930 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
1932 TRACE("surface %p, container %p.\n", surface, surface->container);
1934 return wined3d_texture_decref(surface->container);
1937 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
1939 TRACE("surface %p.\n", surface);
1941 if (!surface->resource.device->d3d_initialized)
1943 ERR("D3D not initialized.\n");
1944 return;
1947 wined3d_texture_preload(surface->container);
1950 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
1952 TRACE("surface %p.\n", surface);
1954 return surface->resource.parent;
1957 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
1959 TRACE("surface %p.\n", surface);
1961 return &surface->resource;
1964 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
1966 TRACE("surface %p, flags %#x.\n", surface, flags);
1968 switch (flags)
1970 case WINEDDGBS_CANBLT:
1971 case WINEDDGBS_ISBLTDONE:
1972 return WINED3D_OK;
1974 default:
1975 return WINED3DERR_INVALIDCALL;
1979 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
1981 TRACE("surface %p, flags %#x.\n", surface, flags);
1983 /* XXX: DDERR_INVALIDSURFACETYPE */
1985 switch (flags)
1987 case WINEDDGFS_CANFLIP:
1988 case WINEDDGFS_ISFLIPDONE:
1989 return WINED3D_OK;
1991 default:
1992 return WINED3DERR_INVALIDCALL;
1996 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
1998 TRACE("surface %p.\n", surface);
2000 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
2001 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
2004 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
2006 TRACE("surface %p.\n", surface);
2008 surface->flags &= ~SFLAG_LOST;
2009 return WINED3D_OK;
2012 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
2014 unsigned int alignment;
2015 DWORD pitch;
2017 TRACE("surface %p.\n", surface);
2019 if (surface->pitch)
2020 return surface->pitch;
2022 alignment = surface->resource.device->surface_alignment;
2023 pitch = wined3d_format_calculate_pitch(surface->resource.format, surface->resource.width);
2024 pitch = (pitch + alignment - 1) & ~(alignment - 1);
2026 TRACE("Returning %u.\n", pitch);
2028 return pitch;
2031 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
2033 LONG w, h;
2035 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
2037 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2039 WARN("Not an overlay surface.\n");
2040 return WINEDDERR_NOTAOVERLAYSURFACE;
2043 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
2044 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
2045 surface->overlay_destrect.left = x;
2046 surface->overlay_destrect.top = y;
2047 surface->overlay_destrect.right = x + w;
2048 surface->overlay_destrect.bottom = y + h;
2050 return WINED3D_OK;
2053 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
2055 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
2057 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2059 TRACE("Not an overlay surface.\n");
2060 return WINEDDERR_NOTAOVERLAYSURFACE;
2063 if (!surface->overlay_dest)
2065 TRACE("Overlay not visible.\n");
2066 *x = 0;
2067 *y = 0;
2068 return WINEDDERR_OVERLAYNOTVISIBLE;
2071 *x = surface->overlay_destrect.left;
2072 *y = surface->overlay_destrect.top;
2074 TRACE("Returning position %d, %d.\n", *x, *y);
2076 return WINED3D_OK;
2079 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
2080 DWORD flags, struct wined3d_surface *ref)
2082 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
2084 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2086 TRACE("Not an overlay surface.\n");
2087 return WINEDDERR_NOTAOVERLAYSURFACE;
2090 return WINED3D_OK;
2093 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2094 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2096 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2097 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2099 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2101 WARN("Not an overlay surface.\n");
2102 return WINEDDERR_NOTAOVERLAYSURFACE;
2104 else if (!dst_surface)
2106 WARN("Dest surface is NULL.\n");
2107 return WINED3DERR_INVALIDCALL;
2110 if (src_rect)
2112 surface->overlay_srcrect = *src_rect;
2114 else
2116 surface->overlay_srcrect.left = 0;
2117 surface->overlay_srcrect.top = 0;
2118 surface->overlay_srcrect.right = surface->resource.width;
2119 surface->overlay_srcrect.bottom = surface->resource.height;
2122 if (dst_rect)
2124 surface->overlay_destrect = *dst_rect;
2126 else
2128 surface->overlay_destrect.left = 0;
2129 surface->overlay_destrect.top = 0;
2130 surface->overlay_destrect.right = dst_surface ? dst_surface->resource.width : 0;
2131 surface->overlay_destrect.bottom = dst_surface ? dst_surface->resource.height : 0;
2134 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2136 surface->overlay_dest = NULL;
2137 list_remove(&surface->overlay_entry);
2140 if (flags & WINEDDOVER_SHOW)
2142 if (surface->overlay_dest != dst_surface)
2144 surface->overlay_dest = dst_surface;
2145 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2148 else if (flags & WINEDDOVER_HIDE)
2150 /* tests show that the rectangles are erased on hide */
2151 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2152 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2153 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2154 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2155 surface->overlay_dest = NULL;
2158 return WINED3D_OK;
2161 HRESULT wined3d_surface_update_desc(struct wined3d_surface *surface,
2162 const struct wined3d_gl_info *gl_info, void *mem, unsigned int pitch)
2164 struct wined3d_resource *texture_resource = &surface->container->resource;
2165 unsigned int width, height;
2166 BOOL create_dib = FALSE;
2167 DWORD valid_location = 0;
2168 HRESULT hr;
2170 if (surface->flags & SFLAG_DIBSECTION)
2172 DeleteDC(surface->hDC);
2173 DeleteObject(surface->dib.DIBsection);
2174 surface->dib.bitmap_data = NULL;
2175 surface->flags &= ~SFLAG_DIBSECTION;
2176 create_dib = TRUE;
2179 surface->locations = 0;
2180 wined3d_resource_free_sysmem(&surface->resource);
2182 width = texture_resource->width;
2183 height = texture_resource->height;
2184 surface->resource.width = width;
2185 surface->resource.height = height;
2186 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2187 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2189 surface->pow2Width = width;
2190 surface->pow2Height = height;
2192 else
2194 surface->pow2Width = surface->pow2Height = 1;
2195 while (surface->pow2Width < width)
2196 surface->pow2Width <<= 1;
2197 while (surface->pow2Height < height)
2198 surface->pow2Height <<= 1;
2201 if (surface->pow2Width != width || surface->pow2Height != height)
2202 surface->flags |= SFLAG_NONPOW2;
2203 else
2204 surface->flags &= ~SFLAG_NONPOW2;
2206 if ((surface->user_memory = mem))
2208 surface->resource.map_binding = WINED3D_LOCATION_USER_MEMORY;
2209 valid_location = WINED3D_LOCATION_USER_MEMORY;
2211 surface->pitch = pitch;
2212 surface->resource.format = texture_resource->format;
2213 surface->resource.multisample_type = texture_resource->multisample_type;
2214 surface->resource.multisample_quality = texture_resource->multisample_quality;
2215 if (surface->pitch)
2216 surface->resource.size = height * surface->pitch;
2217 else
2218 surface->resource.size = wined3d_format_calculate_size(texture_resource->format,
2219 texture_resource->device->surface_alignment, width, height, 1);
2221 /* The format might be changed to a format that needs conversion.
2222 * If the surface didn't use PBOs previously but could now, don't
2223 * change it - whatever made us not use PBOs might come back, e.g.
2224 * color keys. */
2225 if (surface->resource.map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
2226 surface->resource.map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
2228 if (create_dib)
2230 if (FAILED(hr = surface_create_dib_section(surface)))
2232 ERR("Failed to create dib section, hr %#x.\n", hr);
2233 return hr;
2235 if (!valid_location)
2236 valid_location = WINED3D_LOCATION_DIB;
2239 if (!valid_location)
2241 surface_prepare_system_memory(surface);
2242 valid_location = WINED3D_LOCATION_SYSMEM;
2245 surface_validate_location(surface, valid_location);
2247 return WINED3D_OK;
2250 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2251 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2253 unsigned short *dst_s;
2254 const float *src_f;
2255 unsigned int x, y;
2257 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2259 for (y = 0; y < h; ++y)
2261 src_f = (const float *)(src + y * pitch_in);
2262 dst_s = (unsigned short *) (dst + y * pitch_out);
2263 for (x = 0; x < w; ++x)
2265 dst_s[x] = float_32_to_16(src_f + x);
2270 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2271 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2273 static const unsigned char convert_5to8[] =
2275 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2276 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2277 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2278 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2280 static const unsigned char convert_6to8[] =
2282 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2283 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2284 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2285 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2286 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2287 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2288 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2289 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2291 unsigned int x, y;
2293 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2295 for (y = 0; y < h; ++y)
2297 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2298 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2299 for (x = 0; x < w; ++x)
2301 WORD pixel = src_line[x];
2302 dst_line[x] = 0xff000000
2303 | convert_5to8[(pixel & 0xf800) >> 11] << 16
2304 | convert_6to8[(pixel & 0x07e0) >> 5] << 8
2305 | convert_5to8[(pixel & 0x001f)];
2310 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2311 * in both cases we're just setting the X / Alpha channel to 0xff. */
2312 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2313 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2315 unsigned int x, y;
2317 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2319 for (y = 0; y < h; ++y)
2321 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2322 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2324 for (x = 0; x < w; ++x)
2326 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2331 static inline BYTE cliptobyte(int x)
2333 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2336 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2337 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2339 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2340 unsigned int x, y;
2342 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2344 for (y = 0; y < h; ++y)
2346 const BYTE *src_line = src + y * pitch_in;
2347 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2348 for (x = 0; x < w; ++x)
2350 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2351 * C = Y - 16; D = U - 128; E = V - 128;
2352 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2353 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2354 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2355 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2356 * U and V are shared between the pixels. */
2357 if (!(x & 1)) /* For every even pixel, read new U and V. */
2359 d = (int) src_line[1] - 128;
2360 e = (int) src_line[3] - 128;
2361 r2 = 409 * e + 128;
2362 g2 = - 100 * d - 208 * e + 128;
2363 b2 = 516 * d + 128;
2365 c2 = 298 * ((int) src_line[0] - 16);
2366 dst_line[x] = 0xff000000
2367 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2368 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2369 | cliptobyte((c2 + b2) >> 8); /* blue */
2370 /* Scale RGB values to 0..255 range,
2371 * then clip them if still not in range (may be negative),
2372 * then shift them within DWORD if necessary. */
2373 src_line += 2;
2378 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2379 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2381 unsigned int x, y;
2382 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2384 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2386 for (y = 0; y < h; ++y)
2388 const BYTE *src_line = src + y * pitch_in;
2389 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2390 for (x = 0; x < w; ++x)
2392 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2393 * C = Y - 16; D = U - 128; E = V - 128;
2394 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2395 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2396 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2397 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2398 * U and V are shared between the pixels. */
2399 if (!(x & 1)) /* For every even pixel, read new U and V. */
2401 d = (int) src_line[1] - 128;
2402 e = (int) src_line[3] - 128;
2403 r2 = 409 * e + 128;
2404 g2 = - 100 * d - 208 * e + 128;
2405 b2 = 516 * d + 128;
2407 c2 = 298 * ((int) src_line[0] - 16);
2408 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2409 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2410 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2411 /* Scale RGB values to 0..255 range,
2412 * then clip them if still not in range (may be negative),
2413 * then shift them within DWORD if necessary. */
2414 src_line += 2;
2419 struct d3dfmt_converter_desc
2421 enum wined3d_format_id from, to;
2422 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2425 static const struct d3dfmt_converter_desc converters[] =
2427 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2428 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2429 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2430 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2431 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2432 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2435 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2436 enum wined3d_format_id to)
2438 unsigned int i;
2440 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2442 if (converters[i].from == from && converters[i].to == to)
2443 return &converters[i];
2446 return NULL;
2449 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2451 struct wined3d_map_desc src_map, dst_map;
2452 const struct d3dfmt_converter_desc *conv;
2453 struct wined3d_texture *ret = NULL;
2454 struct wined3d_resource_desc desc;
2455 struct wined3d_surface *dst;
2457 conv = find_converter(source->resource.format->id, to_fmt);
2458 if (!conv)
2460 FIXME("Cannot find a conversion function from format %s to %s.\n",
2461 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2462 return NULL;
2465 /* FIXME: Multisampled conversion? */
2466 wined3d_resource_get_desc(&source->resource, &desc);
2467 desc.resource_type = WINED3D_RTYPE_TEXTURE;
2468 desc.format = to_fmt;
2469 desc.usage = 0;
2470 desc.pool = WINED3D_POOL_SCRATCH;
2471 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2472 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, NULL, &wined3d_null_parent_ops, &ret)))
2474 ERR("Failed to create a destination surface for conversion.\n");
2475 return NULL;
2477 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2479 memset(&src_map, 0, sizeof(src_map));
2480 memset(&dst_map, 0, sizeof(dst_map));
2482 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2484 ERR("Failed to lock the source surface.\n");
2485 wined3d_texture_decref(ret);
2486 return NULL;
2488 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2490 ERR("Failed to lock the destination surface.\n");
2491 wined3d_surface_unmap(source);
2492 wined3d_texture_decref(ret);
2493 return NULL;
2496 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2497 source->resource.width, source->resource.height);
2499 wined3d_surface_unmap(dst);
2500 wined3d_surface_unmap(source);
2502 return ret;
2505 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2506 unsigned int bpp, UINT pitch, DWORD color)
2508 BYTE *first;
2509 unsigned int x, y;
2511 /* Do first row */
2513 #define COLORFILL_ROW(type) \
2514 do { \
2515 type *d = (type *)buf; \
2516 for (x = 0; x < width; ++x) \
2517 d[x] = (type)color; \
2518 } while(0)
2520 switch (bpp)
2522 case 1:
2523 COLORFILL_ROW(BYTE);
2524 break;
2526 case 2:
2527 COLORFILL_ROW(WORD);
2528 break;
2530 case 3:
2532 BYTE *d = buf;
2533 for (x = 0; x < width; ++x, d += 3)
2535 d[0] = (color ) & 0xff;
2536 d[1] = (color >> 8) & 0xff;
2537 d[2] = (color >> 16) & 0xff;
2539 break;
2541 case 4:
2542 COLORFILL_ROW(DWORD);
2543 break;
2545 default:
2546 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2547 return WINED3DERR_NOTAVAILABLE;
2550 #undef COLORFILL_ROW
2552 /* Now copy first row. */
2553 first = buf;
2554 for (y = 1; y < height; ++y)
2556 buf += pitch;
2557 memcpy(buf, first, width * bpp);
2560 return WINED3D_OK;
2563 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
2565 return surface_from_resource(resource);
2568 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
2570 TRACE("surface %p.\n", surface);
2572 if (!surface->resource.map_count)
2574 WARN("Trying to unmap unmapped surface.\n");
2575 return WINEDDERR_NOTLOCKED;
2577 --surface->resource.map_count;
2579 surface->surface_ops->surface_unmap(surface);
2581 return WINED3D_OK;
2584 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
2585 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
2587 const struct wined3d_format *format = surface->resource.format;
2588 struct wined3d_device *device = surface->resource.device;
2589 struct wined3d_context *context;
2590 const struct wined3d_gl_info *gl_info;
2591 BYTE *base_memory;
2593 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
2594 surface, map_desc, wine_dbgstr_rect(rect), flags);
2596 if (surface->resource.map_count)
2598 WARN("Surface is already mapped.\n");
2599 return WINED3DERR_INVALIDCALL;
2602 if ((format->flags & WINED3DFMT_FLAG_BLOCKS) && rect
2603 && !surface_check_block_align(surface, rect))
2605 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
2606 wine_dbgstr_rect(rect), format->block_width, format->block_height);
2608 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
2609 return WINED3DERR_INVALIDCALL;
2612 ++surface->resource.map_count;
2614 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
2615 WARN("Trying to lock unlockable surface.\n");
2617 /* Performance optimization: Count how often a surface is mapped, if it is
2618 * mapped regularly do not throw away the system memory copy. This avoids
2619 * the need to download the surface from OpenGL all the time. The surface
2620 * is still downloaded if the OpenGL texture is changed. Note that this
2621 * only really makes sense for managed textures.*/
2622 if (!(surface->container->flags & WINED3D_TEXTURE_DYNAMIC_MAP)
2623 && surface->resource.map_binding == WINED3D_LOCATION_SYSMEM)
2625 if (++surface->lockCount > MAXLOCKCOUNT)
2627 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
2628 surface->container->flags |= WINED3D_TEXTURE_DYNAMIC_MAP;
2632 surface_prepare_map_memory(surface);
2633 if (flags & WINED3D_MAP_DISCARD)
2635 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
2636 wined3d_debug_location(surface->resource.map_binding));
2637 surface_validate_location(surface, surface->resource.map_binding);
2639 else
2641 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
2642 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
2644 surface_load_location(surface, surface->resource.map_binding);
2647 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
2648 surface_invalidate_location(surface, ~surface->resource.map_binding);
2650 switch (surface->resource.map_binding)
2652 case WINED3D_LOCATION_SYSMEM:
2653 base_memory = surface->resource.heap_memory;
2654 break;
2656 case WINED3D_LOCATION_USER_MEMORY:
2657 base_memory = surface->user_memory;
2658 break;
2660 case WINED3D_LOCATION_DIB:
2661 base_memory = surface->dib.bitmap_data;
2662 break;
2664 case WINED3D_LOCATION_BUFFER:
2665 context = context_acquire(device, NULL);
2666 gl_info = context->gl_info;
2668 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
2669 base_memory = GL_EXTCALL(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_WRITE));
2670 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
2671 checkGLcall("map PBO");
2673 context_release(context);
2674 break;
2676 default:
2677 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
2678 base_memory = NULL;
2681 if (format->flags & WINED3DFMT_FLAG_BROKEN_PITCH)
2682 map_desc->row_pitch = surface->resource.width * format->byte_count;
2683 else
2684 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
2685 map_desc->slice_pitch = 0;
2687 if (!rect)
2689 map_desc->data = base_memory;
2690 surface->lockedRect.left = 0;
2691 surface->lockedRect.top = 0;
2692 surface->lockedRect.right = surface->resource.width;
2693 surface->lockedRect.bottom = surface->resource.height;
2695 else
2697 if ((format->flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
2699 /* Compressed textures are block based, so calculate the offset of
2700 * the block that contains the top-left pixel of the locked rectangle. */
2701 map_desc->data = base_memory
2702 + ((rect->top / format->block_height) * map_desc->row_pitch)
2703 + ((rect->left / format->block_width) * format->block_byte_count);
2705 else
2707 map_desc->data = base_memory
2708 + (map_desc->row_pitch * rect->top)
2709 + (rect->left * format->byte_count);
2711 surface->lockedRect.left = rect->left;
2712 surface->lockedRect.top = rect->top;
2713 surface->lockedRect.right = rect->right;
2714 surface->lockedRect.bottom = rect->bottom;
2717 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
2718 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
2720 return WINED3D_OK;
2723 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
2725 HRESULT hr;
2727 TRACE("surface %p, dc %p.\n", surface, dc);
2729 /* Give more detailed info for ddraw. */
2730 if (surface->flags & SFLAG_DCINUSE)
2731 return WINEDDERR_DCALREADYCREATED;
2733 /* Can't GetDC if the surface is locked. */
2734 if (surface->resource.map_count)
2735 return WINED3DERR_INVALIDCALL;
2737 /* Create a DIB section if there isn't a dc yet. */
2738 if (!surface->hDC)
2740 if (surface->flags & SFLAG_CLIENT)
2742 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
2743 surface_release_client_storage(surface);
2745 hr = surface_create_dib_section(surface);
2746 if (FAILED(hr))
2747 return WINED3DERR_INVALIDCALL;
2748 if (!(surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2749 || surface->container->flags & WINED3D_TEXTURE_PIN_SYSMEM
2750 || surface->pbo))
2751 surface->resource.map_binding = WINED3D_LOCATION_DIB;
2754 surface_load_location(surface, WINED3D_LOCATION_DIB);
2755 surface_invalidate_location(surface, ~WINED3D_LOCATION_DIB);
2757 surface->flags |= SFLAG_DCINUSE;
2758 surface->resource.map_count++;
2760 *dc = surface->hDC;
2761 TRACE("Returning dc %p.\n", *dc);
2763 return WINED3D_OK;
2766 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
2768 TRACE("surface %p, dc %p.\n", surface, dc);
2770 if (!(surface->flags & SFLAG_DCINUSE))
2771 return WINEDDERR_NODC;
2773 if (surface->hDC != dc)
2775 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
2776 dc, surface->hDC);
2777 return WINEDDERR_NODC;
2780 surface->resource.map_count--;
2781 surface->flags &= ~SFLAG_DCINUSE;
2783 if (surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2784 || (surface->container->flags & WINED3D_TEXTURE_PIN_SYSMEM
2785 && surface->resource.map_binding != WINED3D_LOCATION_DIB))
2787 /* The game Salammbo modifies the surface contents without mapping the surface between
2788 * a GetDC/ReleaseDC operation and flipping the surface. If the DIB remains the active
2789 * copy and is copied to the screen, this update, which draws the mouse pointer, is lost.
2790 * Do not only copy the DIB to the map location, but also make sure the map location is
2791 * copied back to the DIB in the next getdc call.
2793 * The same consideration applies to user memory surfaces. */
2794 surface_load_location(surface, surface->resource.map_binding);
2795 surface_invalidate_location(surface, WINED3D_LOCATION_DIB);
2798 return WINED3D_OK;
2801 static void read_from_framebuffer(struct wined3d_surface *surface, DWORD dst_location)
2803 struct wined3d_device *device = surface->resource.device;
2804 const struct wined3d_gl_info *gl_info;
2805 struct wined3d_context *context;
2806 BYTE *mem;
2807 BYTE *row, *top, *bottom;
2808 int i;
2809 BOOL srcIsUpsideDown;
2810 struct wined3d_bo_address data;
2812 surface_get_memory(surface, &data, dst_location);
2814 context = context_acquire(device, surface);
2815 context_apply_blit_state(context, device);
2816 gl_info = context->gl_info;
2818 /* Select the correct read buffer, and give some debug output.
2819 * There is no need to keep track of the current read buffer or reset it, every part of the code
2820 * that reads sets the read buffer as desired.
2822 if (wined3d_resource_is_offscreen(&surface->container->resource))
2824 /* Mapping the primary render target which is not on a swapchain.
2825 * Read from the back buffer. */
2826 TRACE("Mapping offscreen render target.\n");
2827 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2828 srcIsUpsideDown = TRUE;
2830 else
2832 /* Onscreen surfaces are always part of a swapchain */
2833 GLenum buffer = surface_get_gl_buffer(surface);
2834 TRACE("Mapping %#x buffer.\n", buffer);
2835 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
2836 checkGLcall("glReadBuffer");
2837 srcIsUpsideDown = FALSE;
2840 if (data.buffer_object)
2842 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
2843 checkGLcall("glBindBuffer");
2846 /* Setup pixel store pack state -- to glReadPixels into the correct place */
2847 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, surface->resource.width);
2848 checkGLcall("glPixelStorei");
2850 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
2851 surface->resource.width, surface->resource.height,
2852 surface->resource.format->glFormat,
2853 surface->resource.format->glType, data.addr);
2854 checkGLcall("glReadPixels");
2856 /* Reset previous pixel store pack state */
2857 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
2858 checkGLcall("glPixelStorei");
2860 if (!srcIsUpsideDown)
2862 /* glReadPixels returns the image upside down, and there is no way to prevent this.
2863 * Flip the lines in software. */
2864 UINT pitch = wined3d_surface_get_pitch(surface);
2866 if (!(row = HeapAlloc(GetProcessHeap(), 0, pitch)))
2867 goto error;
2869 if (data.buffer_object)
2871 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
2872 checkGLcall("glMapBuffer");
2874 else
2875 mem = data.addr;
2877 top = mem;
2878 bottom = mem + pitch * (surface->resource.height - 1);
2879 for (i = 0; i < surface->resource.height / 2; i++)
2881 memcpy(row, top, pitch);
2882 memcpy(top, bottom, pitch);
2883 memcpy(bottom, row, pitch);
2884 top += pitch;
2885 bottom -= pitch;
2887 HeapFree(GetProcessHeap(), 0, row);
2889 if (data.buffer_object)
2890 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
2893 error:
2894 if (data.buffer_object)
2896 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
2897 checkGLcall("glBindBuffer");
2900 context_release(context);
2903 /* Read the framebuffer contents into a texture. Note that this function
2904 * doesn't do any kind of flipping. Using this on an onscreen surface will
2905 * result in a flipped D3D texture. */
2906 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
2908 struct wined3d_device *device = surface->resource.device;
2909 const struct wined3d_gl_info *gl_info;
2910 struct wined3d_context *context;
2912 context = context_acquire(device, surface);
2913 gl_info = context->gl_info;
2914 device_invalidate_state(device, STATE_FRAMEBUFFER);
2916 wined3d_texture_prepare_texture(surface->container, context, srgb);
2917 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
2919 TRACE("Reading back offscreen render target %p.\n", surface);
2921 if (wined3d_resource_is_offscreen(&surface->container->resource))
2922 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2923 else
2924 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
2925 checkGLcall("glReadBuffer");
2927 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
2928 0, 0, 0, 0, surface->resource.width, surface->resource.height);
2929 checkGLcall("glCopyTexSubImage2D");
2931 context_release(context);
2934 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
2936 if (multisample)
2938 if (surface->rb_multisample)
2939 return;
2941 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
2942 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
2943 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, surface->resource.multisample_type,
2944 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
2945 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
2947 else
2949 if (surface->rb_resolved)
2950 return;
2952 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
2953 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
2954 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
2955 surface->pow2Width, surface->pow2Height);
2956 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
2960 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
2962 if (front->container->level_count != 1 || front->container->layer_count != 1
2963 || back->container->level_count != 1 || back->container->layer_count != 1)
2964 ERR("Flip between surfaces %p and %p not supported.\n", front, back);
2966 /* Flip the surface contents */
2967 /* Flip the DC */
2969 HDC tmp;
2970 tmp = front->hDC;
2971 front->hDC = back->hDC;
2972 back->hDC = tmp;
2975 /* Flip the DIBsection */
2977 HBITMAP tmp = front->dib.DIBsection;
2978 front->dib.DIBsection = back->dib.DIBsection;
2979 back->dib.DIBsection = tmp;
2982 /* Flip the surface data */
2984 void* tmp;
2986 tmp = front->dib.bitmap_data;
2987 front->dib.bitmap_data = back->dib.bitmap_data;
2988 back->dib.bitmap_data = tmp;
2990 tmp = front->resource.heap_memory;
2991 front->resource.heap_memory = back->resource.heap_memory;
2992 back->resource.heap_memory = tmp;
2995 /* Flip the PBO */
2997 GLuint tmp_pbo = front->pbo;
2998 front->pbo = back->pbo;
2999 back->pbo = tmp_pbo;
3002 /* Flip the opengl texture */
3004 GLuint tmp;
3006 tmp = back->container->texture_rgb.name;
3007 back->container->texture_rgb.name = front->container->texture_rgb.name;
3008 front->container->texture_rgb.name = tmp;
3010 tmp = back->container->texture_srgb.name;
3011 back->container->texture_srgb.name = front->container->texture_srgb.name;
3012 front->container->texture_srgb.name = tmp;
3014 tmp = back->rb_multisample;
3015 back->rb_multisample = front->rb_multisample;
3016 front->rb_multisample = tmp;
3018 tmp = back->rb_resolved;
3019 back->rb_resolved = front->rb_resolved;
3020 front->rb_resolved = tmp;
3022 resource_unload(&back->resource);
3023 resource_unload(&front->resource);
3027 DWORD tmp_flags = back->flags;
3028 back->flags = front->flags;
3029 front->flags = tmp_flags;
3031 tmp_flags = back->locations;
3032 back->locations = front->locations;
3033 front->locations = tmp_flags;
3037 /* Does a direct frame buffer -> texture copy. Stretching is done with single
3038 * pixel copy calls. */
3039 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3040 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3042 struct wined3d_device *device = dst_surface->resource.device;
3043 const struct wined3d_gl_info *gl_info;
3044 float xrel, yrel;
3045 struct wined3d_context *context;
3046 BOOL upsidedown = FALSE;
3047 RECT dst_rect = *dst_rect_in;
3049 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3050 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3052 if(dst_rect.top > dst_rect.bottom) {
3053 UINT tmp = dst_rect.bottom;
3054 dst_rect.bottom = dst_rect.top;
3055 dst_rect.top = tmp;
3056 upsidedown = TRUE;
3059 context = context_acquire(device, src_surface);
3060 gl_info = context->gl_info;
3061 context_apply_blit_state(context, device);
3062 wined3d_texture_load(dst_surface->container, context, FALSE);
3064 /* Bind the target texture */
3065 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
3066 if (wined3d_resource_is_offscreen(&src_surface->container->resource))
3068 TRACE("Reading from an offscreen target\n");
3069 upsidedown = !upsidedown;
3070 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3072 else
3074 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3076 checkGLcall("glReadBuffer");
3078 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
3079 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
3081 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3083 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3085 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3086 ERR("Texture filtering not supported in direct blit.\n");
3088 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3089 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3091 ERR("Texture filtering not supported in direct blit\n");
3094 if (upsidedown
3095 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3096 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3098 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
3099 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3100 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
3101 src_rect->left, src_surface->resource.height - src_rect->bottom,
3102 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3104 else
3106 LONG row;
3107 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
3108 /* I have to process this row by row to swap the image,
3109 * otherwise it would be upside down, so stretching in y direction
3110 * doesn't cost extra time
3112 * However, stretching in x direction can be avoided if not necessary
3114 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
3115 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3117 /* Well, that stuff works, but it's very slow.
3118 * find a better way instead
3120 LONG col;
3122 for (col = dst_rect.left; col < dst_rect.right; ++col)
3124 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3125 dst_rect.left + col /* x offset */, row /* y offset */,
3126 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3129 else
3131 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3132 dst_rect.left /* x offset */, row /* y offset */,
3133 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
3137 checkGLcall("glCopyTexSubImage2D");
3139 context_release(context);
3141 /* The texture is now most up to date - If the surface is a render target
3142 * and has a drawable, this path is never entered. */
3143 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3144 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3147 /* Uses the hardware to stretch and flip the image */
3148 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3149 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3151 struct wined3d_device *device = dst_surface->resource.device;
3152 GLuint src, backup = 0;
3153 float left, right, top, bottom; /* Texture coordinates */
3154 UINT fbwidth = src_surface->resource.width;
3155 UINT fbheight = src_surface->resource.height;
3156 const struct wined3d_gl_info *gl_info;
3157 struct wined3d_context *context;
3158 GLenum drawBuffer = GL_BACK;
3159 GLenum texture_target;
3160 BOOL noBackBufferBackup;
3161 BOOL src_offscreen;
3162 BOOL upsidedown = FALSE;
3163 RECT dst_rect = *dst_rect_in;
3165 TRACE("Using hwstretch blit\n");
3166 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3167 context = context_acquire(device, src_surface);
3168 gl_info = context->gl_info;
3169 context_apply_blit_state(context, device);
3170 wined3d_texture_load(dst_surface->container, context, FALSE);
3172 src_offscreen = wined3d_resource_is_offscreen(&src_surface->container->resource);
3173 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3174 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
3176 /* Get it a description */
3177 wined3d_texture_load(src_surface->container, context, FALSE);
3180 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3181 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3183 if (context->aux_buffers >= 2)
3185 /* Got more than one aux buffer? Use the 2nd aux buffer */
3186 drawBuffer = GL_AUX1;
3188 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3190 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3191 drawBuffer = GL_AUX0;
3194 if (noBackBufferBackup)
3196 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
3197 checkGLcall("glGenTextures");
3198 context_bind_texture(context, GL_TEXTURE_2D, backup);
3199 texture_target = GL_TEXTURE_2D;
3201 else
3203 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3204 * we are reading from the back buffer, the backup can be used as source texture
3206 texture_target = src_surface->texture_target;
3207 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
3208 gl_info->gl_ops.gl.p_glEnable(texture_target);
3209 checkGLcall("glEnable(texture_target)");
3211 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3212 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
3215 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3216 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3218 if(dst_rect.top > dst_rect.bottom) {
3219 UINT tmp = dst_rect.bottom;
3220 dst_rect.bottom = dst_rect.top;
3221 dst_rect.top = tmp;
3222 upsidedown = TRUE;
3225 if (src_offscreen)
3227 TRACE("Reading from an offscreen target\n");
3228 upsidedown = !upsidedown;
3229 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3231 else
3233 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3236 /* TODO: Only back up the part that will be overwritten */
3237 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
3239 checkGLcall("glCopyTexSubImage2D");
3241 /* No issue with overriding these - the sampler is dirty due to blit usage */
3242 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER,
3243 wined3d_gl_mag_filter(magLookup, filter));
3244 checkGLcall("glTexParameteri");
3245 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3246 wined3d_gl_min_mip_filter(minMipLookup, filter, WINED3D_TEXF_NONE));
3247 checkGLcall("glTexParameteri");
3249 if (!src_surface->container->swapchain
3250 || src_surface->container == src_surface->container->swapchain->back_buffers[0])
3252 src = backup ? backup : src_surface->container->texture_rgb.name;
3254 else
3256 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
3257 checkGLcall("glReadBuffer(GL_FRONT)");
3259 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
3260 checkGLcall("glGenTextures(1, &src)");
3261 context_bind_texture(context, GL_TEXTURE_2D, src);
3263 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3264 * out for power of 2 sizes
3266 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
3267 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3268 checkGLcall("glTexImage2D");
3269 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
3271 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3272 checkGLcall("glTexParameteri");
3273 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3274 checkGLcall("glTexParameteri");
3276 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
3277 checkGLcall("glReadBuffer(GL_BACK)");
3279 if (texture_target != GL_TEXTURE_2D)
3281 gl_info->gl_ops.gl.p_glDisable(texture_target);
3282 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3283 texture_target = GL_TEXTURE_2D;
3286 checkGLcall("glEnd and previous");
3288 left = src_rect->left;
3289 right = src_rect->right;
3291 if (!upsidedown)
3293 top = src_surface->resource.height - src_rect->top;
3294 bottom = src_surface->resource.height - src_rect->bottom;
3296 else
3298 top = src_surface->resource.height - src_rect->bottom;
3299 bottom = src_surface->resource.height - src_rect->top;
3302 if (src_surface->container->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
3304 left /= src_surface->pow2Width;
3305 right /= src_surface->pow2Width;
3306 top /= src_surface->pow2Height;
3307 bottom /= src_surface->pow2Height;
3310 /* draw the source texture stretched and upside down. The correct surface is bound already */
3311 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP);
3312 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP);
3314 context_set_draw_buffer(context, drawBuffer);
3315 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
3317 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3318 /* bottom left */
3319 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
3320 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3322 /* top left */
3323 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
3324 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
3326 /* top right */
3327 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
3328 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3330 /* bottom right */
3331 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
3332 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
3333 gl_info->gl_ops.gl.p_glEnd();
3334 checkGLcall("glEnd and previous");
3336 if (texture_target != dst_surface->texture_target)
3338 gl_info->gl_ops.gl.p_glDisable(texture_target);
3339 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
3340 texture_target = dst_surface->texture_target;
3343 /* Now read the stretched and upside down image into the destination texture */
3344 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
3345 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
3347 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
3348 0, 0, /* We blitted the image to the origin */
3349 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3350 checkGLcall("glCopyTexSubImage2D");
3352 if (drawBuffer == GL_BACK)
3354 /* Write the back buffer backup back. */
3355 if (backup)
3357 if (texture_target != GL_TEXTURE_2D)
3359 gl_info->gl_ops.gl.p_glDisable(texture_target);
3360 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3361 texture_target = GL_TEXTURE_2D;
3363 context_bind_texture(context, GL_TEXTURE_2D, backup);
3365 else
3367 if (texture_target != src_surface->texture_target)
3369 gl_info->gl_ops.gl.p_glDisable(texture_target);
3370 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
3371 texture_target = src_surface->texture_target;
3373 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
3376 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3377 /* top left */
3378 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
3379 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
3381 /* bottom left */
3382 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
3383 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3385 /* bottom right */
3386 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
3387 (float)fbheight / (float)src_surface->pow2Height);
3388 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
3390 /* top right */
3391 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
3392 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
3393 gl_info->gl_ops.gl.p_glEnd();
3395 gl_info->gl_ops.gl.p_glDisable(texture_target);
3396 checkGLcall("glDisable(texture_target)");
3398 /* Cleanup */
3399 if (src != src_surface->container->texture_rgb.name && src != backup)
3401 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
3402 checkGLcall("glDeleteTextures(1, &src)");
3404 if (backup)
3406 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
3407 checkGLcall("glDeleteTextures(1, &backup)");
3410 if (wined3d_settings.strict_draw_ordering)
3411 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3413 context_release(context);
3415 /* The texture is now most up to date - If the surface is a render target
3416 * and has a drawable, this path is never entered. */
3417 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3418 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3421 /* Front buffer coordinates are always full screen coordinates, but our GL
3422 * drawable is limited to the window's client area. The sysmem and texture
3423 * copies do have the full screen size. Note that GL has a bottom-left
3424 * origin, while D3D has a top-left origin. */
3425 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
3427 UINT drawable_height;
3429 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
3431 POINT offset = {0, 0};
3432 RECT windowsize;
3434 ScreenToClient(window, &offset);
3435 OffsetRect(rect, offset.x, offset.y);
3437 GetClientRect(window, &windowsize);
3438 drawable_height = windowsize.bottom - windowsize.top;
3440 else
3442 drawable_height = surface->resource.height;
3445 rect->top = drawable_height - rect->top;
3446 rect->bottom = drawable_height - rect->bottom;
3449 static void surface_blt_to_drawable(const struct wined3d_device *device,
3450 enum wined3d_texture_filter_type filter, BOOL alpha_test,
3451 struct wined3d_surface *src_surface, const RECT *src_rect_in,
3452 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
3454 const struct wined3d_gl_info *gl_info;
3455 struct wined3d_context *context;
3456 RECT src_rect, dst_rect;
3458 src_rect = *src_rect_in;
3459 dst_rect = *dst_rect_in;
3461 context = context_acquire(device, dst_surface);
3462 gl_info = context->gl_info;
3464 /* Make sure the surface is up-to-date. This should probably use
3465 * surface_load_location() and worry about the destination surface too,
3466 * unless we're overwriting it completely. */
3467 wined3d_texture_load(src_surface->container, context, FALSE);
3469 /* Activate the destination context, set it up for blitting */
3470 context_apply_blit_state(context, device);
3472 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
3473 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
3475 device->blitter->set_shader(device->blit_priv, context, src_surface);
3477 if (alpha_test)
3479 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
3480 checkGLcall("glEnable(GL_ALPHA_TEST)");
3482 /* For P8 surfaces, the alpha component contains the palette index.
3483 * Which means that the colorkey is one of the palette entries. In
3484 * other cases pixels that should be masked away have alpha set to 0. */
3485 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
3486 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
3487 (float)src_surface->container->src_blt_color_key.color_space_low_value / 256.0f);
3488 else
3489 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
3490 checkGLcall("glAlphaFunc");
3492 else
3494 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3495 checkGLcall("glDisable(GL_ALPHA_TEST)");
3498 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
3500 if (alpha_test)
3502 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3503 checkGLcall("glDisable(GL_ALPHA_TEST)");
3506 /* Leave the opengl state valid for blitting */
3507 device->blitter->unset_shader(context->gl_info);
3509 if (wined3d_settings.strict_draw_ordering
3510 || (dst_surface->container->swapchain
3511 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
3512 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3514 context_release(context);
3517 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
3519 struct wined3d_device *device = s->resource.device;
3520 const struct blit_shader *blitter;
3522 blitter = wined3d_select_blitter(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_FILL,
3523 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
3524 if (!blitter)
3526 FIXME("No blitter is capable of performing the requested color fill operation.\n");
3527 return WINED3DERR_INVALIDCALL;
3530 return blitter->color_fill(device, s, rect, color);
3533 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3534 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
3535 enum wined3d_texture_filter_type filter)
3537 struct wined3d_device *device = dst_surface->resource.device;
3538 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
3539 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
3540 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3542 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
3543 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3544 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
3546 /* Get the swapchain. One of the surfaces has to be a primary surface */
3547 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3549 WARN("Destination is in sysmem, rejecting gl blt\n");
3550 return WINED3DERR_INVALIDCALL;
3553 dst_swapchain = dst_surface->container->swapchain;
3555 if (src_surface)
3557 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3559 WARN("Src is in sysmem, rejecting gl blt\n");
3560 return WINED3DERR_INVALIDCALL;
3563 src_swapchain = src_surface->container->swapchain;
3565 else
3567 src_swapchain = NULL;
3570 /* Early sort out of cases where no render target is used */
3571 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
3573 TRACE("No surface is render target, not using hardware blit.\n");
3574 return WINED3DERR_INVALIDCALL;
3577 /* No destination color keying supported */
3578 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
3580 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3581 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3582 return WINED3DERR_INVALIDCALL;
3585 if (dst_swapchain && dst_swapchain == src_swapchain)
3587 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3588 return WINED3DERR_INVALIDCALL;
3591 if (dst_swapchain && src_swapchain)
3593 FIXME("Implement hardware blit between two different swapchains\n");
3594 return WINED3DERR_INVALIDCALL;
3597 if (dst_swapchain)
3599 /* Handled with regular texture -> swapchain blit */
3600 if (src_surface == rt)
3601 TRACE("Blit from active render target to a swapchain\n");
3603 else if (src_swapchain && dst_surface == rt)
3605 FIXME("Implement blit from a swapchain to the active render target\n");
3606 return WINED3DERR_INVALIDCALL;
3609 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
3611 /* Blit from render target to texture */
3612 BOOL stretchx;
3614 /* P8 read back is not implemented */
3615 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
3616 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
3618 TRACE("P8 read back not supported by frame buffer to texture blit\n");
3619 return WINED3DERR_INVALIDCALL;
3622 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3624 TRACE("Color keying not supported by frame buffer to texture blit\n");
3625 return WINED3DERR_INVALIDCALL;
3626 /* Destination color key is checked above */
3629 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
3630 stretchx = TRUE;
3631 else
3632 stretchx = FALSE;
3634 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3635 * flip the image nor scale it.
3637 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3638 * -> If the app wants an image width an unscaled width, copy it line per line
3639 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
3640 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3641 * back buffer. This is slower than reading line per line, thus not used for flipping
3642 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3643 * pixel by pixel. */
3644 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
3645 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
3647 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
3648 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
3650 else
3652 TRACE("Using hardware stretching to flip / stretch the texture.\n");
3653 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
3656 surface_evict_sysmem(dst_surface);
3658 return WINED3D_OK;
3660 else if (src_surface)
3662 /* Blit from offscreen surface to render target */
3663 struct wined3d_color_key old_blt_key = src_surface->container->src_blt_color_key;
3664 DWORD old_color_key_flags = src_surface->container->color_key_flags;
3666 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
3668 if (!device->blitter->blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3669 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
3670 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
3672 FIXME("Unsupported blit operation falling back to software\n");
3673 return WINED3DERR_INVALIDCALL;
3676 /* Color keying: Check if we have to do a color keyed blt,
3677 * and if not check if a color key is activated.
3679 * Just modify the color keying parameters in the surface and restore them afterwards
3680 * The surface keeps track of the color key last used to load the opengl surface.
3681 * PreLoad will catch the change to the flags and color key and reload if necessary.
3683 if (flags & WINEDDBLT_KEYSRC)
3685 /* Use color key from surface */
3687 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
3689 /* Use color key from DDBltFx */
3690 wined3d_texture_set_color_key(src_surface->container, WINEDDSD_CKSRCBLT, &DDBltFx->ddckSrcColorkey);
3692 else
3694 /* Do not use color key */
3695 wined3d_texture_set_color_key(src_surface->container, WINEDDSD_CKSRCBLT, NULL);
3698 surface_blt_to_drawable(device, filter,
3699 flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_ALPHATEST),
3700 src_surface, src_rect, dst_surface, dst_rect);
3702 /* Restore the color key parameters */
3703 wined3d_texture_set_color_key(src_surface->container, WINEDDSD_CKSRCBLT,
3704 (old_color_key_flags & WINEDDSD_CKSRCBLT) ? &old_blt_key : NULL);
3706 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
3707 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
3709 return WINED3D_OK;
3712 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3713 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3714 return WINED3DERR_INVALIDCALL;
3717 /* Context activation is done by the caller. */
3718 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
3719 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
3721 struct wined3d_device *device = surface->resource.device;
3722 const struct wined3d_gl_info *gl_info = context->gl_info;
3723 GLint compare_mode = GL_NONE;
3724 struct blt_info info;
3725 GLint old_binding = 0;
3726 RECT rect;
3728 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
3730 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
3731 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
3732 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3733 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
3734 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
3735 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
3736 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
3737 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
3738 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
3739 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
3740 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
3742 SetRect(&rect, 0, h, w, 0);
3743 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
3744 context_active_texture(context, context->gl_info, 0);
3745 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
3746 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
3747 if (gl_info->supported[ARB_SHADOW])
3749 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
3750 if (compare_mode != GL_NONE)
3751 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
3754 device->shader_backend->shader_select_depth_blt(device->shader_priv,
3755 gl_info, info.tex_type, &surface->ds_current_size);
3757 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
3758 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
3759 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
3760 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
3761 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
3762 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
3763 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
3764 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
3765 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
3766 gl_info->gl_ops.gl.p_glEnd();
3768 if (compare_mode != GL_NONE)
3769 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
3770 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
3772 gl_info->gl_ops.gl.p_glPopAttrib();
3774 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
3777 void surface_modify_ds_location(struct wined3d_surface *surface,
3778 DWORD location, UINT w, UINT h)
3780 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
3782 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
3783 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
3784 wined3d_texture_set_dirty(surface->container);
3786 surface->ds_current_size.cx = w;
3787 surface->ds_current_size.cy = h;
3788 surface->locations = location;
3791 /* Context activation is done by the caller. */
3792 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3794 const struct wined3d_gl_info *gl_info = context->gl_info;
3795 struct wined3d_device *device = surface->resource.device;
3796 GLsizei w, h;
3798 TRACE("surface %p, new location %#x.\n", surface, location);
3800 /* TODO: Make this work for modes other than FBO */
3801 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
3803 if (!(surface->locations & location))
3805 w = surface->ds_current_size.cx;
3806 h = surface->ds_current_size.cy;
3807 surface->ds_current_size.cx = 0;
3808 surface->ds_current_size.cy = 0;
3810 else
3812 w = surface->resource.width;
3813 h = surface->resource.height;
3816 if (surface->ds_current_size.cx == surface->resource.width
3817 && surface->ds_current_size.cy == surface->resource.height)
3819 TRACE("Location (%#x) is already up to date.\n", location);
3820 return;
3823 if (surface->current_renderbuffer)
3825 FIXME("Not supported with fixed up depth stencil.\n");
3826 return;
3829 if (surface->locations & WINED3D_LOCATION_DISCARDED)
3831 TRACE("Surface was discarded, no need copy data.\n");
3832 switch (location)
3834 case WINED3D_LOCATION_TEXTURE_RGB:
3835 wined3d_texture_prepare_texture(surface->container, context, FALSE);
3836 break;
3837 case WINED3D_LOCATION_RB_MULTISAMPLE:
3838 surface_prepare_rb(surface, gl_info, TRUE);
3839 break;
3840 case WINED3D_LOCATION_DRAWABLE:
3841 /* Nothing to do */
3842 break;
3843 default:
3844 FIXME("Unhandled location %#x\n", location);
3846 surface->locations &= ~WINED3D_LOCATION_DISCARDED;
3847 surface->locations |= location;
3848 surface->ds_current_size.cx = surface->resource.width;
3849 surface->ds_current_size.cy = surface->resource.height;
3850 return;
3853 if (!surface->locations)
3855 FIXME("No up to date depth stencil location.\n");
3856 surface->locations |= location;
3857 surface->ds_current_size.cx = surface->resource.width;
3858 surface->ds_current_size.cy = surface->resource.height;
3859 return;
3862 if (location == WINED3D_LOCATION_TEXTURE_RGB)
3864 GLint old_binding = 0;
3865 GLenum bind_target;
3867 /* The render target is allowed to be smaller than the depth/stencil
3868 * buffer, so the onscreen depth/stencil buffer is potentially smaller
3869 * than the offscreen surface. Don't overwrite the offscreen surface
3870 * with undefined data. */
3871 w = min(w, context->swapchain->desc.backbuffer_width);
3872 h = min(h, context->swapchain->desc.backbuffer_height);
3874 TRACE("Copying onscreen depth buffer to depth texture.\n");
3876 if (!device->depth_blt_texture)
3877 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
3879 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
3880 * directly on the FBO texture. That's because we need to flip. */
3881 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3882 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3883 NULL, WINED3D_LOCATION_DRAWABLE);
3884 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
3886 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
3887 bind_target = GL_TEXTURE_RECTANGLE_ARB;
3889 else
3891 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
3892 bind_target = GL_TEXTURE_2D;
3894 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
3895 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
3896 * internal format, because the internal format might include stencil
3897 * data. In principle we should copy stencil data as well, but unless
3898 * the driver supports stencil export it's hard to do, and doesn't
3899 * seem to be needed in practice. If the hardware doesn't support
3900 * writing stencil data, the glCopyTexImage2D() call might trigger
3901 * software fallbacks. */
3902 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
3903 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3904 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3905 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3906 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3907 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
3908 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
3909 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
3911 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3912 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
3913 context_set_draw_buffer(context, GL_NONE);
3915 /* Do the actual blit */
3916 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
3917 checkGLcall("depth_blt");
3919 context_invalidate_state(context, STATE_FRAMEBUFFER);
3921 if (wined3d_settings.strict_draw_ordering)
3922 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3924 else if (location == WINED3D_LOCATION_DRAWABLE)
3926 TRACE("Copying depth texture to onscreen depth buffer.\n");
3928 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3929 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3930 NULL, WINED3D_LOCATION_DRAWABLE);
3931 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
3932 0, surface->pow2Height - h, w, h, surface->texture_target);
3933 checkGLcall("depth_blt");
3935 context_invalidate_state(context, STATE_FRAMEBUFFER);
3937 if (wined3d_settings.strict_draw_ordering)
3938 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3940 else
3942 ERR("Invalid location (%#x) specified.\n", location);
3945 surface->locations |= location;
3946 surface->ds_current_size.cx = surface->resource.width;
3947 surface->ds_current_size.cy = surface->resource.height;
3950 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
3952 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3954 surface->locations |= location;
3957 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
3959 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3961 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3962 wined3d_texture_set_dirty(surface->container);
3963 surface->locations &= ~location;
3965 if (!surface->locations)
3966 ERR("Surface %p does not have any up to date location.\n", surface);
3969 static DWORD resource_access_from_location(DWORD location)
3971 switch (location)
3973 case WINED3D_LOCATION_SYSMEM:
3974 case WINED3D_LOCATION_USER_MEMORY:
3975 case WINED3D_LOCATION_DIB:
3976 case WINED3D_LOCATION_BUFFER:
3977 return WINED3D_RESOURCE_ACCESS_CPU;
3979 case WINED3D_LOCATION_DRAWABLE:
3980 case WINED3D_LOCATION_TEXTURE_SRGB:
3981 case WINED3D_LOCATION_TEXTURE_RGB:
3982 case WINED3D_LOCATION_RB_MULTISAMPLE:
3983 case WINED3D_LOCATION_RB_RESOLVED:
3984 return WINED3D_RESOURCE_ACCESS_GPU;
3986 default:
3987 FIXME("Unhandled location %#x.\n", location);
3988 return 0;
3992 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
3994 struct wined3d_device *device = surface->resource.device;
3995 struct wined3d_context *context;
3996 const struct wined3d_gl_info *gl_info;
3997 struct wined3d_bo_address dst, src;
3998 UINT size = surface->resource.size;
4000 surface_get_memory(surface, &dst, location);
4001 surface_get_memory(surface, &src, surface->locations);
4003 if (dst.buffer_object)
4005 context = context_acquire(device, NULL);
4006 gl_info = context->gl_info;
4007 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, dst.buffer_object));
4008 GL_EXTCALL(glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, src.addr));
4009 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
4010 checkGLcall("Upload PBO");
4011 context_release(context);
4012 return;
4014 if (src.buffer_object)
4016 context = context_acquire(device, NULL);
4017 gl_info = context->gl_info;
4018 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, src.buffer_object));
4019 GL_EXTCALL(glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, size, dst.addr));
4020 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
4021 checkGLcall("Download PBO");
4022 context_release(context);
4023 return;
4025 memcpy(dst.addr, src.addr, size);
4028 static void surface_load_sysmem(struct wined3d_surface *surface,
4029 const struct wined3d_gl_info *gl_info, DWORD dst_location)
4031 if (surface->locations & surface_simple_locations)
4033 surface_copy_simple_location(surface, dst_location);
4034 return;
4037 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
4038 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
4040 /* Download the surface to system memory. */
4041 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
4043 struct wined3d_device *device = surface->resource.device;
4044 struct wined3d_context *context;
4046 /* TODO: Use already acquired context when possible. */
4047 context = context_acquire(device, NULL);
4049 wined3d_texture_bind_and_dirtify(surface->container, context,
4050 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
4051 surface_download_data(surface, gl_info, dst_location);
4053 context_release(context);
4055 return;
4058 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
4060 read_from_framebuffer(surface, dst_location);
4061 return;
4064 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
4065 surface, wined3d_debug_location(surface->locations));
4068 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
4069 const struct wined3d_gl_info *gl_info)
4071 RECT r;
4073 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
4074 && wined3d_resource_is_offscreen(&surface->container->resource))
4076 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
4077 return WINED3DERR_INVALIDCALL;
4080 surface_get_rect(surface, NULL, &r);
4081 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
4082 surface_blt_to_drawable(surface->resource.device,
4083 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
4085 return WINED3D_OK;
4088 static HRESULT surface_load_texture(struct wined3d_surface *surface,
4089 const struct wined3d_gl_info *gl_info, BOOL srgb)
4091 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
4092 struct wined3d_device *device = surface->resource.device;
4093 const struct wined3d_color_key_conversion *conversion;
4094 struct wined3d_texture *texture = surface->container;
4095 struct wined3d_context *context;
4096 UINT width, src_pitch, dst_pitch;
4097 struct wined3d_bo_address data;
4098 struct wined3d_format format;
4099 POINT dst_point = {0, 0};
4100 BYTE *mem = NULL;
4102 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
4103 && wined3d_resource_is_offscreen(&texture->resource)
4104 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
4106 surface_load_fb_texture(surface, srgb);
4108 return WINED3D_OK;
4111 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
4112 && (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
4113 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4114 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4115 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4117 if (srgb)
4118 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
4119 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
4120 else
4121 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
4122 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
4124 return WINED3D_OK;
4127 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
4128 && (!srgb || (surface->resource.format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
4129 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4130 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4131 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4133 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
4134 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
4135 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
4136 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4138 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
4139 &rect, surface, dst_location, &rect);
4141 return WINED3D_OK;
4144 /* Upload from system memory */
4146 if (srgb)
4148 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
4149 == WINED3D_LOCATION_TEXTURE_RGB)
4151 /* Performance warning... */
4152 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
4153 surface_prepare_map_memory(surface);
4154 surface_load_location(surface, surface->resource.map_binding);
4157 else
4159 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
4160 == WINED3D_LOCATION_TEXTURE_SRGB)
4162 /* Performance warning... */
4163 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
4164 surface_prepare_map_memory(surface);
4165 surface_load_location(surface, surface->resource.map_binding);
4169 if (!(surface->locations & surface_simple_locations))
4171 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
4172 /* Lets hope we get it from somewhere... */
4173 surface_prepare_system_memory(surface);
4174 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
4177 /* TODO: Use already acquired context when possible. */
4178 context = context_acquire(device, NULL);
4180 wined3d_texture_prepare_texture(texture, context, srgb);
4181 wined3d_texture_bind_and_dirtify(texture, context, srgb);
4183 if (texture->color_key_flags & WINEDDSD_CKSRCBLT)
4185 surface->flags |= SFLAG_GLCKEY;
4186 surface->gl_color_key = texture->src_blt_color_key;
4188 else surface->flags &= ~SFLAG_GLCKEY;
4190 width = surface->resource.width;
4191 src_pitch = wined3d_surface_get_pitch(surface);
4193 format = *texture->resource.format;
4194 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
4195 format = *wined3d_get_format(gl_info, conversion->dst_format);
4197 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
4198 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
4199 * getting called. */
4200 if ((format.convert || conversion) && surface->pbo)
4202 TRACE("Removing the pbo attached to surface %p.\n", surface);
4204 if (surface->flags & SFLAG_DIBSECTION)
4205 surface->resource.map_binding = WINED3D_LOCATION_DIB;
4206 else
4207 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
4209 surface_prepare_map_memory(surface);
4210 surface_load_location(surface, surface->resource.map_binding);
4211 surface_remove_pbo(surface, gl_info);
4214 surface_get_memory(surface, &data, surface->locations);
4215 if (format.convert)
4217 /* This code is entered for texture formats which need a fixup. */
4218 UINT height = surface->resource.height;
4220 format.byte_count = format.conv_byte_count;
4221 dst_pitch = wined3d_format_calculate_pitch(&format, width);
4222 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4224 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4226 ERR("Out of memory (%u).\n", dst_pitch * height);
4227 context_release(context);
4228 return E_OUTOFMEMORY;
4230 format.convert(data.addr, mem, src_pitch, src_pitch * height,
4231 dst_pitch, dst_pitch * height, width, height, 1);
4232 src_pitch = dst_pitch;
4233 data.addr = mem;
4235 else if (conversion)
4237 /* This code is only entered for color keying fixups */
4238 struct wined3d_palette *palette = NULL;
4239 UINT height = surface->resource.height;
4241 dst_pitch = wined3d_format_calculate_pitch(&format, width);
4242 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4244 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4246 ERR("Out of memory (%u).\n", dst_pitch * height);
4247 context_release(context);
4248 return E_OUTOFMEMORY;
4250 if (texture->swapchain && texture->swapchain->palette)
4251 palette = texture->swapchain->palette;
4252 conversion->convert(data.addr, src_pitch, mem, dst_pitch,
4253 width, height, palette, &texture->src_blt_color_key);
4254 src_pitch = dst_pitch;
4255 data.addr = mem;
4258 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
4259 src_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
4261 context_release(context);
4263 HeapFree(GetProcessHeap(), 0, mem);
4265 return WINED3D_OK;
4268 static void surface_multisample_resolve(struct wined3d_surface *surface)
4270 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4272 if (!(surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE))
4273 ERR("Trying to resolve multisampled surface %p, but location WINED3D_LOCATION_RB_MULTISAMPLE not current.\n",
4274 surface);
4276 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
4277 surface, WINED3D_LOCATION_RB_MULTISAMPLE, &rect, surface, WINED3D_LOCATION_RB_RESOLVED, &rect);
4280 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location)
4282 struct wined3d_device *device = surface->resource.device;
4283 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4284 HRESULT hr;
4286 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4288 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
4290 if (location == WINED3D_LOCATION_TEXTURE_RGB
4291 && surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_DISCARDED))
4293 struct wined3d_context *context = context_acquire(device, NULL);
4294 surface_load_ds_location(surface, context, location);
4295 context_release(context);
4296 return WINED3D_OK;
4298 else if (location & surface->locations
4299 && surface->container->resource.draw_binding != WINED3D_LOCATION_DRAWABLE)
4301 /* Already up to date, nothing to do. */
4302 return WINED3D_OK;
4304 else
4306 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
4307 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
4308 return WINED3DERR_INVALIDCALL;
4312 if (surface->locations & location)
4314 TRACE("Location already up to date.\n");
4315 return WINED3D_OK;
4318 if (WARN_ON(d3d_surface))
4320 DWORD required_access = resource_access_from_location(location);
4321 if ((surface->resource.access_flags & required_access) != required_access)
4322 WARN("Operation requires %#x access, but surface only has %#x.\n",
4323 required_access, surface->resource.access_flags);
4326 if (!surface->locations)
4328 ERR("Surface %p does not have any up to date location.\n", surface);
4329 surface->flags |= SFLAG_LOST;
4330 return WINED3DERR_DEVICELOST;
4333 switch (location)
4335 case WINED3D_LOCATION_DIB:
4336 case WINED3D_LOCATION_USER_MEMORY:
4337 case WINED3D_LOCATION_SYSMEM:
4338 case WINED3D_LOCATION_BUFFER:
4339 surface_load_sysmem(surface, gl_info, location);
4340 break;
4342 case WINED3D_LOCATION_DRAWABLE:
4343 if (FAILED(hr = surface_load_drawable(surface, gl_info)))
4344 return hr;
4345 break;
4347 case WINED3D_LOCATION_RB_RESOLVED:
4348 surface_multisample_resolve(surface);
4349 break;
4351 case WINED3D_LOCATION_TEXTURE_RGB:
4352 case WINED3D_LOCATION_TEXTURE_SRGB:
4353 if (FAILED(hr = surface_load_texture(surface, gl_info, location == WINED3D_LOCATION_TEXTURE_SRGB)))
4354 return hr;
4355 break;
4357 default:
4358 ERR("Don't know how to handle location %#x.\n", location);
4359 break;
4362 surface_validate_location(surface, location);
4364 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
4365 surface_evict_sysmem(surface);
4367 return WINED3D_OK;
4370 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
4371 /* Context activation is done by the caller. */
4372 static void ffp_blit_free(struct wined3d_device *device) { }
4374 /* Context activation is done by the caller. */
4375 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
4377 const struct wined3d_gl_info *gl_info = context->gl_info;
4379 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
4380 checkGLcall("glEnable(target)");
4382 return WINED3D_OK;
4385 /* Context activation is done by the caller. */
4386 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
4388 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
4389 checkGLcall("glDisable(GL_TEXTURE_2D)");
4390 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
4392 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4393 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4395 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
4397 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
4398 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4402 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
4403 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4404 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4406 switch (blit_op)
4408 case WINED3D_BLIT_OP_COLOR_BLIT:
4409 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
4410 return FALSE;
4412 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4414 TRACE("Checking support for fixup:\n");
4415 dump_color_fixup_desc(src_format->color_fixup);
4418 /* We only support identity conversions. */
4419 if (!is_identity_fixup(src_format->color_fixup)
4420 || !is_identity_fixup(dst_format->color_fixup))
4422 TRACE("Fixups are not supported.\n");
4423 return FALSE;
4426 return TRUE;
4428 case WINED3D_BLIT_OP_COLOR_FILL:
4429 if (dst_pool == WINED3D_POOL_SYSTEM_MEM)
4430 return FALSE;
4432 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
4434 if (!((dst_format->flags & WINED3DFMT_FLAG_FBO_ATTACHABLE) || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
4435 return FALSE;
4437 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4439 TRACE("Color fill not supported\n");
4440 return FALSE;
4443 /* FIXME: We should reject color fills on formats with fixups,
4444 * but this would break P8 color fills for example. */
4446 return TRUE;
4448 case WINED3D_BLIT_OP_DEPTH_FILL:
4449 return TRUE;
4451 default:
4452 TRACE("Unsupported blit_op=%d\n", blit_op);
4453 return FALSE;
4457 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4458 const RECT *dst_rect, const struct wined3d_color *color)
4460 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4461 struct wined3d_rendertarget_view *view;
4462 struct wined3d_fb_state fb = {&view, NULL};
4463 HRESULT hr;
4465 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4466 NULL, &wined3d_null_parent_ops, &view)))
4468 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4469 return hr;
4472 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
4473 wined3d_rendertarget_view_decref(view);
4475 return WINED3D_OK;
4478 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4479 const RECT *dst_rect, float depth)
4481 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4482 struct wined3d_fb_state fb = {NULL, NULL};
4483 HRESULT hr;
4485 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4486 NULL, &wined3d_null_parent_ops, &fb.depth_stencil)))
4488 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4489 return hr;
4492 device_clear_render_targets(device, 0, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
4493 wined3d_rendertarget_view_decref(fb.depth_stencil);
4495 return WINED3D_OK;
4498 const struct blit_shader ffp_blit = {
4499 ffp_blit_alloc,
4500 ffp_blit_free,
4501 ffp_blit_set,
4502 ffp_blit_unset,
4503 ffp_blit_supported,
4504 ffp_blit_color_fill,
4505 ffp_blit_depth_fill,
4508 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
4510 return WINED3D_OK;
4513 /* Context activation is done by the caller. */
4514 static void cpu_blit_free(struct wined3d_device *device)
4518 /* Context activation is done by the caller. */
4519 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface)
4521 return WINED3D_OK;
4524 /* Context activation is done by the caller. */
4525 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
4529 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info, enum wined3d_blit_op blit_op,
4530 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4531 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4533 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
4535 return TRUE;
4538 return FALSE;
4541 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
4542 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
4543 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
4545 UINT row_block_count;
4546 const BYTE *src_row;
4547 BYTE *dst_row;
4548 UINT x, y;
4550 src_row = src_data;
4551 dst_row = dst_data;
4553 row_block_count = (update_w + format->block_width - 1) / format->block_width;
4555 if (!flags)
4557 for (y = 0; y < update_h; y += format->block_height)
4559 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
4560 src_row += src_pitch;
4561 dst_row += dst_pitch;
4564 return WINED3D_OK;
4567 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
4569 src_row += (((update_h / format->block_height) - 1) * src_pitch);
4571 switch (format->id)
4573 case WINED3DFMT_DXT1:
4574 for (y = 0; y < update_h; y += format->block_height)
4576 struct block
4578 WORD color[2];
4579 BYTE control_row[4];
4582 const struct block *s = (const struct block *)src_row;
4583 struct block *d = (struct block *)dst_row;
4585 for (x = 0; x < row_block_count; ++x)
4587 d[x].color[0] = s[x].color[0];
4588 d[x].color[1] = s[x].color[1];
4589 d[x].control_row[0] = s[x].control_row[3];
4590 d[x].control_row[1] = s[x].control_row[2];
4591 d[x].control_row[2] = s[x].control_row[1];
4592 d[x].control_row[3] = s[x].control_row[0];
4594 src_row -= src_pitch;
4595 dst_row += dst_pitch;
4597 return WINED3D_OK;
4599 case WINED3DFMT_DXT2:
4600 case WINED3DFMT_DXT3:
4601 for (y = 0; y < update_h; y += format->block_height)
4603 struct block
4605 WORD alpha_row[4];
4606 WORD color[2];
4607 BYTE control_row[4];
4610 const struct block *s = (const struct block *)src_row;
4611 struct block *d = (struct block *)dst_row;
4613 for (x = 0; x < row_block_count; ++x)
4615 d[x].alpha_row[0] = s[x].alpha_row[3];
4616 d[x].alpha_row[1] = s[x].alpha_row[2];
4617 d[x].alpha_row[2] = s[x].alpha_row[1];
4618 d[x].alpha_row[3] = s[x].alpha_row[0];
4619 d[x].color[0] = s[x].color[0];
4620 d[x].color[1] = s[x].color[1];
4621 d[x].control_row[0] = s[x].control_row[3];
4622 d[x].control_row[1] = s[x].control_row[2];
4623 d[x].control_row[2] = s[x].control_row[1];
4624 d[x].control_row[3] = s[x].control_row[0];
4626 src_row -= src_pitch;
4627 dst_row += dst_pitch;
4629 return WINED3D_OK;
4631 default:
4632 FIXME("Compressed flip not implemented for format %s.\n",
4633 debug_d3dformat(format->id));
4634 return E_NOTIMPL;
4638 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
4639 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
4641 return E_NOTIMPL;
4644 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4645 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
4646 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
4648 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
4649 const struct wined3d_format *src_format, *dst_format;
4650 struct wined3d_texture *src_texture = NULL;
4651 struct wined3d_map_desc dst_map, src_map;
4652 const BYTE *sbase = NULL;
4653 HRESULT hr = WINED3D_OK;
4654 const BYTE *sbuf;
4655 BYTE *dbuf;
4656 int x, y;
4658 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
4659 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4660 flags, fx, debug_d3dtexturefiltertype(filter));
4662 if (src_surface == dst_surface)
4664 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
4665 src_map = dst_map;
4666 src_format = dst_surface->resource.format;
4667 dst_format = src_format;
4669 else
4671 dst_format = dst_surface->resource.format;
4672 if (src_surface)
4674 if (dst_surface->resource.format->id != src_surface->resource.format->id)
4676 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
4678 /* The conv function writes a FIXME */
4679 WARN("Cannot convert source surface format to dest format.\n");
4680 goto release;
4682 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
4684 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
4685 src_format = src_surface->resource.format;
4687 else
4689 src_format = dst_format;
4692 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
4695 bpp = dst_surface->resource.format->byte_count;
4696 srcheight = src_rect->bottom - src_rect->top;
4697 srcwidth = src_rect->right - src_rect->left;
4698 dstheight = dst_rect->bottom - dst_rect->top;
4699 dstwidth = dst_rect->right - dst_rect->left;
4700 width = (dst_rect->right - dst_rect->left) * bpp;
4702 if (src_surface)
4703 sbase = (BYTE *)src_map.data
4704 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
4705 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
4706 if (src_surface != dst_surface)
4707 dbuf = dst_map.data;
4708 else
4709 dbuf = (BYTE *)dst_map.data
4710 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
4711 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
4713 if (src_format->flags & dst_format->flags & WINED3DFMT_FLAG_BLOCKS)
4715 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
4717 if (src_surface == dst_surface)
4719 FIXME("Only plain blits supported on compressed surfaces.\n");
4720 hr = E_NOTIMPL;
4721 goto release;
4724 if (srcheight != dstheight || srcwidth != dstwidth)
4726 WARN("Stretching not supported on compressed surfaces.\n");
4727 hr = WINED3DERR_INVALIDCALL;
4728 goto release;
4731 if (!surface_check_block_align(src_surface, src_rect))
4733 WARN("Source rectangle not block-aligned.\n");
4734 hr = WINED3DERR_INVALIDCALL;
4735 goto release;
4738 if (!surface_check_block_align(dst_surface, dst_rect))
4740 WARN("Destination rectangle not block-aligned.\n");
4741 hr = WINED3DERR_INVALIDCALL;
4742 goto release;
4745 hr = surface_cpu_blt_compressed(sbase, dbuf,
4746 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
4747 src_format, flags, fx);
4748 goto release;
4751 /* First, all the 'source-less' blits */
4752 if (flags & WINEDDBLT_COLORFILL)
4754 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
4755 flags &= ~WINEDDBLT_COLORFILL;
4758 if (flags & WINEDDBLT_DEPTHFILL)
4760 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
4762 if (flags & WINEDDBLT_ROP)
4764 /* Catch some degenerate cases here. */
4765 switch (fx->dwROP)
4767 case BLACKNESS:
4768 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, 0);
4769 break;
4770 case 0xaa0029: /* No-op */
4771 break;
4772 case WHITENESS:
4773 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, ~0U);
4774 break;
4775 case SRCCOPY: /* Well, we do that below? */
4776 break;
4777 default:
4778 FIXME("Unsupported raster op: %08x Pattern: %p\n", fx->dwROP, fx->u5.lpDDSPattern);
4779 goto error;
4781 flags &= ~WINEDDBLT_ROP;
4783 if (flags & WINEDDBLT_DDROPS)
4785 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
4787 /* Now the 'with source' blits. */
4788 if (src_surface)
4790 int sx, xinc, sy, yinc;
4792 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
4793 goto release;
4795 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
4796 && (srcwidth != dstwidth || srcheight != dstheight))
4798 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
4799 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
4802 xinc = (srcwidth << 16) / dstwidth;
4803 yinc = (srcheight << 16) / dstheight;
4805 if (!flags)
4807 /* No effects, we can cheat here. */
4808 if (dstwidth == srcwidth)
4810 if (dstheight == srcheight)
4812 /* No stretching in either direction. This needs to be as
4813 * fast as possible. */
4814 sbuf = sbase;
4816 /* Check for overlapping surfaces. */
4817 if (src_surface != dst_surface || dst_rect->top < src_rect->top
4818 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
4820 /* No overlap, or dst above src, so copy from top downwards. */
4821 for (y = 0; y < dstheight; ++y)
4823 memcpy(dbuf, sbuf, width);
4824 sbuf += src_map.row_pitch;
4825 dbuf += dst_map.row_pitch;
4828 else if (dst_rect->top > src_rect->top)
4830 /* Copy from bottom upwards. */
4831 sbuf += src_map.row_pitch * dstheight;
4832 dbuf += dst_map.row_pitch * dstheight;
4833 for (y = 0; y < dstheight; ++y)
4835 sbuf -= src_map.row_pitch;
4836 dbuf -= dst_map.row_pitch;
4837 memcpy(dbuf, sbuf, width);
4840 else
4842 /* Src and dst overlapping on the same line, use memmove. */
4843 for (y = 0; y < dstheight; ++y)
4845 memmove(dbuf, sbuf, width);
4846 sbuf += src_map.row_pitch;
4847 dbuf += dst_map.row_pitch;
4851 else
4853 /* Stretching in y direction only. */
4854 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4856 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4857 memcpy(dbuf, sbuf, width);
4858 dbuf += dst_map.row_pitch;
4862 else
4864 /* Stretching in X direction. */
4865 int last_sy = -1;
4866 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4868 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4870 if ((sy >> 16) == (last_sy >> 16))
4872 /* This source row is the same as last source row -
4873 * Copy the already stretched row. */
4874 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
4876 else
4878 #define STRETCH_ROW(type) \
4879 do { \
4880 const type *s = (const type *)sbuf; \
4881 type *d = (type *)dbuf; \
4882 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4883 d[x] = s[sx >> 16]; \
4884 } while(0)
4886 switch(bpp)
4888 case 1:
4889 STRETCH_ROW(BYTE);
4890 break;
4891 case 2:
4892 STRETCH_ROW(WORD);
4893 break;
4894 case 4:
4895 STRETCH_ROW(DWORD);
4896 break;
4897 case 3:
4899 const BYTE *s;
4900 BYTE *d = dbuf;
4901 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
4903 DWORD pixel;
4905 s = sbuf + 3 * (sx >> 16);
4906 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4907 d[0] = (pixel ) & 0xff;
4908 d[1] = (pixel >> 8) & 0xff;
4909 d[2] = (pixel >> 16) & 0xff;
4910 d += 3;
4912 break;
4914 default:
4915 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
4916 hr = WINED3DERR_NOTAVAILABLE;
4917 goto error;
4919 #undef STRETCH_ROW
4921 dbuf += dst_map.row_pitch;
4922 last_sy = sy;
4926 else
4928 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
4929 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
4930 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
4931 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
4933 /* The color keying flags are checked for correctness in ddraw */
4934 if (flags & WINEDDBLT_KEYSRC)
4936 keylow = src_surface->container->src_blt_color_key.color_space_low_value;
4937 keyhigh = src_surface->container->src_blt_color_key.color_space_high_value;
4939 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4941 keylow = fx->ddckSrcColorkey.color_space_low_value;
4942 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
4945 if (flags & WINEDDBLT_KEYDEST)
4947 /* Destination color keys are taken from the source surface! */
4948 destkeylow = src_surface->container->dst_blt_color_key.color_space_low_value;
4949 destkeyhigh = src_surface->container->dst_blt_color_key.color_space_high_value;
4951 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
4953 destkeylow = fx->ddckDestColorkey.color_space_low_value;
4954 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
4957 if (bpp == 1)
4959 keymask = 0xff;
4961 else
4963 DWORD masks[3];
4964 get_color_masks(src_format, masks);
4965 keymask = masks[0]
4966 | masks[1]
4967 | masks[2];
4969 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
4972 if (flags & WINEDDBLT_DDFX)
4974 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
4975 LONG tmpxy;
4976 dTopLeft = dbuf;
4977 dTopRight = dbuf + ((dstwidth - 1) * bpp);
4978 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
4979 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
4981 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
4983 /* I don't think we need to do anything about this flag */
4984 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
4986 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
4988 tmp = dTopRight;
4989 dTopRight = dTopLeft;
4990 dTopLeft = tmp;
4991 tmp = dBottomRight;
4992 dBottomRight = dBottomLeft;
4993 dBottomLeft = tmp;
4994 dstxinc = dstxinc * -1;
4996 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
4998 tmp = dTopLeft;
4999 dTopLeft = dBottomLeft;
5000 dBottomLeft = tmp;
5001 tmp = dTopRight;
5002 dTopRight = dBottomRight;
5003 dBottomRight = tmp;
5004 dstyinc = dstyinc * -1;
5006 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
5008 /* I don't think we need to do anything about this flag */
5009 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
5011 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
5013 tmp = dBottomRight;
5014 dBottomRight = dTopLeft;
5015 dTopLeft = tmp;
5016 tmp = dBottomLeft;
5017 dBottomLeft = dTopRight;
5018 dTopRight = tmp;
5019 dstxinc = dstxinc * -1;
5020 dstyinc = dstyinc * -1;
5022 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
5024 tmp = dTopLeft;
5025 dTopLeft = dBottomLeft;
5026 dBottomLeft = dBottomRight;
5027 dBottomRight = dTopRight;
5028 dTopRight = tmp;
5029 tmpxy = dstxinc;
5030 dstxinc = dstyinc;
5031 dstyinc = tmpxy;
5032 dstxinc = dstxinc * -1;
5034 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
5036 tmp = dTopLeft;
5037 dTopLeft = dTopRight;
5038 dTopRight = dBottomRight;
5039 dBottomRight = dBottomLeft;
5040 dBottomLeft = tmp;
5041 tmpxy = dstxinc;
5042 dstxinc = dstyinc;
5043 dstyinc = tmpxy;
5044 dstyinc = dstyinc * -1;
5046 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
5048 /* I don't think we need to do anything about this flag */
5049 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
5051 dbuf = dTopLeft;
5052 flags &= ~(WINEDDBLT_DDFX);
5055 #define COPY_COLORKEY_FX(type) \
5056 do { \
5057 const type *s; \
5058 type *d = (type *)dbuf, *dx, tmp; \
5059 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
5061 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
5062 dx = d; \
5063 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
5065 tmp = s[sx >> 16]; \
5066 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
5067 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
5069 dx[0] = tmp; \
5071 dx = (type *)(((BYTE *)dx) + dstxinc); \
5073 d = (type *)(((BYTE *)d) + dstyinc); \
5075 } while(0)
5077 switch (bpp)
5079 case 1:
5080 COPY_COLORKEY_FX(BYTE);
5081 break;
5082 case 2:
5083 COPY_COLORKEY_FX(WORD);
5084 break;
5085 case 4:
5086 COPY_COLORKEY_FX(DWORD);
5087 break;
5088 case 3:
5090 const BYTE *s;
5091 BYTE *d = dbuf, *dx;
5092 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
5094 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
5095 dx = d;
5096 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
5098 DWORD pixel, dpixel = 0;
5099 s = sbuf + 3 * (sx>>16);
5100 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
5101 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
5102 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
5103 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
5105 dx[0] = (pixel ) & 0xff;
5106 dx[1] = (pixel >> 8) & 0xff;
5107 dx[2] = (pixel >> 16) & 0xff;
5109 dx += dstxinc;
5111 d += dstyinc;
5113 break;
5115 default:
5116 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
5117 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
5118 hr = WINED3DERR_NOTAVAILABLE;
5119 goto error;
5120 #undef COPY_COLORKEY_FX
5125 error:
5126 if (flags && FIXME_ON(d3d_surface))
5128 FIXME("\tUnsupported flags: %#x.\n", flags);
5131 release:
5132 wined3d_surface_unmap(dst_surface);
5133 if (src_surface && src_surface != dst_surface)
5134 wined3d_surface_unmap(src_surface);
5135 /* Release the converted surface, if any. */
5136 if (src_texture)
5137 wined3d_texture_decref(src_texture);
5139 return hr;
5142 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5143 const RECT *dst_rect, const struct wined3d_color *color)
5145 static const RECT src_rect;
5146 WINEDDBLTFX BltFx;
5148 memset(&BltFx, 0, sizeof(BltFx));
5149 BltFx.dwSize = sizeof(BltFx);
5150 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
5151 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
5152 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
5155 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
5156 struct wined3d_surface *surface, const RECT *rect, float depth)
5158 FIXME("Depth filling not implemented by cpu_blit.\n");
5159 return WINED3DERR_INVALIDCALL;
5162 const struct blit_shader cpu_blit = {
5163 cpu_blit_alloc,
5164 cpu_blit_free,
5165 cpu_blit_set,
5166 cpu_blit_unset,
5167 cpu_blit_supported,
5168 cpu_blit_color_fill,
5169 cpu_blit_depth_fill,
5172 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
5173 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
5174 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5176 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
5177 struct wined3d_device *device = dst_surface->resource.device;
5178 DWORD src_ds_flags, dst_ds_flags;
5179 RECT src_rect, dst_rect;
5180 BOOL scale, convert;
5182 static const DWORD simple_blit = WINEDDBLT_ASYNC
5183 | WINEDDBLT_COLORFILL
5184 | WINEDDBLT_WAIT
5185 | WINEDDBLT_DEPTHFILL
5186 | WINEDDBLT_DONOTWAIT;
5188 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5189 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
5190 flags, fx, debug_d3dtexturefiltertype(filter));
5191 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
5193 if (fx)
5195 TRACE("dwSize %#x.\n", fx->dwSize);
5196 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
5197 TRACE("dwROP %#x.\n", fx->dwROP);
5198 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
5199 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
5200 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
5201 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
5202 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
5203 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
5204 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
5205 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
5206 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
5207 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
5208 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
5209 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
5210 TRACE("dwReserved %#x.\n", fx->dwReserved);
5211 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
5212 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
5213 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
5214 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
5215 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
5216 TRACE("ddckDestColorkey {%#x, %#x}.\n",
5217 fx->ddckDestColorkey.color_space_low_value,
5218 fx->ddckDestColorkey.color_space_high_value);
5219 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
5220 fx->ddckSrcColorkey.color_space_low_value,
5221 fx->ddckSrcColorkey.color_space_high_value);
5224 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
5226 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
5227 return WINEDDERR_SURFACEBUSY;
5230 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
5232 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
5233 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
5234 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
5235 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
5236 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
5238 WARN("The application gave us a bad destination rectangle.\n");
5239 return WINEDDERR_INVALIDRECT;
5242 if (src_surface)
5244 surface_get_rect(src_surface, src_rect_in, &src_rect);
5246 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
5247 || src_rect.left > src_surface->resource.width || src_rect.left < 0
5248 || src_rect.top > src_surface->resource.height || src_rect.top < 0
5249 || src_rect.right > src_surface->resource.width || src_rect.right < 0
5250 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
5252 WARN("Application gave us bad source rectangle for Blt.\n");
5253 return WINEDDERR_INVALIDRECT;
5256 else
5258 memset(&src_rect, 0, sizeof(src_rect));
5261 if (!fx || !(fx->dwDDFX))
5262 flags &= ~WINEDDBLT_DDFX;
5264 if (flags & WINEDDBLT_WAIT)
5265 flags &= ~WINEDDBLT_WAIT;
5267 if (flags & WINEDDBLT_ASYNC)
5269 static unsigned int once;
5271 if (!once++)
5272 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
5273 flags &= ~WINEDDBLT_ASYNC;
5276 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
5277 if (flags & WINEDDBLT_DONOTWAIT)
5279 static unsigned int once;
5281 if (!once++)
5282 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
5283 flags &= ~WINEDDBLT_DONOTWAIT;
5286 if (!device->d3d_initialized)
5288 WARN("D3D not initialized, using fallback.\n");
5289 goto cpu;
5292 /* We want to avoid invalidating the sysmem location for converted
5293 * surfaces, since otherwise we'd have to convert the data back when
5294 * locking them. */
5295 if (dst_surface->container->flags & WINED3D_TEXTURE_CONVERTED
5296 || dst_surface->container->resource.format->convert
5297 || wined3d_format_get_color_key_conversion(dst_surface->container, TRUE))
5299 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
5300 goto cpu;
5303 if (flags & ~simple_blit)
5305 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
5306 goto fallback;
5309 if (src_surface)
5310 src_swapchain = src_surface->container->swapchain;
5311 else
5312 src_swapchain = NULL;
5314 dst_swapchain = dst_surface->container->swapchain;
5316 /* This isn't strictly needed. FBO blits for example could deal with
5317 * cross-swapchain blits by first downloading the source to a texture
5318 * before switching to the destination context. We just have this here to
5319 * not have to deal with the issue, since cross-swapchain blits should be
5320 * rare. */
5321 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
5323 FIXME("Using fallback for cross-swapchain blit.\n");
5324 goto fallback;
5327 scale = src_surface
5328 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
5329 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
5330 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
5332 dst_ds_flags = dst_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5333 if (src_surface)
5334 src_ds_flags = src_surface->resource.format->flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5335 else
5336 src_ds_flags = 0;
5338 if (src_ds_flags || dst_ds_flags)
5340 if (flags & WINEDDBLT_DEPTHFILL)
5342 float depth;
5344 TRACE("Depth fill.\n");
5346 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
5347 return WINED3DERR_INVALIDCALL;
5349 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
5350 return WINED3D_OK;
5352 else
5354 if (src_ds_flags != dst_ds_flags)
5356 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
5357 return WINED3DERR_INVALIDCALL;
5360 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->container->resource.draw_binding,
5361 &src_rect, dst_surface, dst_surface->container->resource.draw_binding, &dst_rect)))
5362 return WINED3D_OK;
5365 else
5367 /* In principle this would apply to depth blits as well, but we don't
5368 * implement those in the CPU blitter at the moment. */
5369 if ((dst_surface->locations & dst_surface->resource.map_binding)
5370 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
5372 if (scale)
5373 TRACE("Not doing sysmem blit because of scaling.\n");
5374 else if (convert)
5375 TRACE("Not doing sysmem blit because of format conversion.\n");
5376 else
5377 goto cpu;
5380 if (flags & WINEDDBLT_COLORFILL)
5382 struct wined3d_color color;
5384 TRACE("Color fill.\n");
5386 if (!surface_convert_color_to_float(dst_surface, fx->u5.dwFillColor, &color))
5387 goto fallback;
5389 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
5390 return WINED3D_OK;
5392 else
5394 TRACE("Color blit.\n");
5396 /* Upload */
5397 if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
5398 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
5400 if (scale)
5401 TRACE("Not doing upload because of scaling.\n");
5402 else if (convert)
5403 TRACE("Not doing upload because of format conversion.\n");
5404 else
5406 POINT dst_point = {dst_rect.left, dst_rect.top};
5408 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
5410 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
5411 surface_load_location(dst_surface, dst_surface->container->resource.draw_binding);
5412 return WINED3D_OK;
5417 /* Use present for back -> front blits. The idea behind this is
5418 * that present is potentially faster than a blit, in particular
5419 * when FBO blits aren't available. Some ddraw applications like
5420 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
5421 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
5422 * applications can't blit directly to the frontbuffer. */
5423 if (dst_swapchain && dst_swapchain->back_buffers
5424 && dst_surface->container == dst_swapchain->front_buffer
5425 && src_surface->container == dst_swapchain->back_buffers[0])
5427 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
5429 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
5431 /* Set the swap effect to COPY, we don't want the backbuffer
5432 * to become undefined. */
5433 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
5434 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
5435 dst_swapchain->desc.swap_effect = swap_effect;
5437 return WINED3D_OK;
5440 if (fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5441 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5442 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5444 TRACE("Using FBO blit.\n");
5446 surface_blt_fbo(device, filter,
5447 src_surface, src_surface->container->resource.draw_binding, &src_rect,
5448 dst_surface, dst_surface->container->resource.draw_binding, &dst_rect);
5449 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
5450 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
5452 return WINED3D_OK;
5455 if (arbfp_blit.blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
5456 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5457 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5459 TRACE("Using arbfp blit.\n");
5461 if (SUCCEEDED(arbfp_blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect)))
5462 return WINED3D_OK;
5467 fallback:
5468 /* Special cases for render targets. */
5469 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
5470 return WINED3D_OK;
5472 cpu:
5474 /* For the rest call the X11 surface implementation. For render targets
5475 * this should be implemented OpenGL accelerated in surface_blt_special(),
5476 * other blits are rather rare. */
5477 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
5480 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
5481 const struct wined3d_resource_desc *desc, GLenum target, unsigned int level, unsigned int layer, DWORD flags)
5483 struct wined3d_device *device = container->resource.device;
5484 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5485 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
5486 UINT multisample_quality = desc->multisample_quality;
5487 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
5488 unsigned int resource_size;
5489 HRESULT hr;
5491 if (multisample_quality > 0)
5493 FIXME("multisample_quality set to %u, substituting 0.\n", multisample_quality);
5494 multisample_quality = 0;
5497 /* Quick lockable sanity check.
5498 * TODO: remove this after surfaces, usage and lockability have been debugged properly
5499 * this function is too deep to need to care about things like this.
5500 * Levels need to be checked too, since they all affect what can be done. */
5501 switch (desc->pool)
5503 case WINED3D_POOL_MANAGED:
5504 if (desc->usage & WINED3DUSAGE_DYNAMIC)
5505 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
5506 break;
5508 case WINED3D_POOL_DEFAULT:
5509 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
5510 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
5511 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
5512 break;
5514 case WINED3D_POOL_SCRATCH:
5515 case WINED3D_POOL_SYSTEM_MEM:
5516 break;
5518 default:
5519 FIXME("Unknown pool %#x.\n", desc->pool);
5520 break;
5523 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
5524 FIXME("Trying to create a render target that isn't in the default pool.\n");
5526 /* FIXME: Check that the format is supported by the device. */
5528 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
5529 if (!resource_size)
5530 return WINED3DERR_INVALIDCALL;
5532 if (device->wined3d->flags & WINED3D_NO3D)
5533 surface->surface_ops = &gdi_surface_ops;
5534 else
5535 surface->surface_ops = &surface_ops;
5537 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE, format,
5538 desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height, 1,
5539 resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
5541 WARN("Failed to initialize resource, returning %#x.\n", hr);
5542 return hr;
5545 surface->container = container;
5546 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
5547 list_init(&surface->renderbuffers);
5548 list_init(&surface->overlays);
5550 /* Flags */
5551 if (flags & WINED3D_SURFACE_DISCARD)
5552 surface->flags |= SFLAG_DISCARD;
5553 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
5554 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
5556 surface->texture_target = target;
5557 surface->texture_level = level;
5558 surface->texture_layer = layer;
5560 /* Call the private setup routine */
5561 if (FAILED(hr = surface->surface_ops->surface_private_setup(surface)))
5563 ERR("Private setup failed, hr %#x.\n", hr);
5564 surface_cleanup(surface);
5565 return hr;
5568 /* Similar to lockable rendertargets above, creating the DIB section
5569 * during surface initialization prevents the sysmem pointer from changing
5570 * after a wined3d_surface_getdc() call. */
5571 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
5572 && SUCCEEDED(surface_create_dib_section(surface)))
5573 surface->resource.map_binding = WINED3D_LOCATION_DIB;
5575 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
5577 wined3d_resource_free_sysmem(&surface->resource);
5578 surface_validate_location(surface, WINED3D_LOCATION_DIB);
5579 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
5582 return hr;
5585 HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
5586 GLenum target, unsigned int level, unsigned int layer, DWORD flags, struct wined3d_surface **surface)
5588 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
5589 const struct wined3d_parent_ops *parent_ops;
5590 struct wined3d_surface *object;
5591 void *parent;
5592 HRESULT hr;
5594 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), pool %s, "
5595 "multisample_type %#x, multisample_quality %u, target %#x, level %u, layer %u, flags %#x, surface %p.\n",
5596 container, desc->width, desc->height, debug_d3dformat(desc->format),
5597 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
5598 desc->multisample_type, desc->multisample_quality, target, level, layer, flags, surface);
5600 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
5601 return E_OUTOFMEMORY;
5603 if (FAILED(hr = surface_init(object, container, desc, target, level, layer, flags)))
5605 WARN("Failed to initialize surface, returning %#x.\n", hr);
5606 HeapFree(GetProcessHeap(), 0, object);
5607 return hr;
5610 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
5611 wined3d_texture_get_parent(container), object, &parent, &parent_ops)))
5613 WARN("Failed to create surface parent, hr %#x.\n", hr);
5614 wined3d_surface_destroy(object);
5615 return hr;
5618 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
5620 object->resource.parent = parent;
5621 object->resource.parent_ops = parent_ops;
5622 *surface = object;
5624 return hr;