d3d11: Rename d3d10_texture2d to d3d_texture2d.
[wine.git] / dlls / wined3d / surface.c
blobf303d903cd5a27562f602693b0d3c3ce7c65eb3b
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 wined3d_gl_resource_type 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 = WINED3D_GL_RES_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 = WINED3D_GL_RES_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 = WINED3D_GL_RES_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 = WINED3D_GL_RES_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 = WINED3D_GL_RES_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 = WINED3D_GL_RES_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 = WINED3D_GL_RES_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 = WINED3D_GL_RES_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, wined3d_gl_mag_filter(filter));
314 checkGLcall("glTexParameteri");
315 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_MIN_FILTER,
316 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
317 checkGLcall("glTexParameteri");
318 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
319 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
320 if (context->gl_info->supported[EXT_TEXTURE_SRGB_DECODE])
321 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
322 gl_info->gl_ops.gl.p_glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
323 checkGLcall("glTexEnvi");
325 /* Draw a quad */
326 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
327 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
328 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->top);
330 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
331 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->top);
333 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
334 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->left, dst_rect->bottom);
336 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
337 gl_info->gl_ops.gl.p_glVertex2i(dst_rect->right, dst_rect->bottom);
338 gl_info->gl_ops.gl.p_glEnd();
340 /* Unbind the texture */
341 context_bind_texture(context, info.bind_target, 0);
343 /* We changed the filtering settings on the texture. Inform the
344 * container about this to get the filters reset properly next draw. */
345 texture->texture_rgb.sampler_desc.mag_filter = WINED3D_TEXF_POINT;
346 texture->texture_rgb.sampler_desc.min_filter = WINED3D_TEXF_POINT;
347 texture->texture_rgb.sampler_desc.mip_filter = WINED3D_TEXF_NONE;
348 texture->texture_rgb.sampler_desc.srgb_decode = FALSE;
351 /* Works correctly only for <= 4 bpp formats. */
352 static void get_color_masks(const struct wined3d_format *format, DWORD *masks)
354 masks[0] = ((1u << format->red_size) - 1) << format->red_offset;
355 masks[1] = ((1u << format->green_size) - 1) << format->green_offset;
356 masks[2] = ((1u << format->blue_size) - 1) << format->blue_offset;
359 static HRESULT surface_create_dib_section(struct wined3d_surface *surface)
361 const struct wined3d_format *format = surface->resource.format;
362 unsigned int format_flags = surface->container->resource.format_flags;
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) * (1u << (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->container->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->container->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->container->resource.format_flags & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
784 dst_mask = dst_surface->container->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[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
993 || (src_usage & WINED3DUSAGE_RENDERTARGET)))
994 return FALSE;
995 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
996 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
997 return FALSE;
998 break;
1000 case WINED3D_BLIT_OP_DEPTH_BLIT:
1001 if (!(src_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1002 return FALSE;
1003 if (!(dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL)))
1004 return FALSE;
1005 break;
1007 default:
1008 return FALSE;
1011 if (!(src_format->id == dst_format->id
1012 || (is_identity_fixup(src_format->color_fixup)
1013 && is_identity_fixup(dst_format->color_fixup))))
1014 return FALSE;
1016 return TRUE;
1019 static BOOL surface_convert_depth_to_float(const struct wined3d_surface *surface, DWORD depth, float *float_depth)
1021 const struct wined3d_format *format = surface->resource.format;
1023 switch (format->id)
1025 case WINED3DFMT_S1_UINT_D15_UNORM:
1026 *float_depth = depth / (float)0x00007fff;
1027 break;
1029 case WINED3DFMT_D16_UNORM:
1030 *float_depth = depth / (float)0x0000ffff;
1031 break;
1033 case WINED3DFMT_D24_UNORM_S8_UINT:
1034 case WINED3DFMT_X8D24_UNORM:
1035 *float_depth = depth / (float)0x00ffffff;
1036 break;
1038 case WINED3DFMT_D32_UNORM:
1039 *float_depth = depth / (float)0xffffffff;
1040 break;
1042 default:
1043 ERR("Unhandled conversion from %s to floating point.\n", debug_d3dformat(format->id));
1044 return FALSE;
1047 return TRUE;
1050 static HRESULT wined3d_surface_depth_fill(struct wined3d_surface *surface, const RECT *rect, float depth)
1052 const struct wined3d_resource *resource = &surface->container->resource;
1053 struct wined3d_device *device = resource->device;
1054 const struct blit_shader *blitter;
1056 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, WINED3D_BLIT_OP_DEPTH_FILL,
1057 NULL, 0, 0, NULL, rect, resource->usage, resource->pool, resource->format);
1058 if (!blitter)
1060 FIXME("No blitter is capable of performing the requested depth fill operation.\n");
1061 return WINED3DERR_INVALIDCALL;
1064 return blitter->depth_fill(device, surface, rect, depth);
1067 static HRESULT wined3d_surface_depth_blt(struct wined3d_surface *src_surface, DWORD src_location, const RECT *src_rect,
1068 struct wined3d_surface *dst_surface, DWORD dst_location, const RECT *dst_rect)
1070 struct wined3d_device *device = src_surface->resource.device;
1072 if (!fbo_blit_supported(&device->adapter->gl_info, WINED3D_BLIT_OP_DEPTH_BLIT,
1073 src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
1074 dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
1075 return WINED3DERR_INVALIDCALL;
1077 surface_depth_blt_fbo(device, src_surface, src_location, src_rect, dst_surface, dst_location, dst_rect);
1079 surface_modify_ds_location(dst_surface, dst_location,
1080 dst_surface->ds_current_size.cx, dst_surface->ds_current_size.cy);
1082 return WINED3D_OK;
1085 HRESULT CDECL wined3d_surface_get_render_target_data(struct wined3d_surface *surface,
1086 struct wined3d_surface *render_target)
1088 TRACE("surface %p, render_target %p.\n", surface, render_target);
1090 /* TODO: Check surface sizes, pools, etc. */
1092 if (render_target->resource.multisample_type)
1093 return WINED3DERR_INVALIDCALL;
1095 return wined3d_surface_blt(surface, NULL, render_target, NULL, 0, NULL, WINED3D_TEXF_POINT);
1098 /* Context activation is done by the caller. */
1099 static void surface_remove_pbo(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info)
1101 GL_EXTCALL(glDeleteBuffers(1, &surface->pbo));
1102 checkGLcall("glDeleteBuffers(1, &surface->pbo)");
1104 surface->pbo = 0;
1105 surface_invalidate_location(surface, WINED3D_LOCATION_BUFFER);
1108 static ULONG surface_resource_incref(struct wined3d_resource *resource)
1110 return wined3d_surface_incref(surface_from_resource(resource));
1113 static ULONG surface_resource_decref(struct wined3d_resource *resource)
1115 return wined3d_surface_decref(surface_from_resource(resource));
1118 static void surface_unload(struct wined3d_resource *resource)
1120 struct wined3d_surface *surface = surface_from_resource(resource);
1121 struct wined3d_renderbuffer_entry *entry, *entry2;
1122 struct wined3d_device *device = resource->device;
1123 const struct wined3d_gl_info *gl_info;
1124 struct wined3d_context *context;
1126 TRACE("surface %p.\n", surface);
1128 if (resource->pool == WINED3D_POOL_DEFAULT)
1130 /* Default pool resources are supposed to be destroyed before Reset is called.
1131 * Implicit resources stay however. So this means we have an implicit render target
1132 * or depth stencil. The content may be destroyed, but we still have to tear down
1133 * opengl resources, so we cannot leave early.
1135 * Put the surfaces into sysmem, and reset the content. The D3D content is undefined,
1136 * but we can't set the sysmem INDRAWABLE because when we're rendering the swapchain
1137 * or the depth stencil into an FBO the texture or render buffer will be removed
1138 * and all flags get lost */
1139 surface_prepare_system_memory(surface);
1140 memset(surface->resource.heap_memory, 0, surface->resource.size);
1141 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
1142 surface_invalidate_location(surface, ~WINED3D_LOCATION_SYSMEM);
1144 /* We also get here when the ddraw swapchain is destroyed, for example
1145 * for a mode switch. In this case this surface won't necessarily be
1146 * an implicit surface. We have to mark it lost so that the
1147 * application can restore it after the mode switch. */
1148 surface->flags |= SFLAG_LOST;
1150 else
1152 surface_prepare_map_memory(surface);
1153 surface_load_location(surface, surface->resource.map_binding);
1154 surface_invalidate_location(surface, ~surface->resource.map_binding);
1157 context = context_acquire(device, NULL);
1158 gl_info = context->gl_info;
1160 /* Destroy PBOs, but load them into real sysmem before */
1161 if (surface->pbo)
1162 surface_remove_pbo(surface, gl_info);
1164 /* Destroy fbo render buffers. This is needed for implicit render targets, for
1165 * all application-created targets the application has to release the surface
1166 * before calling _Reset
1168 LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1170 gl_info->fbo_ops.glDeleteRenderbuffers(1, &entry->id);
1171 list_remove(&entry->entry);
1172 HeapFree(GetProcessHeap(), 0, entry);
1174 list_init(&surface->renderbuffers);
1175 surface->current_renderbuffer = NULL;
1177 if (surface->rb_multisample)
1179 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_multisample);
1180 surface->rb_multisample = 0;
1182 if (surface->rb_resolved)
1184 gl_info->fbo_ops.glDeleteRenderbuffers(1, &surface->rb_resolved);
1185 surface->rb_resolved = 0;
1188 context_release(context);
1190 resource_unload(resource);
1193 static const struct wined3d_resource_ops surface_resource_ops =
1195 surface_resource_incref,
1196 surface_resource_decref,
1197 surface_unload,
1200 static const struct wined3d_surface_ops surface_ops =
1202 surface_private_setup,
1203 surface_unmap,
1206 /*****************************************************************************
1207 * Initializes the GDI surface, aka creates the DIB section we render to
1208 * The DIB section creation is done by calling GetDC, which will create the
1209 * section and releasing the dc to allow the app to use it. The dib section
1210 * will stay until the surface is released
1212 * GDI surfaces do not need to be a power of 2 in size, so the pow2 sizes
1213 * are set to the real sizes to save memory. The NONPOW2 flag is unset to
1214 * avoid confusion in the shared surface code.
1216 * Returns:
1217 * WINED3D_OK on success
1218 * The return values of called methods on failure
1220 *****************************************************************************/
1221 static HRESULT gdi_surface_private_setup(struct wined3d_surface *surface)
1223 HRESULT hr;
1225 TRACE("surface %p.\n", surface);
1227 if (surface->resource.usage & WINED3DUSAGE_OVERLAY)
1229 ERR("Overlays not yet supported by GDI surfaces.\n");
1230 return WINED3DERR_INVALIDCALL;
1233 /* Sysmem textures have memory already allocated - release it,
1234 * this avoids an unnecessary memcpy. */
1235 hr = surface_create_dib_section(surface);
1236 if (FAILED(hr))
1237 return hr;
1238 surface->resource.map_binding = WINED3D_LOCATION_DIB;
1240 /* We don't mind the nonpow2 stuff in GDI. */
1241 surface->pow2Width = surface->resource.width;
1242 surface->pow2Height = surface->resource.height;
1244 return WINED3D_OK;
1247 static void gdi_surface_unmap(struct wined3d_surface *surface)
1249 TRACE("surface %p.\n", surface);
1251 /* Tell the swapchain to update the screen. */
1252 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
1253 x11_copy_to_screen(surface->container->swapchain, &surface->lockedRect);
1255 memset(&surface->lockedRect, 0, sizeof(RECT));
1258 static const struct wined3d_surface_ops gdi_surface_ops =
1260 gdi_surface_private_setup,
1261 gdi_surface_unmap,
1264 /* This call just downloads data, the caller is responsible for binding the
1265 * correct texture. */
1266 /* Context activation is done by the caller. */
1267 static void surface_download_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1268 DWORD dst_location)
1270 const struct wined3d_format *format = surface->resource.format;
1271 struct wined3d_bo_address data;
1273 /* Only support read back of converted P8 surfaces. */
1274 if (surface->container->flags & WINED3D_TEXTURE_CONVERTED && format->id != WINED3DFMT_P8_UINT)
1276 ERR("Trying to read back converted surface %p with format %s.\n", surface, debug_d3dformat(format->id));
1277 return;
1280 surface_get_memory(surface, &data, dst_location);
1282 if (surface->container->resource.format_flags & WINED3DFMT_FLAG_COMPRESSED)
1284 TRACE("(%p) : Calling glGetCompressedTexImage level %d, format %#x, type %#x, data %p.\n",
1285 surface, surface->texture_level, format->glFormat, format->glType, data.addr);
1287 if (data.buffer_object)
1289 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1290 checkGLcall("glBindBuffer");
1291 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target, surface->texture_level, NULL));
1292 checkGLcall("glGetCompressedTexImage");
1293 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1294 checkGLcall("glBindBuffer");
1296 else
1298 GL_EXTCALL(glGetCompressedTexImage(surface->texture_target,
1299 surface->texture_level, data.addr));
1300 checkGLcall("glGetCompressedTexImage");
1303 else
1305 void *mem;
1306 GLenum gl_format = format->glFormat;
1307 GLenum gl_type = format->glType;
1308 int src_pitch = 0;
1309 int dst_pitch = 0;
1311 if (surface->flags & SFLAG_NONPOW2)
1313 unsigned char alignment = surface->resource.device->surface_alignment;
1314 src_pitch = format->byte_count * surface->pow2Width;
1315 dst_pitch = wined3d_surface_get_pitch(surface);
1316 src_pitch = (src_pitch + alignment - 1) & ~(alignment - 1);
1317 mem = HeapAlloc(GetProcessHeap(), 0, src_pitch * surface->pow2Height);
1319 else
1321 mem = data.addr;
1324 TRACE("(%p) : Calling glGetTexImage level %d, format %#x, type %#x, data %p\n",
1325 surface, surface->texture_level, gl_format, gl_type, mem);
1327 if (data.buffer_object)
1329 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
1330 checkGLcall("glBindBuffer");
1332 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1333 gl_format, gl_type, NULL);
1334 checkGLcall("glGetTexImage");
1336 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
1337 checkGLcall("glBindBuffer");
1339 else
1341 gl_info->gl_ops.gl.p_glGetTexImage(surface->texture_target, surface->texture_level,
1342 gl_format, gl_type, mem);
1343 checkGLcall("glGetTexImage");
1346 if (surface->flags & SFLAG_NONPOW2)
1348 const BYTE *src_data;
1349 BYTE *dst_data;
1350 UINT y;
1352 * Some games (e.g. warhammer 40k) don't work properly with the odd pitches, preventing
1353 * the surface pitch from being used to box non-power2 textures. Instead we have to use a hack to
1354 * repack the texture so that the bpp * width pitch can be used instead of bpp * pow2width.
1356 * We're doing this...
1358 * instead of boxing the texture :
1359 * |<-texture width ->| -->pow2width| /\
1360 * |111111111111111111| | |
1361 * |222 Texture 222222| boxed empty | texture height
1362 * |3333 Data 33333333| | |
1363 * |444444444444444444| | \/
1364 * ----------------------------------- |
1365 * | boxed empty | boxed empty | pow2height
1366 * | | | \/
1367 * -----------------------------------
1370 * we're repacking the data to the expected texture width
1372 * |<-texture width ->| -->pow2width| /\
1373 * |111111111111111111222222222222222| |
1374 * |222333333333333333333444444444444| texture height
1375 * |444444 | |
1376 * | | \/
1377 * | | |
1378 * | empty | pow2height
1379 * | | \/
1380 * -----------------------------------
1382 * == is the same as
1384 * |<-texture width ->| /\
1385 * |111111111111111111|
1386 * |222222222222222222|texture height
1387 * |333333333333333333|
1388 * |444444444444444444| \/
1389 * --------------------
1391 * This also means that any references to surface memory should work with the data as if it were a
1392 * standard texture with a non-power2 width instead of a texture boxed up to be a power2 texture.
1394 * internally the texture is still stored in a boxed format so any references to textureName will
1395 * get a boxed texture with width pow2width and not a texture of width resource.width.
1397 * Performance should not be an issue, because applications normally do not lock the surfaces when
1398 * rendering. If an app does, the WINED3D_TEXTURE_DYNAMIC_MAP flag will kick in and the memory copy
1399 * won't be released, and doesn't have to be re-read. */
1400 src_data = mem;
1401 dst_data = data.addr;
1402 TRACE("(%p) : Repacking the surface data from pitch %d to pitch %d\n", surface, src_pitch, dst_pitch);
1403 for (y = 0; y < surface->resource.height; ++y)
1405 memcpy(dst_data, src_data, dst_pitch);
1406 src_data += src_pitch;
1407 dst_data += dst_pitch;
1410 HeapFree(GetProcessHeap(), 0, mem);
1415 /* This call just uploads data, the caller is responsible for binding the
1416 * correct texture. */
1417 /* Context activation is done by the caller. */
1418 void wined3d_surface_upload_data(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info,
1419 const struct wined3d_format *format, const RECT *src_rect, UINT src_pitch, const POINT *dst_point,
1420 BOOL srgb, const struct wined3d_const_bo_address *data)
1422 UINT update_w = src_rect->right - src_rect->left;
1423 UINT update_h = src_rect->bottom - src_rect->top;
1425 TRACE("surface %p, gl_info %p, format %s, src_rect %s, src_pitch %u, dst_point %s, srgb %#x, data {%#x:%p}.\n",
1426 surface, gl_info, debug_d3dformat(format->id), wine_dbgstr_rect(src_rect), src_pitch,
1427 wine_dbgstr_point(dst_point), srgb, data->buffer_object, data->addr);
1429 if (surface->resource.map_count)
1431 WARN("Uploading a surface that is currently mapped, setting WINED3D_TEXTURE_PIN_SYSMEM.\n");
1432 surface->container->flags |= WINED3D_TEXTURE_PIN_SYSMEM;
1435 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_HEIGHT_SCALE)
1437 update_h *= format->height_scale.numerator;
1438 update_h /= format->height_scale.denominator;
1441 if (data->buffer_object)
1443 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, data->buffer_object));
1444 checkGLcall("glBindBuffer");
1447 if (format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_COMPRESSED)
1449 UINT row_length = wined3d_format_calculate_size(format, 1, update_w, 1, 1);
1450 UINT row_count = (update_h + format->block_height - 1) / format->block_height;
1451 const BYTE *addr = data->addr;
1452 GLenum internal;
1454 addr += (src_rect->top / format->block_height) * src_pitch;
1455 addr += (src_rect->left / format->block_width) * format->block_byte_count;
1457 if (srgb)
1458 internal = format->glGammaInternal;
1459 else if (surface->resource.usage & WINED3DUSAGE_RENDERTARGET
1460 && wined3d_resource_is_offscreen(&surface->container->resource))
1461 internal = format->rtInternal;
1462 else
1463 internal = format->glInternal;
1465 TRACE("glCompressedTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, "
1466 "format %#x, image_size %#x, addr %p.\n", surface->texture_target, surface->texture_level,
1467 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr);
1469 if (row_length == src_pitch)
1471 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1472 dst_point->x, dst_point->y, update_w, update_h, internal, row_count * row_length, addr));
1474 else
1476 UINT row, y;
1478 /* glCompressedTexSubImage2D() ignores pixel store state, so we
1479 * can't use the unpack row length like for glTexSubImage2D. */
1480 for (row = 0, y = dst_point->y; row < row_count; ++row)
1482 GL_EXTCALL(glCompressedTexSubImage2D(surface->texture_target, surface->texture_level,
1483 dst_point->x, y, update_w, format->block_height, internal, row_length, addr));
1484 y += format->block_height;
1485 addr += src_pitch;
1488 checkGLcall("glCompressedTexSubImage2D");
1490 else
1492 const BYTE *addr = data->addr;
1494 addr += src_rect->top * src_pitch;
1495 addr += src_rect->left * format->byte_count;
1497 TRACE("glTexSubImage2D, target %#x, level %d, x %d, y %d, w %d, h %d, format %#x, type %#x, addr %p.\n",
1498 surface->texture_target, surface->texture_level, dst_point->x, dst_point->y,
1499 update_w, update_h, format->glFormat, format->glType, addr);
1501 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, src_pitch / format->byte_count);
1502 gl_info->gl_ops.gl.p_glTexSubImage2D(surface->texture_target, surface->texture_level,
1503 dst_point->x, dst_point->y, update_w, update_h, format->glFormat, format->glType, addr);
1504 gl_info->gl_ops.gl.p_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
1505 checkGLcall("glTexSubImage2D");
1508 if (data->buffer_object)
1510 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
1511 checkGLcall("glBindBuffer");
1514 if (wined3d_settings.strict_draw_ordering)
1515 gl_info->gl_ops.gl.p_glFlush();
1517 if (gl_info->quirks & WINED3D_QUIRK_FBO_TEX_UPDATE)
1519 struct wined3d_device *device = surface->resource.device;
1520 unsigned int i;
1522 for (i = 0; i < device->context_count; ++i)
1524 context_surface_update(device->contexts[i], surface);
1529 static BOOL surface_check_block_align(struct wined3d_surface *surface, const RECT *rect)
1531 UINT width_mask, height_mask;
1533 if (!rect->left && !rect->top
1534 && rect->right == surface->resource.width
1535 && rect->bottom == surface->resource.height)
1536 return TRUE;
1538 /* This assumes power of two block sizes, but NPOT block sizes would be
1539 * silly anyway. */
1540 width_mask = surface->resource.format->block_width - 1;
1541 height_mask = surface->resource.format->block_height - 1;
1543 if (!(rect->left & width_mask) && !(rect->top & height_mask)
1544 && !(rect->right & width_mask) && !(rect->bottom & height_mask))
1545 return TRUE;
1547 return FALSE;
1550 HRESULT surface_upload_from_surface(struct wined3d_surface *dst_surface, const POINT *dst_point,
1551 struct wined3d_surface *src_surface, const RECT *src_rect)
1553 const struct wined3d_format *src_format;
1554 const struct wined3d_format *dst_format;
1555 unsigned int src_fmt_flags, dst_fmt_flags;
1556 const struct wined3d_gl_info *gl_info;
1557 struct wined3d_context *context;
1558 struct wined3d_bo_address data;
1559 UINT update_w, update_h;
1560 UINT dst_w, dst_h;
1561 RECT r, dst_rect;
1562 UINT src_pitch;
1563 POINT p;
1565 TRACE("dst_surface %p, dst_point %s, src_surface %p, src_rect %s.\n",
1566 dst_surface, wine_dbgstr_point(dst_point),
1567 src_surface, wine_dbgstr_rect(src_rect));
1569 src_format = src_surface->resource.format;
1570 dst_format = dst_surface->resource.format;
1571 src_fmt_flags = src_surface->container->resource.format_flags;
1572 dst_fmt_flags = dst_surface->container->resource.format_flags;
1574 if (src_format->id != dst_format->id)
1576 WARN("Source and destination surfaces should have the same format.\n");
1577 return WINED3DERR_INVALIDCALL;
1580 if (!dst_point)
1582 p.x = 0;
1583 p.y = 0;
1584 dst_point = &p;
1586 else if (dst_point->x < 0 || dst_point->y < 0)
1588 WARN("Invalid destination point.\n");
1589 return WINED3DERR_INVALIDCALL;
1592 if (!src_rect)
1594 r.left = 0;
1595 r.top = 0;
1596 r.right = src_surface->resource.width;
1597 r.bottom = src_surface->resource.height;
1598 src_rect = &r;
1600 else if (src_rect->left < 0 || src_rect->left >= src_rect->right
1601 || src_rect->top < 0 || src_rect->top >= src_rect->bottom)
1603 WARN("Invalid source rectangle.\n");
1604 return WINED3DERR_INVALIDCALL;
1607 dst_w = dst_surface->resource.width;
1608 dst_h = dst_surface->resource.height;
1610 update_w = src_rect->right - src_rect->left;
1611 update_h = src_rect->bottom - src_rect->top;
1613 if (update_w > dst_w || dst_point->x > dst_w - update_w
1614 || update_h > dst_h || dst_point->y > dst_h - update_h)
1616 WARN("Destination out of bounds.\n");
1617 return WINED3DERR_INVALIDCALL;
1620 if ((src_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(src_surface, src_rect))
1622 WARN("Source rectangle not block-aligned.\n");
1623 return WINED3DERR_INVALIDCALL;
1626 SetRect(&dst_rect, dst_point->x, dst_point->y, dst_point->x + update_w, dst_point->y + update_h);
1627 if ((dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS) && !surface_check_block_align(dst_surface, &dst_rect))
1629 WARN("Destination rectangle not block-aligned.\n");
1630 return WINED3DERR_INVALIDCALL;
1633 /* Use wined3d_surface_blt() instead of uploading directly if we need conversion. */
1634 if (dst_format->convert || wined3d_format_get_color_key_conversion(dst_surface->container, FALSE))
1635 return wined3d_surface_blt(dst_surface, &dst_rect, src_surface, src_rect, 0, NULL, WINED3D_TEXF_POINT);
1637 context = context_acquire(dst_surface->resource.device, NULL);
1638 gl_info = context->gl_info;
1640 /* Only load the surface for partial updates. For newly allocated texture
1641 * the texture wouldn't be the current location, and we'd upload zeroes
1642 * just to overwrite them again. */
1643 if (update_w == dst_w && update_h == dst_h)
1644 wined3d_texture_prepare_texture(dst_surface->container, context, FALSE);
1645 else
1646 surface_load_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1647 wined3d_texture_bind_and_dirtify(dst_surface->container, context, FALSE);
1649 surface_get_memory(src_surface, &data, src_surface->locations);
1650 src_pitch = wined3d_surface_get_pitch(src_surface);
1652 wined3d_surface_upload_data(dst_surface, gl_info, src_format, src_rect,
1653 src_pitch, dst_point, FALSE, wined3d_const_bo_address(&data));
1655 context_release(context);
1657 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
1658 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
1660 return WINED3D_OK;
1663 /* In D3D the depth stencil dimensions have to be greater than or equal to the
1664 * render target dimensions. With FBOs, the dimensions have to be an exact match. */
1665 /* TODO: We should synchronize the renderbuffer's content with the texture's content. */
1666 /* Context activation is done by the caller. */
1667 void surface_set_compatible_renderbuffer(struct wined3d_surface *surface, const struct wined3d_surface *rt)
1669 const struct wined3d_gl_info *gl_info = &surface->resource.device->adapter->gl_info;
1670 struct wined3d_renderbuffer_entry *entry;
1671 GLuint renderbuffer = 0;
1672 unsigned int src_width, src_height;
1673 unsigned int width, height;
1675 if (rt && rt->resource.format->id != WINED3DFMT_NULL)
1677 width = rt->pow2Width;
1678 height = rt->pow2Height;
1680 else
1682 width = surface->pow2Width;
1683 height = surface->pow2Height;
1686 src_width = surface->pow2Width;
1687 src_height = surface->pow2Height;
1689 /* A depth stencil smaller than the render target is not valid */
1690 if (width > src_width || height > src_height) return;
1692 /* Remove any renderbuffer set if the sizes match */
1693 if (gl_info->supported[ARB_FRAMEBUFFER_OBJECT]
1694 || (width == src_width && height == src_height))
1696 surface->current_renderbuffer = NULL;
1697 return;
1700 /* Look if we've already got a renderbuffer of the correct dimensions */
1701 LIST_FOR_EACH_ENTRY(entry, &surface->renderbuffers, struct wined3d_renderbuffer_entry, entry)
1703 if (entry->width == width && entry->height == height)
1705 renderbuffer = entry->id;
1706 surface->current_renderbuffer = entry;
1707 break;
1711 if (!renderbuffer)
1713 gl_info->fbo_ops.glGenRenderbuffers(1, &renderbuffer);
1714 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
1715 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER,
1716 surface->resource.format->glInternal, width, height);
1718 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
1719 entry->width = width;
1720 entry->height = height;
1721 entry->id = renderbuffer;
1722 list_add_head(&surface->renderbuffers, &entry->entry);
1724 surface->current_renderbuffer = entry;
1727 checkGLcall("set_compatible_renderbuffer");
1730 GLenum surface_get_gl_buffer(const struct wined3d_surface *surface)
1732 const struct wined3d_swapchain *swapchain = surface->container->swapchain;
1734 TRACE("surface %p.\n", surface);
1736 if (!swapchain)
1738 ERR("Surface %p is not on a swapchain.\n", surface);
1739 return GL_NONE;
1742 if (swapchain->back_buffers && swapchain->back_buffers[0] == surface->container)
1744 if (swapchain->render_to_fbo)
1746 TRACE("Returning GL_COLOR_ATTACHMENT0\n");
1747 return GL_COLOR_ATTACHMENT0;
1749 TRACE("Returning GL_BACK\n");
1750 return GL_BACK;
1752 else if (surface->container == swapchain->front_buffer)
1754 TRACE("Returning GL_FRONT\n");
1755 return GL_FRONT;
1758 FIXME("Higher back buffer, returning GL_BACK\n");
1759 return GL_BACK;
1762 void surface_load(struct wined3d_surface *surface, BOOL srgb)
1764 DWORD location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
1766 TRACE("surface %p, srgb %#x.\n", surface, srgb);
1768 if (surface->resource.pool == WINED3D_POOL_SCRATCH)
1769 ERR("Not supported on scratch surfaces.\n");
1771 if (surface->locations & location)
1773 TRACE("surface is already in texture\n");
1774 return;
1776 TRACE("Reloading because surface is dirty.\n");
1778 surface_load_location(surface, location);
1779 surface_evict_sysmem(surface);
1782 /* See also float_16_to_32() in wined3d_private.h */
1783 static inline unsigned short float_32_to_16(const float *in)
1785 int exp = 0;
1786 float tmp = fabsf(*in);
1787 unsigned int mantissa;
1788 unsigned short ret;
1790 /* Deal with special numbers */
1791 if (*in == 0.0f)
1792 return 0x0000;
1793 if (isnan(*in))
1794 return 0x7c01;
1795 if (isinf(*in))
1796 return (*in < 0.0f ? 0xfc00 : 0x7c00);
1798 if (tmp < (float)(1u << 10))
1802 tmp = tmp * 2.0f;
1803 exp--;
1804 } while (tmp < (float)(1u << 10));
1806 else if (tmp >= (float)(1u << 11))
1810 tmp /= 2.0f;
1811 exp++;
1812 } while (tmp >= (float)(1u << 11));
1815 mantissa = (unsigned int)tmp;
1816 if (tmp - mantissa >= 0.5f)
1817 ++mantissa; /* Round to nearest, away from zero. */
1819 exp += 10; /* Normalize the mantissa. */
1820 exp += 15; /* Exponent is encoded with excess 15. */
1822 if (exp > 30) /* too big */
1824 ret = 0x7c00; /* INF */
1826 else if (exp <= 0)
1828 /* exp == 0: Non-normalized mantissa. Returns 0x0000 (=0.0) for too small numbers. */
1829 while (exp <= 0)
1831 mantissa = mantissa >> 1;
1832 ++exp;
1834 ret = mantissa & 0x3ff;
1836 else
1838 ret = (exp << 10) | (mantissa & 0x3ff);
1841 ret |= ((*in < 0.0f ? 1 : 0) << 15); /* Add the sign */
1842 return ret;
1845 ULONG CDECL wined3d_surface_incref(struct wined3d_surface *surface)
1847 TRACE("surface %p, container %p.\n", surface, surface->container);
1849 return wined3d_texture_incref(surface->container);
1852 ULONG CDECL wined3d_surface_decref(struct wined3d_surface *surface)
1854 TRACE("surface %p, container %p.\n", surface, surface->container);
1856 return wined3d_texture_decref(surface->container);
1859 void CDECL wined3d_surface_preload(struct wined3d_surface *surface)
1861 TRACE("surface %p.\n", surface);
1863 if (!surface->resource.device->d3d_initialized)
1865 ERR("D3D not initialized.\n");
1866 return;
1869 wined3d_texture_preload(surface->container);
1872 void * CDECL wined3d_surface_get_parent(const struct wined3d_surface *surface)
1874 TRACE("surface %p.\n", surface);
1876 return surface->resource.parent;
1879 struct wined3d_resource * CDECL wined3d_surface_get_resource(struct wined3d_surface *surface)
1881 TRACE("surface %p.\n", surface);
1883 return &surface->resource;
1886 HRESULT CDECL wined3d_surface_get_blt_status(const struct wined3d_surface *surface, DWORD flags)
1888 TRACE("surface %p, flags %#x.\n", surface, flags);
1890 switch (flags)
1892 case WINEDDGBS_CANBLT:
1893 case WINEDDGBS_ISBLTDONE:
1894 return WINED3D_OK;
1896 default:
1897 return WINED3DERR_INVALIDCALL;
1901 HRESULT CDECL wined3d_surface_get_flip_status(const struct wined3d_surface *surface, DWORD flags)
1903 TRACE("surface %p, flags %#x.\n", surface, flags);
1905 /* XXX: DDERR_INVALIDSURFACETYPE */
1907 switch (flags)
1909 case WINEDDGFS_CANFLIP:
1910 case WINEDDGFS_ISFLIPDONE:
1911 return WINED3D_OK;
1913 default:
1914 return WINED3DERR_INVALIDCALL;
1918 HRESULT CDECL wined3d_surface_is_lost(const struct wined3d_surface *surface)
1920 TRACE("surface %p.\n", surface);
1922 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
1923 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
1926 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
1928 TRACE("surface %p.\n", surface);
1930 surface->flags &= ~SFLAG_LOST;
1931 return WINED3D_OK;
1934 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
1936 unsigned int alignment;
1937 DWORD pitch;
1939 TRACE("surface %p.\n", surface);
1941 if (surface->pitch)
1942 return surface->pitch;
1944 alignment = surface->resource.device->surface_alignment;
1945 pitch = wined3d_format_calculate_pitch(surface->resource.format, surface->resource.width);
1946 pitch = (pitch + alignment - 1) & ~(alignment - 1);
1948 TRACE("Returning %u.\n", pitch);
1950 return pitch;
1953 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
1955 LONG w, h;
1957 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
1959 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1961 WARN("Not an overlay surface.\n");
1962 return WINEDDERR_NOTAOVERLAYSURFACE;
1965 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
1966 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
1967 surface->overlay_destrect.left = x;
1968 surface->overlay_destrect.top = y;
1969 surface->overlay_destrect.right = x + w;
1970 surface->overlay_destrect.bottom = y + h;
1972 return WINED3D_OK;
1975 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
1977 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
1979 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1981 TRACE("Not an overlay surface.\n");
1982 return WINEDDERR_NOTAOVERLAYSURFACE;
1985 if (!surface->overlay_dest)
1987 TRACE("Overlay not visible.\n");
1988 *x = 0;
1989 *y = 0;
1990 return WINEDDERR_OVERLAYNOTVISIBLE;
1993 *x = surface->overlay_destrect.left;
1994 *y = surface->overlay_destrect.top;
1996 TRACE("Returning position %d, %d.\n", *x, *y);
1998 return WINED3D_OK;
2001 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
2002 DWORD flags, struct wined3d_surface *ref)
2004 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
2006 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2008 TRACE("Not an overlay surface.\n");
2009 return WINEDDERR_NOTAOVERLAYSURFACE;
2012 return WINED3D_OK;
2015 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
2016 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
2018 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
2019 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
2021 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
2023 WARN("Not an overlay surface.\n");
2024 return WINEDDERR_NOTAOVERLAYSURFACE;
2026 else if (!dst_surface)
2028 WARN("Dest surface is NULL.\n");
2029 return WINED3DERR_INVALIDCALL;
2032 surface_get_rect(surface, src_rect, &surface->overlay_srcrect);
2033 surface_get_rect(dst_surface, dst_rect, &surface->overlay_destrect);
2035 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2037 surface->overlay_dest = NULL;
2038 list_remove(&surface->overlay_entry);
2041 if (flags & WINEDDOVER_SHOW)
2043 if (surface->overlay_dest != dst_surface)
2045 surface->overlay_dest = dst_surface;
2046 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2049 else if (flags & WINEDDOVER_HIDE)
2051 /* tests show that the rectangles are erased on hide */
2052 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2053 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2054 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2055 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2056 surface->overlay_dest = NULL;
2059 return WINED3D_OK;
2062 HRESULT wined3d_surface_update_desc(struct wined3d_surface *surface,
2063 const struct wined3d_gl_info *gl_info, void *mem, unsigned int pitch)
2065 struct wined3d_resource *texture_resource = &surface->container->resource;
2066 unsigned int width, height;
2067 BOOL create_dib = FALSE;
2068 DWORD valid_location = 0;
2069 HRESULT hr;
2071 if (surface->flags & SFLAG_DIBSECTION)
2073 DeleteDC(surface->hDC);
2074 DeleteObject(surface->dib.DIBsection);
2075 surface->dib.bitmap_data = NULL;
2076 surface->flags &= ~SFLAG_DIBSECTION;
2077 create_dib = TRUE;
2080 surface->locations = 0;
2081 wined3d_resource_free_sysmem(&surface->resource);
2083 width = texture_resource->width;
2084 height = texture_resource->height;
2085 surface->resource.width = width;
2086 surface->resource.height = height;
2087 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2088 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2090 surface->pow2Width = width;
2091 surface->pow2Height = height;
2093 else
2095 surface->pow2Width = surface->pow2Height = 1;
2096 while (surface->pow2Width < width)
2097 surface->pow2Width <<= 1;
2098 while (surface->pow2Height < height)
2099 surface->pow2Height <<= 1;
2102 if (surface->pow2Width != width || surface->pow2Height != height)
2103 surface->flags |= SFLAG_NONPOW2;
2104 else
2105 surface->flags &= ~SFLAG_NONPOW2;
2107 if ((surface->user_memory = mem))
2109 surface->resource.map_binding = WINED3D_LOCATION_USER_MEMORY;
2110 valid_location = WINED3D_LOCATION_USER_MEMORY;
2112 surface->pitch = pitch;
2113 surface->resource.format = texture_resource->format;
2114 surface->resource.multisample_type = texture_resource->multisample_type;
2115 surface->resource.multisample_quality = texture_resource->multisample_quality;
2116 if (surface->pitch)
2118 surface->resource.size = height * surface->pitch;
2120 else
2122 /* User memory surfaces don't have the regular surface alignment. */
2123 surface->resource.size = wined3d_format_calculate_size(texture_resource->format,
2124 1, width, height, 1);
2125 surface->pitch = wined3d_format_calculate_pitch(texture_resource->format, width);
2128 /* The format might be changed to a format that needs conversion.
2129 * If the surface didn't use PBOs previously but could now, don't
2130 * change it - whatever made us not use PBOs might come back, e.g.
2131 * color keys. */
2132 if (surface->resource.map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
2133 surface->resource.map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
2135 if (create_dib)
2137 if (FAILED(hr = surface_create_dib_section(surface)))
2139 ERR("Failed to create dib section, hr %#x.\n", hr);
2140 return hr;
2142 if (!valid_location)
2143 valid_location = WINED3D_LOCATION_DIB;
2146 if (!valid_location)
2148 surface_prepare_system_memory(surface);
2149 valid_location = WINED3D_LOCATION_SYSMEM;
2152 surface_validate_location(surface, valid_location);
2154 return WINED3D_OK;
2157 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2158 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2160 unsigned short *dst_s;
2161 const float *src_f;
2162 unsigned int x, y;
2164 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2166 for (y = 0; y < h; ++y)
2168 src_f = (const float *)(src + y * pitch_in);
2169 dst_s = (unsigned short *) (dst + y * pitch_out);
2170 for (x = 0; x < w; ++x)
2172 dst_s[x] = float_32_to_16(src_f + x);
2177 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2178 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2180 static const unsigned char convert_5to8[] =
2182 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2183 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2184 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2185 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2187 static const unsigned char convert_6to8[] =
2189 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2190 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2191 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2192 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2193 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2194 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2195 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2196 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2198 unsigned int x, y;
2200 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2202 for (y = 0; y < h; ++y)
2204 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2205 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2206 for (x = 0; x < w; ++x)
2208 WORD pixel = src_line[x];
2209 dst_line[x] = 0xff000000u
2210 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
2211 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
2212 | convert_5to8[(pixel & 0x001fu)];
2217 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2218 * in both cases we're just setting the X / Alpha channel to 0xff. */
2219 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2220 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2222 unsigned int x, y;
2224 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2226 for (y = 0; y < h; ++y)
2228 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2229 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2231 for (x = 0; x < w; ++x)
2233 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2238 static inline BYTE cliptobyte(int x)
2240 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2243 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2244 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2246 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2247 unsigned int x, y;
2249 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2251 for (y = 0; y < h; ++y)
2253 const BYTE *src_line = src + y * pitch_in;
2254 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2255 for (x = 0; x < w; ++x)
2257 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2258 * C = Y - 16; D = U - 128; E = V - 128;
2259 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2260 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2261 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2262 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2263 * U and V are shared between the pixels. */
2264 if (!(x & 1)) /* For every even pixel, read new U and V. */
2266 d = (int) src_line[1] - 128;
2267 e = (int) src_line[3] - 128;
2268 r2 = 409 * e + 128;
2269 g2 = - 100 * d - 208 * e + 128;
2270 b2 = 516 * d + 128;
2272 c2 = 298 * ((int) src_line[0] - 16);
2273 dst_line[x] = 0xff000000
2274 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2275 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2276 | cliptobyte((c2 + b2) >> 8); /* blue */
2277 /* Scale RGB values to 0..255 range,
2278 * then clip them if still not in range (may be negative),
2279 * then shift them within DWORD if necessary. */
2280 src_line += 2;
2285 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2286 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2288 unsigned int x, y;
2289 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2291 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2293 for (y = 0; y < h; ++y)
2295 const BYTE *src_line = src + y * pitch_in;
2296 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2297 for (x = 0; x < w; ++x)
2299 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2300 * C = Y - 16; D = U - 128; E = V - 128;
2301 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2302 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2303 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2304 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2305 * U and V are shared between the pixels. */
2306 if (!(x & 1)) /* For every even pixel, read new U and V. */
2308 d = (int) src_line[1] - 128;
2309 e = (int) src_line[3] - 128;
2310 r2 = 409 * e + 128;
2311 g2 = - 100 * d - 208 * e + 128;
2312 b2 = 516 * d + 128;
2314 c2 = 298 * ((int) src_line[0] - 16);
2315 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2316 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2317 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2318 /* Scale RGB values to 0..255 range,
2319 * then clip them if still not in range (may be negative),
2320 * then shift them within DWORD if necessary. */
2321 src_line += 2;
2326 struct d3dfmt_converter_desc
2328 enum wined3d_format_id from, to;
2329 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2332 static const struct d3dfmt_converter_desc converters[] =
2334 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2335 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2336 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2337 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2338 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2339 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2342 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2343 enum wined3d_format_id to)
2345 unsigned int i;
2347 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2349 if (converters[i].from == from && converters[i].to == to)
2350 return &converters[i];
2353 return NULL;
2356 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2358 struct wined3d_map_desc src_map, dst_map;
2359 const struct d3dfmt_converter_desc *conv;
2360 struct wined3d_texture *ret = NULL;
2361 struct wined3d_resource_desc desc;
2362 struct wined3d_surface *dst;
2364 conv = find_converter(source->resource.format->id, to_fmt);
2365 if (!conv)
2367 FIXME("Cannot find a conversion function from format %s to %s.\n",
2368 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2369 return NULL;
2372 /* FIXME: Multisampled conversion? */
2373 wined3d_resource_get_desc(&source->resource, &desc);
2374 desc.resource_type = WINED3D_RTYPE_TEXTURE;
2375 desc.format = to_fmt;
2376 desc.usage = 0;
2377 desc.pool = WINED3D_POOL_SCRATCH;
2378 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2379 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, NULL, &wined3d_null_parent_ops, &ret)))
2381 ERR("Failed to create a destination surface for conversion.\n");
2382 return NULL;
2384 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2386 memset(&src_map, 0, sizeof(src_map));
2387 memset(&dst_map, 0, sizeof(dst_map));
2389 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2391 ERR("Failed to lock the source surface.\n");
2392 wined3d_texture_decref(ret);
2393 return NULL;
2395 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2397 ERR("Failed to lock the destination surface.\n");
2398 wined3d_surface_unmap(source);
2399 wined3d_texture_decref(ret);
2400 return NULL;
2403 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2404 source->resource.width, source->resource.height);
2406 wined3d_surface_unmap(dst);
2407 wined3d_surface_unmap(source);
2409 return ret;
2412 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2413 unsigned int bpp, UINT pitch, DWORD color)
2415 BYTE *first;
2416 unsigned int x, y;
2418 /* Do first row */
2420 #define COLORFILL_ROW(type) \
2421 do { \
2422 type *d = (type *)buf; \
2423 for (x = 0; x < width; ++x) \
2424 d[x] = (type)color; \
2425 } while(0)
2427 switch (bpp)
2429 case 1:
2430 COLORFILL_ROW(BYTE);
2431 break;
2433 case 2:
2434 COLORFILL_ROW(WORD);
2435 break;
2437 case 3:
2439 BYTE *d = buf;
2440 for (x = 0; x < width; ++x, d += 3)
2442 d[0] = (color ) & 0xff;
2443 d[1] = (color >> 8) & 0xff;
2444 d[2] = (color >> 16) & 0xff;
2446 break;
2448 case 4:
2449 COLORFILL_ROW(DWORD);
2450 break;
2452 default:
2453 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2454 return WINED3DERR_NOTAVAILABLE;
2457 #undef COLORFILL_ROW
2459 /* Now copy first row. */
2460 first = buf;
2461 for (y = 1; y < height; ++y)
2463 buf += pitch;
2464 memcpy(buf, first, width * bpp);
2467 return WINED3D_OK;
2470 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
2472 return surface_from_resource(resource);
2475 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
2477 TRACE("surface %p.\n", surface);
2479 if (!surface->resource.map_count)
2481 WARN("Trying to unmap unmapped surface.\n");
2482 return WINEDDERR_NOTLOCKED;
2484 --surface->resource.map_count;
2486 surface->surface_ops->surface_unmap(surface);
2488 return WINED3D_OK;
2491 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
2492 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
2494 const struct wined3d_format *format = surface->resource.format;
2495 unsigned int fmt_flags = surface->container->resource.format_flags;
2496 struct wined3d_device *device = surface->resource.device;
2497 struct wined3d_context *context;
2498 const struct wined3d_gl_info *gl_info;
2499 BYTE *base_memory;
2501 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
2502 surface, map_desc, wine_dbgstr_rect(rect), flags);
2504 if (surface->resource.map_count)
2506 WARN("Surface is already mapped.\n");
2507 return WINED3DERR_INVALIDCALL;
2510 if ((fmt_flags & WINED3DFMT_FLAG_BLOCKS) && rect
2511 && !surface_check_block_align(surface, rect))
2513 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
2514 wine_dbgstr_rect(rect), format->block_width, format->block_height);
2516 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
2517 return WINED3DERR_INVALIDCALL;
2520 ++surface->resource.map_count;
2522 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
2523 WARN("Trying to lock unlockable surface.\n");
2525 /* Performance optimization: Count how often a surface is mapped, if it is
2526 * mapped regularly do not throw away the system memory copy. This avoids
2527 * the need to download the surface from OpenGL all the time. The surface
2528 * is still downloaded if the OpenGL texture is changed. Note that this
2529 * only really makes sense for managed textures.*/
2530 if (!(surface->container->flags & WINED3D_TEXTURE_DYNAMIC_MAP)
2531 && surface->resource.map_binding == WINED3D_LOCATION_SYSMEM)
2533 if (++surface->lockCount > MAXLOCKCOUNT)
2535 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
2536 surface->container->flags |= WINED3D_TEXTURE_DYNAMIC_MAP;
2540 surface_prepare_map_memory(surface);
2541 if (flags & WINED3D_MAP_DISCARD)
2543 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
2544 wined3d_debug_location(surface->resource.map_binding));
2545 surface_validate_location(surface, surface->resource.map_binding);
2547 else
2549 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
2550 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
2552 surface_load_location(surface, surface->resource.map_binding);
2555 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
2556 surface_invalidate_location(surface, ~surface->resource.map_binding);
2558 switch (surface->resource.map_binding)
2560 case WINED3D_LOCATION_SYSMEM:
2561 base_memory = surface->resource.heap_memory;
2562 break;
2564 case WINED3D_LOCATION_USER_MEMORY:
2565 base_memory = surface->user_memory;
2566 break;
2568 case WINED3D_LOCATION_DIB:
2569 base_memory = surface->dib.bitmap_data;
2570 break;
2572 case WINED3D_LOCATION_BUFFER:
2573 context = context_acquire(device, NULL);
2574 gl_info = context->gl_info;
2576 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
2577 base_memory = GL_EXTCALL(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_WRITE));
2578 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
2579 checkGLcall("map PBO");
2581 context_release(context);
2582 break;
2584 default:
2585 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
2586 base_memory = NULL;
2589 if (fmt_flags & WINED3DFMT_FLAG_BROKEN_PITCH)
2590 map_desc->row_pitch = surface->resource.width * format->byte_count;
2591 else
2592 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
2593 map_desc->slice_pitch = 0;
2595 if (!rect)
2597 map_desc->data = base_memory;
2598 surface->lockedRect.left = 0;
2599 surface->lockedRect.top = 0;
2600 surface->lockedRect.right = surface->resource.width;
2601 surface->lockedRect.bottom = surface->resource.height;
2603 else
2605 if ((fmt_flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
2607 /* Compressed textures are block based, so calculate the offset of
2608 * the block that contains the top-left pixel of the locked rectangle. */
2609 map_desc->data = base_memory
2610 + ((rect->top / format->block_height) * map_desc->row_pitch)
2611 + ((rect->left / format->block_width) * format->block_byte_count);
2613 else
2615 map_desc->data = base_memory
2616 + (map_desc->row_pitch * rect->top)
2617 + (rect->left * format->byte_count);
2619 surface->lockedRect.left = rect->left;
2620 surface->lockedRect.top = rect->top;
2621 surface->lockedRect.right = rect->right;
2622 surface->lockedRect.bottom = rect->bottom;
2625 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
2626 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
2628 return WINED3D_OK;
2631 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
2633 HRESULT hr;
2635 TRACE("surface %p, dc %p.\n", surface, dc);
2637 /* Give more detailed info for ddraw. */
2638 if (surface->flags & SFLAG_DCINUSE)
2639 return WINEDDERR_DCALREADYCREATED;
2641 /* Can't GetDC if the surface is locked. */
2642 if (surface->resource.map_count)
2643 return WINED3DERR_INVALIDCALL;
2645 /* Create a DIB section if there isn't a dc yet. */
2646 if (!surface->hDC)
2648 if (surface->flags & SFLAG_CLIENT)
2650 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
2651 surface_release_client_storage(surface);
2653 hr = surface_create_dib_section(surface);
2654 if (FAILED(hr))
2655 return WINED3DERR_INVALIDCALL;
2656 if (!(surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2657 || surface->container->flags & WINED3D_TEXTURE_PIN_SYSMEM
2658 || surface->pbo))
2659 surface->resource.map_binding = WINED3D_LOCATION_DIB;
2662 surface_load_location(surface, WINED3D_LOCATION_DIB);
2663 surface_invalidate_location(surface, ~WINED3D_LOCATION_DIB);
2665 surface->flags |= SFLAG_DCINUSE;
2666 surface->resource.map_count++;
2668 *dc = surface->hDC;
2669 TRACE("Returning dc %p.\n", *dc);
2671 return WINED3D_OK;
2674 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
2676 TRACE("surface %p, dc %p.\n", surface, dc);
2678 if (!(surface->flags & SFLAG_DCINUSE))
2679 return WINEDDERR_NODC;
2681 if (surface->hDC != dc)
2683 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
2684 dc, surface->hDC);
2685 return WINEDDERR_NODC;
2688 surface->resource.map_count--;
2689 surface->flags &= ~SFLAG_DCINUSE;
2691 if (surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2692 || (surface->container->flags & WINED3D_TEXTURE_PIN_SYSMEM
2693 && surface->resource.map_binding != WINED3D_LOCATION_DIB))
2695 /* The game Salammbo modifies the surface contents without mapping the surface between
2696 * a GetDC/ReleaseDC operation and flipping the surface. If the DIB remains the active
2697 * copy and is copied to the screen, this update, which draws the mouse pointer, is lost.
2698 * Do not only copy the DIB to the map location, but also make sure the map location is
2699 * copied back to the DIB in the next getdc call.
2701 * The same consideration applies to user memory surfaces. */
2702 surface_load_location(surface, surface->resource.map_binding);
2703 surface_invalidate_location(surface, WINED3D_LOCATION_DIB);
2706 return WINED3D_OK;
2709 static void read_from_framebuffer(struct wined3d_surface *surface, DWORD dst_location)
2711 struct wined3d_device *device = surface->resource.device;
2712 const struct wined3d_gl_info *gl_info;
2713 struct wined3d_context *context;
2714 BYTE *mem;
2715 BYTE *row, *top, *bottom;
2716 int i;
2717 BOOL srcIsUpsideDown;
2718 struct wined3d_bo_address data;
2720 surface_get_memory(surface, &data, dst_location);
2722 context = context_acquire(device, surface);
2723 context_apply_blit_state(context, device);
2724 gl_info = context->gl_info;
2726 /* Select the correct read buffer, and give some debug output.
2727 * There is no need to keep track of the current read buffer or reset it, every part of the code
2728 * that reads sets the read buffer as desired.
2730 if (wined3d_resource_is_offscreen(&surface->container->resource))
2732 /* Mapping the primary render target which is not on a swapchain.
2733 * Read from the back buffer. */
2734 TRACE("Mapping offscreen render target.\n");
2735 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2736 srcIsUpsideDown = TRUE;
2738 else
2740 /* Onscreen surfaces are always part of a swapchain */
2741 GLenum buffer = surface_get_gl_buffer(surface);
2742 TRACE("Mapping %#x buffer.\n", buffer);
2743 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
2744 checkGLcall("glReadBuffer");
2745 srcIsUpsideDown = FALSE;
2748 if (data.buffer_object)
2750 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
2751 checkGLcall("glBindBuffer");
2754 /* Setup pixel store pack state -- to glReadPixels into the correct place */
2755 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH,
2756 wined3d_surface_get_pitch(surface) / surface->resource.format->byte_count);
2757 checkGLcall("glPixelStorei");
2759 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
2760 surface->resource.width, surface->resource.height,
2761 surface->resource.format->glFormat,
2762 surface->resource.format->glType, data.addr);
2763 checkGLcall("glReadPixels");
2765 /* Reset previous pixel store pack state */
2766 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
2767 checkGLcall("glPixelStorei");
2769 if (!srcIsUpsideDown)
2771 /* glReadPixels returns the image upside down, and there is no way to prevent this.
2772 * Flip the lines in software. */
2773 UINT pitch = wined3d_surface_get_pitch(surface);
2775 if (!(row = HeapAlloc(GetProcessHeap(), 0, pitch)))
2776 goto error;
2778 if (data.buffer_object)
2780 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
2781 checkGLcall("glMapBuffer");
2783 else
2784 mem = data.addr;
2786 top = mem;
2787 bottom = mem + pitch * (surface->resource.height - 1);
2788 for (i = 0; i < surface->resource.height / 2; i++)
2790 memcpy(row, top, pitch);
2791 memcpy(top, bottom, pitch);
2792 memcpy(bottom, row, pitch);
2793 top += pitch;
2794 bottom -= pitch;
2796 HeapFree(GetProcessHeap(), 0, row);
2798 if (data.buffer_object)
2799 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
2802 error:
2803 if (data.buffer_object)
2805 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
2806 checkGLcall("glBindBuffer");
2809 context_release(context);
2812 /* Read the framebuffer contents into a texture. Note that this function
2813 * doesn't do any kind of flipping. Using this on an onscreen surface will
2814 * result in a flipped D3D texture. */
2815 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
2817 struct wined3d_device *device = surface->resource.device;
2818 const struct wined3d_gl_info *gl_info;
2819 struct wined3d_context *context;
2821 context = context_acquire(device, surface);
2822 gl_info = context->gl_info;
2823 device_invalidate_state(device, STATE_FRAMEBUFFER);
2825 wined3d_texture_prepare_texture(surface->container, context, srgb);
2826 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
2828 TRACE("Reading back offscreen render target %p.\n", surface);
2830 if (wined3d_resource_is_offscreen(&surface->container->resource))
2831 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2832 else
2833 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
2834 checkGLcall("glReadBuffer");
2836 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
2837 0, 0, 0, 0, surface->resource.width, surface->resource.height);
2838 checkGLcall("glCopyTexSubImage2D");
2840 context_release(context);
2843 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
2845 if (multisample)
2847 DWORD samples;
2849 if (surface->rb_multisample)
2850 return;
2852 /* TODO: Nvidia exposes their Coverage Sample Anti-Aliasing (CSAA) feature
2853 * through type == MULTISAMPLE_XX and quality != 0. This could be mapped
2854 * to GL_NV_framebuffer_multisample_coverage.
2856 * AMD has a similar feature called Enhanced Quality Anti-Aliasing (EQAA),
2857 * but it does not have an equivalent OpenGL extension. */
2858 if (surface->resource.multisample_type == WINED3D_MULTISAMPLE_NON_MASKABLE)
2859 samples = surface->resource.multisample_quality;
2860 else
2861 samples = surface->resource.multisample_type;
2863 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
2864 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
2865 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
2866 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
2867 checkGLcall("glRenderbufferStorageMultisample()");
2868 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
2870 else
2872 if (surface->rb_resolved)
2873 return;
2875 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
2876 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
2877 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
2878 surface->pow2Width, surface->pow2Height);
2879 checkGLcall("glRenderbufferStorage()");
2880 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
2884 void flip_surface(struct wined3d_surface *front, struct wined3d_surface *back)
2886 if (front->container->level_count != 1 || front->container->layer_count != 1
2887 || back->container->level_count != 1 || back->container->layer_count != 1)
2888 ERR("Flip between surfaces %p and %p not supported.\n", front, back);
2890 /* Flip the surface contents */
2891 /* Flip the DC */
2893 HDC tmp;
2894 tmp = front->hDC;
2895 front->hDC = back->hDC;
2896 back->hDC = tmp;
2899 /* Flip the DIBsection */
2901 HBITMAP tmp = front->dib.DIBsection;
2902 front->dib.DIBsection = back->dib.DIBsection;
2903 back->dib.DIBsection = tmp;
2906 /* Flip the surface data */
2908 void* tmp;
2910 tmp = front->dib.bitmap_data;
2911 front->dib.bitmap_data = back->dib.bitmap_data;
2912 back->dib.bitmap_data = tmp;
2914 tmp = front->resource.heap_memory;
2915 front->resource.heap_memory = back->resource.heap_memory;
2916 back->resource.heap_memory = tmp;
2919 /* Flip the PBO */
2921 GLuint tmp_pbo = front->pbo;
2922 front->pbo = back->pbo;
2923 back->pbo = tmp_pbo;
2926 /* Flip the opengl texture */
2928 GLuint tmp;
2930 tmp = back->container->texture_rgb.name;
2931 back->container->texture_rgb.name = front->container->texture_rgb.name;
2932 front->container->texture_rgb.name = tmp;
2934 tmp = back->container->texture_srgb.name;
2935 back->container->texture_srgb.name = front->container->texture_srgb.name;
2936 front->container->texture_srgb.name = tmp;
2938 tmp = back->rb_multisample;
2939 back->rb_multisample = front->rb_multisample;
2940 front->rb_multisample = tmp;
2942 tmp = back->rb_resolved;
2943 back->rb_resolved = front->rb_resolved;
2944 front->rb_resolved = tmp;
2946 resource_unload(&back->resource);
2947 resource_unload(&front->resource);
2951 DWORD tmp_flags = back->flags;
2952 back->flags = front->flags;
2953 front->flags = tmp_flags;
2955 tmp_flags = back->locations;
2956 back->locations = front->locations;
2957 front->locations = tmp_flags;
2961 /* Does a direct frame buffer -> texture copy. Stretching is done with single
2962 * pixel copy calls. */
2963 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
2964 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
2966 struct wined3d_device *device = dst_surface->resource.device;
2967 const struct wined3d_gl_info *gl_info;
2968 float xrel, yrel;
2969 struct wined3d_context *context;
2970 BOOL upsidedown = FALSE;
2971 RECT dst_rect = *dst_rect_in;
2973 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2974 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2976 if(dst_rect.top > dst_rect.bottom) {
2977 UINT tmp = dst_rect.bottom;
2978 dst_rect.bottom = dst_rect.top;
2979 dst_rect.top = tmp;
2980 upsidedown = TRUE;
2983 context = context_acquire(device, src_surface);
2984 gl_info = context->gl_info;
2985 context_apply_blit_state(context, device);
2986 wined3d_texture_load(dst_surface->container, context, FALSE);
2988 /* Bind the target texture */
2989 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
2990 if (wined3d_resource_is_offscreen(&src_surface->container->resource))
2992 TRACE("Reading from an offscreen target\n");
2993 upsidedown = !upsidedown;
2994 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2996 else
2998 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3000 checkGLcall("glReadBuffer");
3002 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
3003 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
3005 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3007 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
3009 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3010 ERR("Texture filtering not supported in direct blit.\n");
3012 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
3013 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3015 ERR("Texture filtering not supported in direct blit\n");
3018 if (upsidedown
3019 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3020 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
3022 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
3023 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3024 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
3025 src_rect->left, src_surface->resource.height - src_rect->bottom,
3026 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3028 else
3030 LONG row;
3031 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
3032 /* I have to process this row by row to swap the image,
3033 * otherwise it would be upside down, so stretching in y direction
3034 * doesn't cost extra time
3036 * However, stretching in x direction can be avoided if not necessary
3038 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
3039 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
3041 /* Well, that stuff works, but it's very slow.
3042 * find a better way instead
3044 LONG col;
3046 for (col = dst_rect.left; col < dst_rect.right; ++col)
3048 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3049 dst_rect.left + col /* x offset */, row /* y offset */,
3050 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
3053 else
3055 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
3056 dst_rect.left /* x offset */, row /* y offset */,
3057 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
3061 checkGLcall("glCopyTexSubImage2D");
3063 context_release(context);
3065 /* The texture is now most up to date - If the surface is a render target
3066 * and has a drawable, this path is never entered. */
3067 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3068 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3071 /* Uses the hardware to stretch and flip the image */
3072 static void fb_copy_to_texture_hwstretch(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
3073 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
3075 struct wined3d_device *device = dst_surface->resource.device;
3076 GLuint src, backup = 0;
3077 float left, right, top, bottom; /* Texture coordinates */
3078 UINT fbwidth = src_surface->resource.width;
3079 UINT fbheight = src_surface->resource.height;
3080 const struct wined3d_gl_info *gl_info;
3081 struct wined3d_context *context;
3082 GLenum drawBuffer = GL_BACK;
3083 GLenum texture_target;
3084 BOOL noBackBufferBackup;
3085 BOOL src_offscreen;
3086 BOOL upsidedown = FALSE;
3087 RECT dst_rect = *dst_rect_in;
3089 TRACE("Using hwstretch blit\n");
3090 /* Activate the Proper context for reading from the source surface, set it up for blitting */
3091 context = context_acquire(device, src_surface);
3092 gl_info = context->gl_info;
3093 context_apply_blit_state(context, device);
3094 wined3d_texture_load(dst_surface->container, context, FALSE);
3096 src_offscreen = wined3d_resource_is_offscreen(&src_surface->container->resource);
3097 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
3098 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
3100 /* Get it a description */
3101 wined3d_texture_load(src_surface->container, context, FALSE);
3104 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
3105 * This way we don't have to wait for the 2nd readback to finish to leave this function.
3107 if (context->aux_buffers >= 2)
3109 /* Got more than one aux buffer? Use the 2nd aux buffer */
3110 drawBuffer = GL_AUX1;
3112 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3114 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3115 drawBuffer = GL_AUX0;
3118 if (noBackBufferBackup)
3120 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
3121 checkGLcall("glGenTextures");
3122 context_bind_texture(context, GL_TEXTURE_2D, backup);
3123 texture_target = GL_TEXTURE_2D;
3125 else
3127 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3128 * we are reading from the back buffer, the backup can be used as source texture
3130 texture_target = src_surface->texture_target;
3131 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
3132 gl_info->gl_ops.gl.p_glEnable(texture_target);
3133 checkGLcall("glEnable(texture_target)");
3135 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3136 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
3139 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3140 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3142 if(dst_rect.top > dst_rect.bottom) {
3143 UINT tmp = dst_rect.bottom;
3144 dst_rect.bottom = dst_rect.top;
3145 dst_rect.top = tmp;
3146 upsidedown = TRUE;
3149 if (src_offscreen)
3151 TRACE("Reading from an offscreen target\n");
3152 upsidedown = !upsidedown;
3153 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3155 else
3157 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3160 /* TODO: Only back up the part that will be overwritten */
3161 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
3163 checkGLcall("glCopyTexSubImage2D");
3165 /* No issue with overriding these - the sampler is dirty due to blit usage */
3166 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
3167 checkGLcall("glTexParameteri");
3168 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3169 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
3170 checkGLcall("glTexParameteri");
3172 if (!src_surface->container->swapchain
3173 || src_surface->container == src_surface->container->swapchain->back_buffers[0])
3175 src = backup ? backup : src_surface->container->texture_rgb.name;
3177 else
3179 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
3180 checkGLcall("glReadBuffer(GL_FRONT)");
3182 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
3183 checkGLcall("glGenTextures(1, &src)");
3184 context_bind_texture(context, GL_TEXTURE_2D, src);
3186 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3187 * out for power of 2 sizes
3189 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
3190 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3191 checkGLcall("glTexImage2D");
3192 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
3194 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3195 checkGLcall("glTexParameteri");
3196 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3197 checkGLcall("glTexParameteri");
3199 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
3200 checkGLcall("glReadBuffer(GL_BACK)");
3202 if (texture_target != GL_TEXTURE_2D)
3204 gl_info->gl_ops.gl.p_glDisable(texture_target);
3205 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3206 texture_target = GL_TEXTURE_2D;
3209 checkGLcall("glEnd and previous");
3211 left = src_rect->left;
3212 right = src_rect->right;
3214 if (!upsidedown)
3216 top = src_surface->resource.height - src_rect->top;
3217 bottom = src_surface->resource.height - src_rect->bottom;
3219 else
3221 top = src_surface->resource.height - src_rect->bottom;
3222 bottom = src_surface->resource.height - src_rect->top;
3225 if (src_surface->container->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
3227 left /= src_surface->pow2Width;
3228 right /= src_surface->pow2Width;
3229 top /= src_surface->pow2Height;
3230 bottom /= src_surface->pow2Height;
3233 /* draw the source texture stretched and upside down. The correct surface is bound already */
3234 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3235 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3237 context_set_draw_buffer(context, drawBuffer);
3238 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
3240 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3241 /* bottom left */
3242 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
3243 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3245 /* top left */
3246 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
3247 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
3249 /* top right */
3250 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
3251 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3253 /* bottom right */
3254 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
3255 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
3256 gl_info->gl_ops.gl.p_glEnd();
3257 checkGLcall("glEnd and previous");
3259 if (texture_target != dst_surface->texture_target)
3261 gl_info->gl_ops.gl.p_glDisable(texture_target);
3262 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
3263 texture_target = dst_surface->texture_target;
3266 /* Now read the stretched and upside down image into the destination texture */
3267 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
3268 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
3270 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
3271 0, 0, /* We blitted the image to the origin */
3272 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3273 checkGLcall("glCopyTexSubImage2D");
3275 if (drawBuffer == GL_BACK)
3277 /* Write the back buffer backup back. */
3278 if (backup)
3280 if (texture_target != GL_TEXTURE_2D)
3282 gl_info->gl_ops.gl.p_glDisable(texture_target);
3283 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3284 texture_target = GL_TEXTURE_2D;
3286 context_bind_texture(context, GL_TEXTURE_2D, backup);
3288 else
3290 if (texture_target != src_surface->texture_target)
3292 gl_info->gl_ops.gl.p_glDisable(texture_target);
3293 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
3294 texture_target = src_surface->texture_target;
3296 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
3299 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3300 /* top left */
3301 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
3302 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
3304 /* bottom left */
3305 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
3306 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3308 /* bottom right */
3309 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
3310 (float)fbheight / (float)src_surface->pow2Height);
3311 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
3313 /* top right */
3314 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
3315 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
3316 gl_info->gl_ops.gl.p_glEnd();
3318 gl_info->gl_ops.gl.p_glDisable(texture_target);
3319 checkGLcall("glDisable(texture_target)");
3321 /* Cleanup */
3322 if (src != src_surface->container->texture_rgb.name && src != backup)
3324 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
3325 checkGLcall("glDeleteTextures(1, &src)");
3327 if (backup)
3329 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
3330 checkGLcall("glDeleteTextures(1, &backup)");
3333 if (wined3d_settings.strict_draw_ordering)
3334 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3336 context_release(context);
3338 /* The texture is now most up to date - If the surface is a render target
3339 * and has a drawable, this path is never entered. */
3340 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3341 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3344 /* Front buffer coordinates are always full screen coordinates, but our GL
3345 * drawable is limited to the window's client area. The sysmem and texture
3346 * copies do have the full screen size. Note that GL has a bottom-left
3347 * origin, while D3D has a top-left origin. */
3348 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
3350 UINT drawable_height;
3352 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
3354 POINT offset = {0, 0};
3355 RECT windowsize;
3357 ScreenToClient(window, &offset);
3358 OffsetRect(rect, offset.x, offset.y);
3360 GetClientRect(window, &windowsize);
3361 drawable_height = windowsize.bottom - windowsize.top;
3363 else
3365 drawable_height = surface->resource.height;
3368 rect->top = drawable_height - rect->top;
3369 rect->bottom = drawable_height - rect->bottom;
3372 static void surface_blt_to_drawable(const struct wined3d_device *device,
3373 enum wined3d_texture_filter_type filter, BOOL alpha_test,
3374 struct wined3d_surface *src_surface, const RECT *src_rect_in,
3375 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
3377 const struct wined3d_gl_info *gl_info;
3378 struct wined3d_context *context;
3379 RECT src_rect, dst_rect;
3381 src_rect = *src_rect_in;
3382 dst_rect = *dst_rect_in;
3384 context = context_acquire(device, dst_surface);
3385 gl_info = context->gl_info;
3387 /* Make sure the surface is up-to-date. This should probably use
3388 * surface_load_location() and worry about the destination surface too,
3389 * unless we're overwriting it completely. */
3390 wined3d_texture_load(src_surface->container, context, FALSE);
3392 /* Activate the destination context, set it up for blitting */
3393 context_apply_blit_state(context, device);
3395 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
3396 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
3398 device->blitter->set_shader(device->blit_priv, context, src_surface, NULL);
3400 if (alpha_test)
3402 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
3403 checkGLcall("glEnable(GL_ALPHA_TEST)");
3405 /* For P8 surfaces, the alpha component contains the palette index.
3406 * Which means that the colorkey is one of the palette entries. In
3407 * other cases pixels that should be masked away have alpha set to 0. */
3408 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
3409 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
3410 (float)src_surface->container->async.src_blt_color_key.color_space_low_value / 255.0f);
3411 else
3412 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
3413 checkGLcall("glAlphaFunc");
3415 else
3417 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3418 checkGLcall("glDisable(GL_ALPHA_TEST)");
3421 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
3423 if (alpha_test)
3425 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3426 checkGLcall("glDisable(GL_ALPHA_TEST)");
3429 /* Leave the opengl state valid for blitting */
3430 device->blitter->unset_shader(context->gl_info);
3432 if (wined3d_settings.strict_draw_ordering
3433 || (dst_surface->container->swapchain
3434 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
3435 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3437 context_release(context);
3440 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
3442 struct wined3d_device *device = s->resource.device;
3443 const struct blit_shader *blitter;
3445 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, WINED3D_BLIT_OP_COLOR_FILL,
3446 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
3447 if (!blitter)
3449 FIXME("No blitter is capable of performing the requested color fill operation.\n");
3450 return WINED3DERR_INVALIDCALL;
3453 return blitter->color_fill(device, s, rect, color);
3456 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3457 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
3458 enum wined3d_texture_filter_type filter)
3460 struct wined3d_device *device = dst_surface->resource.device;
3461 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
3462 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3464 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
3465 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3466 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
3468 /* Get the swapchain. One of the surfaces has to be a primary surface */
3469 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3471 WARN("Destination is in sysmem, rejecting gl blt\n");
3472 return WINED3DERR_INVALIDCALL;
3475 dst_swapchain = dst_surface->container->swapchain;
3477 if (src_surface)
3479 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3481 WARN("Src is in sysmem, rejecting gl blt\n");
3482 return WINED3DERR_INVALIDCALL;
3485 src_swapchain = src_surface->container->swapchain;
3487 else
3489 src_swapchain = NULL;
3492 /* Early sort out of cases where no render target is used */
3493 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
3495 TRACE("No surface is render target, not using hardware blit.\n");
3496 return WINED3DERR_INVALIDCALL;
3499 /* No destination color keying supported */
3500 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
3502 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3503 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3504 return WINED3DERR_INVALIDCALL;
3507 if (dst_swapchain && dst_swapchain == src_swapchain)
3509 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3510 return WINED3DERR_INVALIDCALL;
3513 if (dst_swapchain && src_swapchain)
3515 FIXME("Implement hardware blit between two different swapchains\n");
3516 return WINED3DERR_INVALIDCALL;
3519 if (dst_swapchain)
3521 /* Handled with regular texture -> swapchain blit */
3522 if (src_surface == rt)
3523 TRACE("Blit from active render target to a swapchain\n");
3525 else if (src_swapchain && dst_surface == rt)
3527 FIXME("Implement blit from a swapchain to the active render target\n");
3528 return WINED3DERR_INVALIDCALL;
3531 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
3533 /* Blit from render target to texture */
3534 BOOL stretchx;
3536 /* P8 read back is not implemented */
3537 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
3538 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
3540 TRACE("P8 read back not supported by frame buffer to texture blit\n");
3541 return WINED3DERR_INVALIDCALL;
3544 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3546 TRACE("Color keying not supported by frame buffer to texture blit\n");
3547 return WINED3DERR_INVALIDCALL;
3548 /* Destination color key is checked above */
3551 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
3552 stretchx = TRUE;
3553 else
3554 stretchx = FALSE;
3556 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3557 * flip the image nor scale it.
3559 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3560 * -> If the app wants an image width an unscaled width, copy it line per line
3561 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
3562 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3563 * back buffer. This is slower than reading line per line, thus not used for flipping
3564 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3565 * pixel by pixel. */
3566 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
3567 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
3569 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
3570 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
3572 else
3574 TRACE("Using hardware stretching to flip / stretch the texture.\n");
3575 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
3578 surface_evict_sysmem(dst_surface);
3580 return WINED3D_OK;
3583 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3584 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3585 return WINED3DERR_INVALIDCALL;
3588 /* Context activation is done by the caller. */
3589 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
3590 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
3592 struct wined3d_device *device = surface->resource.device;
3593 const struct wined3d_gl_info *gl_info = context->gl_info;
3594 GLint compare_mode = GL_NONE;
3595 struct blt_info info;
3596 GLint old_binding = 0;
3597 RECT rect;
3599 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
3601 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
3602 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
3603 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3604 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
3605 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
3606 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
3607 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
3608 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
3609 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
3610 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
3611 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
3613 SetRect(&rect, 0, h, w, 0);
3614 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
3615 context_active_texture(context, context->gl_info, 0);
3616 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
3617 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
3618 if (gl_info->supported[ARB_SHADOW])
3620 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
3621 if (compare_mode != GL_NONE)
3622 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
3625 device->shader_backend->shader_select_depth_blt(device->shader_priv,
3626 gl_info, info.tex_type, &surface->ds_current_size);
3628 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
3629 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
3630 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
3631 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
3632 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
3633 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
3634 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
3635 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
3636 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
3637 gl_info->gl_ops.gl.p_glEnd();
3639 if (compare_mode != GL_NONE)
3640 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
3641 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
3643 gl_info->gl_ops.gl.p_glPopAttrib();
3645 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
3648 void surface_modify_ds_location(struct wined3d_surface *surface,
3649 DWORD location, UINT w, UINT h)
3651 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
3653 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
3654 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
3655 wined3d_texture_set_dirty(surface->container);
3657 surface->ds_current_size.cx = w;
3658 surface->ds_current_size.cy = h;
3659 surface->locations = location;
3662 /* Context activation is done by the caller. */
3663 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3665 const struct wined3d_gl_info *gl_info = context->gl_info;
3666 struct wined3d_device *device = surface->resource.device;
3667 GLsizei w, h;
3669 TRACE("surface %p, new location %#x.\n", surface, location);
3671 /* TODO: Make this work for modes other than FBO */
3672 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
3674 if (!(surface->locations & location))
3676 w = surface->ds_current_size.cx;
3677 h = surface->ds_current_size.cy;
3678 surface->ds_current_size.cx = 0;
3679 surface->ds_current_size.cy = 0;
3681 else
3683 w = surface->resource.width;
3684 h = surface->resource.height;
3687 if (surface->ds_current_size.cx == surface->resource.width
3688 && surface->ds_current_size.cy == surface->resource.height)
3690 TRACE("Location (%#x) is already up to date.\n", location);
3691 return;
3694 if (surface->current_renderbuffer)
3696 FIXME("Not supported with fixed up depth stencil.\n");
3697 return;
3700 if (surface->locations & WINED3D_LOCATION_DISCARDED)
3702 TRACE("Surface was discarded, no need copy data.\n");
3703 switch (location)
3705 case WINED3D_LOCATION_TEXTURE_RGB:
3706 wined3d_texture_prepare_texture(surface->container, context, FALSE);
3707 break;
3708 case WINED3D_LOCATION_RB_MULTISAMPLE:
3709 surface_prepare_rb(surface, gl_info, TRUE);
3710 break;
3711 case WINED3D_LOCATION_RB_RESOLVED:
3712 surface_prepare_rb(surface, gl_info, FALSE);
3713 break;
3714 case WINED3D_LOCATION_DRAWABLE:
3715 /* Nothing to do */
3716 break;
3717 default:
3718 FIXME("Unhandled location %#x\n", location);
3720 surface->locations &= ~WINED3D_LOCATION_DISCARDED;
3721 surface->locations |= location;
3722 surface->ds_current_size.cx = surface->resource.width;
3723 surface->ds_current_size.cy = surface->resource.height;
3724 return;
3727 if (!surface->locations)
3729 FIXME("No up to date depth stencil location.\n");
3730 surface->locations |= location;
3731 surface->ds_current_size.cx = surface->resource.width;
3732 surface->ds_current_size.cy = surface->resource.height;
3733 return;
3736 if (location == WINED3D_LOCATION_TEXTURE_RGB)
3738 GLint old_binding = 0;
3739 GLenum bind_target;
3741 /* The render target is allowed to be smaller than the depth/stencil
3742 * buffer, so the onscreen depth/stencil buffer is potentially smaller
3743 * than the offscreen surface. Don't overwrite the offscreen surface
3744 * with undefined data. */
3745 w = min(w, context->swapchain->desc.backbuffer_width);
3746 h = min(h, context->swapchain->desc.backbuffer_height);
3748 TRACE("Copying onscreen depth buffer to depth texture.\n");
3750 if (!device->depth_blt_texture)
3751 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
3753 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
3754 * directly on the FBO texture. That's because we need to flip. */
3755 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3756 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3757 NULL, WINED3D_LOCATION_DRAWABLE);
3758 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
3760 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
3761 bind_target = GL_TEXTURE_RECTANGLE_ARB;
3763 else
3765 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
3766 bind_target = GL_TEXTURE_2D;
3768 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
3769 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
3770 * internal format, because the internal format might include stencil
3771 * data. In principle we should copy stencil data as well, but unless
3772 * the driver supports stencil export it's hard to do, and doesn't
3773 * seem to be needed in practice. If the hardware doesn't support
3774 * writing stencil data, the glCopyTexImage2D() call might trigger
3775 * software fallbacks. */
3776 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
3777 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3778 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3779 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3780 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3781 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
3782 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
3783 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
3785 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3786 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
3787 context_set_draw_buffer(context, GL_NONE);
3789 /* Do the actual blit */
3790 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
3791 checkGLcall("depth_blt");
3793 context_invalidate_state(context, STATE_FRAMEBUFFER);
3795 if (wined3d_settings.strict_draw_ordering)
3796 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3798 else if (location == WINED3D_LOCATION_DRAWABLE)
3800 TRACE("Copying depth texture to onscreen depth buffer.\n");
3802 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3803 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3804 NULL, WINED3D_LOCATION_DRAWABLE);
3805 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
3806 0, surface->pow2Height - h, w, h, surface->texture_target);
3807 checkGLcall("depth_blt");
3809 context_invalidate_state(context, STATE_FRAMEBUFFER);
3811 if (wined3d_settings.strict_draw_ordering)
3812 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3814 else
3816 ERR("Invalid location (%#x) specified.\n", location);
3819 surface->locations |= location;
3820 surface->ds_current_size.cx = surface->resource.width;
3821 surface->ds_current_size.cy = surface->resource.height;
3824 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
3826 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3828 surface->locations |= location;
3831 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
3833 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3835 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3836 wined3d_texture_set_dirty(surface->container);
3837 surface->locations &= ~location;
3839 if (!surface->locations)
3840 ERR("Surface %p does not have any up to date location.\n", surface);
3843 static DWORD resource_access_from_location(DWORD location)
3845 switch (location)
3847 case WINED3D_LOCATION_SYSMEM:
3848 case WINED3D_LOCATION_USER_MEMORY:
3849 case WINED3D_LOCATION_DIB:
3850 case WINED3D_LOCATION_BUFFER:
3851 return WINED3D_RESOURCE_ACCESS_CPU;
3853 case WINED3D_LOCATION_DRAWABLE:
3854 case WINED3D_LOCATION_TEXTURE_SRGB:
3855 case WINED3D_LOCATION_TEXTURE_RGB:
3856 case WINED3D_LOCATION_RB_MULTISAMPLE:
3857 case WINED3D_LOCATION_RB_RESOLVED:
3858 return WINED3D_RESOURCE_ACCESS_GPU;
3860 default:
3861 FIXME("Unhandled location %#x.\n", location);
3862 return 0;
3866 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
3868 struct wined3d_device *device = surface->resource.device;
3869 struct wined3d_context *context;
3870 const struct wined3d_gl_info *gl_info;
3871 struct wined3d_bo_address dst, src;
3872 UINT size = surface->resource.size;
3874 surface_get_memory(surface, &dst, location);
3875 surface_get_memory(surface, &src, surface->locations);
3877 if (dst.buffer_object)
3879 context = context_acquire(device, NULL);
3880 gl_info = context->gl_info;
3881 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, dst.buffer_object));
3882 GL_EXTCALL(glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, src.addr));
3883 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
3884 checkGLcall("Upload PBO");
3885 context_release(context);
3886 return;
3888 if (src.buffer_object)
3890 context = context_acquire(device, NULL);
3891 gl_info = context->gl_info;
3892 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, src.buffer_object));
3893 GL_EXTCALL(glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, size, dst.addr));
3894 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
3895 checkGLcall("Download PBO");
3896 context_release(context);
3897 return;
3899 memcpy(dst.addr, src.addr, size);
3902 static void surface_load_sysmem(struct wined3d_surface *surface,
3903 const struct wined3d_gl_info *gl_info, DWORD dst_location)
3905 if (surface->locations & surface_simple_locations)
3907 surface_copy_simple_location(surface, dst_location);
3908 return;
3911 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
3912 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
3914 /* Download the surface to system memory. */
3915 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3917 struct wined3d_device *device = surface->resource.device;
3918 struct wined3d_context *context;
3920 /* TODO: Use already acquired context when possible. */
3921 context = context_acquire(device, NULL);
3923 wined3d_texture_bind_and_dirtify(surface->container, context,
3924 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
3925 surface_download_data(surface, gl_info, dst_location);
3927 context_release(context);
3929 return;
3932 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
3934 read_from_framebuffer(surface, dst_location);
3935 return;
3938 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
3939 surface, wined3d_debug_location(surface->locations));
3942 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
3943 const struct wined3d_gl_info *gl_info)
3945 RECT r;
3947 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
3948 && wined3d_resource_is_offscreen(&surface->container->resource))
3950 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
3951 return WINED3DERR_INVALIDCALL;
3954 surface_get_rect(surface, NULL, &r);
3955 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
3956 surface_blt_to_drawable(surface->resource.device,
3957 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
3959 return WINED3D_OK;
3962 static HRESULT surface_load_texture(struct wined3d_surface *surface,
3963 const struct wined3d_gl_info *gl_info, BOOL srgb)
3965 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
3966 struct wined3d_device *device = surface->resource.device;
3967 const struct wined3d_color_key_conversion *conversion;
3968 struct wined3d_texture *texture = surface->container;
3969 struct wined3d_context *context;
3970 UINT width, src_pitch, dst_pitch;
3971 struct wined3d_bo_address data;
3972 struct wined3d_format format;
3973 POINT dst_point = {0, 0};
3974 BYTE *mem = NULL;
3976 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
3977 && wined3d_resource_is_offscreen(&texture->resource)
3978 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
3980 surface_load_fb_texture(surface, srgb);
3982 return WINED3D_OK;
3985 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
3986 && (surface->container->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
3987 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3988 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3989 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3991 if (srgb)
3992 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
3993 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
3994 else
3995 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
3996 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
3998 return WINED3D_OK;
4001 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
4002 && (!srgb || (surface->container->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
4003 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
4004 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
4005 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
4007 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
4008 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
4009 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
4010 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4012 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
4013 &rect, surface, dst_location, &rect);
4015 return WINED3D_OK;
4018 /* Upload from system memory */
4020 if (srgb)
4022 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
4023 == WINED3D_LOCATION_TEXTURE_RGB)
4025 /* Performance warning... */
4026 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
4027 surface_prepare_map_memory(surface);
4028 surface_load_location(surface, surface->resource.map_binding);
4031 else
4033 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
4034 == WINED3D_LOCATION_TEXTURE_SRGB)
4036 /* Performance warning... */
4037 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
4038 surface_prepare_map_memory(surface);
4039 surface_load_location(surface, surface->resource.map_binding);
4043 if (!(surface->locations & surface_simple_locations))
4045 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
4046 /* Lets hope we get it from somewhere... */
4047 surface_prepare_system_memory(surface);
4048 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
4051 /* TODO: Use already acquired context when possible. */
4052 context = context_acquire(device, NULL);
4054 wined3d_texture_prepare_texture(texture, context, srgb);
4055 wined3d_texture_bind_and_dirtify(texture, context, srgb);
4057 width = surface->resource.width;
4058 src_pitch = wined3d_surface_get_pitch(surface);
4060 format = *texture->resource.format;
4061 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
4062 format = *wined3d_get_format(gl_info, conversion->dst_format);
4064 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
4065 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
4066 * getting called. */
4067 if ((format.convert || conversion) && surface->pbo)
4069 TRACE("Removing the pbo attached to surface %p.\n", surface);
4071 if (surface->flags & SFLAG_DIBSECTION)
4072 surface->resource.map_binding = WINED3D_LOCATION_DIB;
4073 else
4074 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
4076 surface_prepare_map_memory(surface);
4077 surface_load_location(surface, surface->resource.map_binding);
4078 surface_remove_pbo(surface, gl_info);
4081 surface_get_memory(surface, &data, surface->locations);
4082 if (format.convert)
4084 /* This code is entered for texture formats which need a fixup. */
4085 UINT height = surface->resource.height;
4087 format.byte_count = format.conv_byte_count;
4088 dst_pitch = wined3d_format_calculate_pitch(&format, width);
4090 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4092 ERR("Out of memory (%u).\n", dst_pitch * height);
4093 context_release(context);
4094 return E_OUTOFMEMORY;
4096 format.convert(data.addr, mem, src_pitch, src_pitch * height,
4097 dst_pitch, dst_pitch * height, width, height, 1);
4098 src_pitch = dst_pitch;
4099 data.addr = mem;
4101 else if (conversion)
4103 /* This code is only entered for color keying fixups */
4104 struct wined3d_palette *palette = NULL;
4105 UINT height = surface->resource.height;
4107 dst_pitch = wined3d_format_calculate_pitch(&format, width);
4108 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4110 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4112 ERR("Out of memory (%u).\n", dst_pitch * height);
4113 context_release(context);
4114 return E_OUTOFMEMORY;
4116 if (texture->swapchain && texture->swapchain->palette)
4117 palette = texture->swapchain->palette;
4118 conversion->convert(data.addr, src_pitch, mem, dst_pitch,
4119 width, height, palette, &texture->async.gl_color_key);
4120 src_pitch = dst_pitch;
4121 data.addr = mem;
4124 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
4125 src_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
4127 context_release(context);
4129 HeapFree(GetProcessHeap(), 0, mem);
4131 return WINED3D_OK;
4134 static void surface_multisample_resolve(struct wined3d_surface *surface)
4136 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4138 if (!(surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE))
4139 ERR("Trying to resolve multisampled surface %p, but location WINED3D_LOCATION_RB_MULTISAMPLE not current.\n",
4140 surface);
4142 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
4143 surface, WINED3D_LOCATION_RB_MULTISAMPLE, &rect, surface, WINED3D_LOCATION_RB_RESOLVED, &rect);
4146 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location)
4148 struct wined3d_device *device = surface->resource.device;
4149 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4150 HRESULT hr;
4152 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4154 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
4156 if (location == WINED3D_LOCATION_TEXTURE_RGB
4157 && surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_DISCARDED))
4159 struct wined3d_context *context = context_acquire(device, NULL);
4160 surface_load_ds_location(surface, context, location);
4161 context_release(context);
4162 return WINED3D_OK;
4164 else if (location & surface->locations
4165 && surface->container->resource.draw_binding != WINED3D_LOCATION_DRAWABLE)
4167 /* Already up to date, nothing to do. */
4168 return WINED3D_OK;
4170 else
4172 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
4173 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
4174 return WINED3DERR_INVALIDCALL;
4178 if (surface->locations & location)
4180 TRACE("Location already up to date.\n");
4181 return WINED3D_OK;
4184 if (WARN_ON(d3d_surface))
4186 DWORD required_access = resource_access_from_location(location);
4187 if ((surface->resource.access_flags & required_access) != required_access)
4188 WARN("Operation requires %#x access, but surface only has %#x.\n",
4189 required_access, surface->resource.access_flags);
4192 if (!surface->locations)
4194 ERR("Surface %p does not have any up to date location.\n", surface);
4195 surface->flags |= SFLAG_LOST;
4196 return WINED3DERR_DEVICELOST;
4199 switch (location)
4201 case WINED3D_LOCATION_DIB:
4202 case WINED3D_LOCATION_USER_MEMORY:
4203 case WINED3D_LOCATION_SYSMEM:
4204 case WINED3D_LOCATION_BUFFER:
4205 surface_load_sysmem(surface, gl_info, location);
4206 break;
4208 case WINED3D_LOCATION_DRAWABLE:
4209 if (FAILED(hr = surface_load_drawable(surface, gl_info)))
4210 return hr;
4211 break;
4213 case WINED3D_LOCATION_RB_RESOLVED:
4214 surface_multisample_resolve(surface);
4215 break;
4217 case WINED3D_LOCATION_TEXTURE_RGB:
4218 case WINED3D_LOCATION_TEXTURE_SRGB:
4219 if (FAILED(hr = surface_load_texture(surface, gl_info, location == WINED3D_LOCATION_TEXTURE_SRGB)))
4220 return hr;
4221 break;
4223 default:
4224 ERR("Don't know how to handle location %#x.\n", location);
4225 break;
4228 surface_validate_location(surface, location);
4230 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
4231 surface_evict_sysmem(surface);
4233 return WINED3D_OK;
4236 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
4237 /* Context activation is done by the caller. */
4238 static void ffp_blit_free(struct wined3d_device *device) { }
4240 /* Context activation is done by the caller. */
4241 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
4242 const struct wined3d_color_key *color_key)
4244 const struct wined3d_gl_info *gl_info = context->gl_info;
4246 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
4247 checkGLcall("glEnable(target)");
4249 return WINED3D_OK;
4252 /* Context activation is done by the caller. */
4253 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
4255 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
4256 checkGLcall("glDisable(GL_TEXTURE_2D)");
4257 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
4259 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4260 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4262 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
4264 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
4265 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4269 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info,
4270 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
4271 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4272 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4274 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
4276 TRACE("Source or destination is in system memory.\n");
4277 return FALSE;
4280 switch (blit_op)
4282 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
4283 if (d3d_info->shader_color_key)
4285 TRACE("Color keying requires converted textures.\n");
4286 return FALSE;
4288 case WINED3D_BLIT_OP_COLOR_BLIT:
4289 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4291 TRACE("Checking support for fixup:\n");
4292 dump_color_fixup_desc(src_format->color_fixup);
4295 /* We only support identity conversions. */
4296 if (!is_identity_fixup(src_format->color_fixup)
4297 || !is_identity_fixup(dst_format->color_fixup))
4299 TRACE("Fixups are not supported.\n");
4300 return FALSE;
4303 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4305 TRACE("Can only blit to render targets.\n");
4306 return FALSE;
4308 return TRUE;
4310 case WINED3D_BLIT_OP_COLOR_FILL:
4311 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
4313 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
4314 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
4315 return FALSE;
4317 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4319 TRACE("Color fill not supported\n");
4320 return FALSE;
4323 /* FIXME: We should reject color fills on formats with fixups,
4324 * but this would break P8 color fills for example. */
4326 return TRUE;
4328 case WINED3D_BLIT_OP_DEPTH_FILL:
4329 return TRUE;
4331 default:
4332 TRACE("Unsupported blit_op=%d\n", blit_op);
4333 return FALSE;
4337 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4338 const RECT *dst_rect, const struct wined3d_color *color)
4340 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4341 struct wined3d_rendertarget_view *view;
4342 struct wined3d_fb_state fb = {&view, NULL};
4343 HRESULT hr;
4345 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4346 NULL, &wined3d_null_parent_ops, &view)))
4348 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4349 return hr;
4352 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
4353 wined3d_rendertarget_view_decref(view);
4355 return WINED3D_OK;
4358 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4359 const RECT *dst_rect, float depth)
4361 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4362 struct wined3d_fb_state fb = {NULL, NULL};
4363 HRESULT hr;
4365 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4366 NULL, &wined3d_null_parent_ops, &fb.depth_stencil)))
4368 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4369 return hr;
4372 device_clear_render_targets(device, 0, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
4373 wined3d_rendertarget_view_decref(fb.depth_stencil);
4375 return WINED3D_OK;
4378 static void ffp_blit_blit_surface(struct wined3d_device *device, DWORD filter,
4379 struct wined3d_surface *src_surface, const RECT *src_rect,
4380 struct wined3d_surface *dst_surface, const RECT *dst_rect,
4381 const struct wined3d_color_key *color_key)
4383 /* Blit from offscreen surface to render target */
4384 struct wined3d_color_key old_blt_key = src_surface->container->async.src_blt_color_key;
4385 DWORD old_color_key_flags = src_surface->container->async.color_key_flags;
4387 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4389 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT, color_key);
4391 surface_blt_to_drawable(device, filter,
4392 !!color_key, src_surface, src_rect, dst_surface, dst_rect);
4394 /* Restore the color key parameters */
4395 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT,
4396 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
4398 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
4399 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
4402 const struct blit_shader ffp_blit = {
4403 ffp_blit_alloc,
4404 ffp_blit_free,
4405 ffp_blit_set,
4406 ffp_blit_unset,
4407 ffp_blit_supported,
4408 ffp_blit_color_fill,
4409 ffp_blit_depth_fill,
4410 ffp_blit_blit_surface,
4413 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
4415 return WINED3D_OK;
4418 /* Context activation is done by the caller. */
4419 static void cpu_blit_free(struct wined3d_device *device)
4423 /* Context activation is done by the caller. */
4424 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
4425 const struct wined3d_color_key *color_key)
4427 return WINED3D_OK;
4430 /* Context activation is done by the caller. */
4431 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
4435 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info,
4436 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
4437 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4438 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4440 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
4442 return TRUE;
4445 return FALSE;
4448 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
4449 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
4450 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
4452 UINT row_block_count;
4453 const BYTE *src_row;
4454 BYTE *dst_row;
4455 UINT x, y;
4457 src_row = src_data;
4458 dst_row = dst_data;
4460 row_block_count = (update_w + format->block_width - 1) / format->block_width;
4462 if (!flags)
4464 for (y = 0; y < update_h; y += format->block_height)
4466 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
4467 src_row += src_pitch;
4468 dst_row += dst_pitch;
4471 return WINED3D_OK;
4474 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
4476 src_row += (((update_h / format->block_height) - 1) * src_pitch);
4478 switch (format->id)
4480 case WINED3DFMT_DXT1:
4481 for (y = 0; y < update_h; y += format->block_height)
4483 struct block
4485 WORD color[2];
4486 BYTE control_row[4];
4489 const struct block *s = (const struct block *)src_row;
4490 struct block *d = (struct block *)dst_row;
4492 for (x = 0; x < row_block_count; ++x)
4494 d[x].color[0] = s[x].color[0];
4495 d[x].color[1] = s[x].color[1];
4496 d[x].control_row[0] = s[x].control_row[3];
4497 d[x].control_row[1] = s[x].control_row[2];
4498 d[x].control_row[2] = s[x].control_row[1];
4499 d[x].control_row[3] = s[x].control_row[0];
4501 src_row -= src_pitch;
4502 dst_row += dst_pitch;
4504 return WINED3D_OK;
4506 case WINED3DFMT_DXT2:
4507 case WINED3DFMT_DXT3:
4508 for (y = 0; y < update_h; y += format->block_height)
4510 struct block
4512 WORD alpha_row[4];
4513 WORD color[2];
4514 BYTE control_row[4];
4517 const struct block *s = (const struct block *)src_row;
4518 struct block *d = (struct block *)dst_row;
4520 for (x = 0; x < row_block_count; ++x)
4522 d[x].alpha_row[0] = s[x].alpha_row[3];
4523 d[x].alpha_row[1] = s[x].alpha_row[2];
4524 d[x].alpha_row[2] = s[x].alpha_row[1];
4525 d[x].alpha_row[3] = s[x].alpha_row[0];
4526 d[x].color[0] = s[x].color[0];
4527 d[x].color[1] = s[x].color[1];
4528 d[x].control_row[0] = s[x].control_row[3];
4529 d[x].control_row[1] = s[x].control_row[2];
4530 d[x].control_row[2] = s[x].control_row[1];
4531 d[x].control_row[3] = s[x].control_row[0];
4533 src_row -= src_pitch;
4534 dst_row += dst_pitch;
4536 return WINED3D_OK;
4538 default:
4539 FIXME("Compressed flip not implemented for format %s.\n",
4540 debug_d3dformat(format->id));
4541 return E_NOTIMPL;
4545 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
4546 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
4548 return E_NOTIMPL;
4551 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4552 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
4553 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
4555 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
4556 const struct wined3d_format *src_format, *dst_format;
4557 unsigned int src_fmt_flags, dst_fmt_flags;
4558 struct wined3d_texture *src_texture = NULL;
4559 struct wined3d_map_desc dst_map, src_map;
4560 const BYTE *sbase = NULL;
4561 HRESULT hr = WINED3D_OK;
4562 const BYTE *sbuf;
4563 BYTE *dbuf;
4564 int x, y;
4566 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
4567 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4568 flags, fx, debug_d3dtexturefiltertype(filter));
4570 if (src_surface == dst_surface)
4572 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
4573 src_map = dst_map;
4574 src_format = dst_surface->resource.format;
4575 dst_format = src_format;
4576 dst_fmt_flags = dst_surface->container->resource.format_flags;
4577 src_fmt_flags = dst_fmt_flags;
4579 else
4581 dst_format = dst_surface->resource.format;
4582 dst_fmt_flags = dst_surface->container->resource.format_flags;
4583 if (src_surface)
4585 if (dst_surface->resource.format->id != src_surface->resource.format->id)
4587 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
4589 /* The conv function writes a FIXME */
4590 WARN("Cannot convert source surface format to dest format.\n");
4591 goto release;
4593 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
4595 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
4596 src_format = src_surface->resource.format;
4597 src_fmt_flags = src_surface->container->resource.format_flags;
4599 else
4601 src_format = dst_format;
4602 src_fmt_flags = dst_fmt_flags;
4605 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
4608 bpp = dst_surface->resource.format->byte_count;
4609 srcheight = src_rect->bottom - src_rect->top;
4610 srcwidth = src_rect->right - src_rect->left;
4611 dstheight = dst_rect->bottom - dst_rect->top;
4612 dstwidth = dst_rect->right - dst_rect->left;
4613 width = (dst_rect->right - dst_rect->left) * bpp;
4615 if (src_surface)
4616 sbase = (BYTE *)src_map.data
4617 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
4618 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
4619 if (src_surface != dst_surface)
4620 dbuf = dst_map.data;
4621 else
4622 dbuf = (BYTE *)dst_map.data
4623 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
4624 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
4626 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
4628 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
4630 if (src_surface == dst_surface)
4632 FIXME("Only plain blits supported on compressed surfaces.\n");
4633 hr = E_NOTIMPL;
4634 goto release;
4637 if (srcheight != dstheight || srcwidth != dstwidth)
4639 WARN("Stretching not supported on compressed surfaces.\n");
4640 hr = WINED3DERR_INVALIDCALL;
4641 goto release;
4644 if (!surface_check_block_align(src_surface, src_rect))
4646 WARN("Source rectangle not block-aligned.\n");
4647 hr = WINED3DERR_INVALIDCALL;
4648 goto release;
4651 if (!surface_check_block_align(dst_surface, dst_rect))
4653 WARN("Destination rectangle not block-aligned.\n");
4654 hr = WINED3DERR_INVALIDCALL;
4655 goto release;
4658 hr = surface_cpu_blt_compressed(sbase, dbuf,
4659 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
4660 src_format, flags, fx);
4661 goto release;
4664 /* First, all the 'source-less' blits */
4665 if (flags & WINEDDBLT_COLORFILL)
4667 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
4668 flags &= ~WINEDDBLT_COLORFILL;
4671 if (flags & WINEDDBLT_DEPTHFILL)
4673 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
4675 if (flags & WINEDDBLT_DDROPS)
4677 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
4679 /* Now the 'with source' blits. */
4680 if (src_surface)
4682 int sx, xinc, sy, yinc;
4684 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
4685 goto release;
4687 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
4688 && (srcwidth != dstwidth || srcheight != dstheight))
4690 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
4691 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
4694 xinc = (srcwidth << 16) / dstwidth;
4695 yinc = (srcheight << 16) / dstheight;
4697 if (!flags)
4699 /* No effects, we can cheat here. */
4700 if (dstwidth == srcwidth)
4702 if (dstheight == srcheight)
4704 /* No stretching in either direction. This needs to be as
4705 * fast as possible. */
4706 sbuf = sbase;
4708 /* Check for overlapping surfaces. */
4709 if (src_surface != dst_surface || dst_rect->top < src_rect->top
4710 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
4712 /* No overlap, or dst above src, so copy from top downwards. */
4713 for (y = 0; y < dstheight; ++y)
4715 memcpy(dbuf, sbuf, width);
4716 sbuf += src_map.row_pitch;
4717 dbuf += dst_map.row_pitch;
4720 else if (dst_rect->top > src_rect->top)
4722 /* Copy from bottom upwards. */
4723 sbuf += src_map.row_pitch * dstheight;
4724 dbuf += dst_map.row_pitch * dstheight;
4725 for (y = 0; y < dstheight; ++y)
4727 sbuf -= src_map.row_pitch;
4728 dbuf -= dst_map.row_pitch;
4729 memcpy(dbuf, sbuf, width);
4732 else
4734 /* Src and dst overlapping on the same line, use memmove. */
4735 for (y = 0; y < dstheight; ++y)
4737 memmove(dbuf, sbuf, width);
4738 sbuf += src_map.row_pitch;
4739 dbuf += dst_map.row_pitch;
4743 else
4745 /* Stretching in y direction only. */
4746 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4748 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4749 memcpy(dbuf, sbuf, width);
4750 dbuf += dst_map.row_pitch;
4754 else
4756 /* Stretching in X direction. */
4757 int last_sy = -1;
4758 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4760 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4762 if ((sy >> 16) == (last_sy >> 16))
4764 /* This source row is the same as last source row -
4765 * Copy the already stretched row. */
4766 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
4768 else
4770 #define STRETCH_ROW(type) \
4771 do { \
4772 const type *s = (const type *)sbuf; \
4773 type *d = (type *)dbuf; \
4774 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4775 d[x] = s[sx >> 16]; \
4776 } while(0)
4778 switch(bpp)
4780 case 1:
4781 STRETCH_ROW(BYTE);
4782 break;
4783 case 2:
4784 STRETCH_ROW(WORD);
4785 break;
4786 case 4:
4787 STRETCH_ROW(DWORD);
4788 break;
4789 case 3:
4791 const BYTE *s;
4792 BYTE *d = dbuf;
4793 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
4795 DWORD pixel;
4797 s = sbuf + 3 * (sx >> 16);
4798 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4799 d[0] = (pixel ) & 0xff;
4800 d[1] = (pixel >> 8) & 0xff;
4801 d[2] = (pixel >> 16) & 0xff;
4802 d += 3;
4804 break;
4806 default:
4807 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
4808 hr = WINED3DERR_NOTAVAILABLE;
4809 goto error;
4811 #undef STRETCH_ROW
4813 dbuf += dst_map.row_pitch;
4814 last_sy = sy;
4818 else
4820 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
4821 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
4822 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
4823 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
4825 /* The color keying flags are checked for correctness in ddraw */
4826 if (flags & WINEDDBLT_KEYSRC)
4828 keylow = src_surface->container->async.src_blt_color_key.color_space_low_value;
4829 keyhigh = src_surface->container->async.src_blt_color_key.color_space_high_value;
4831 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4833 keylow = fx->ddckSrcColorkey.color_space_low_value;
4834 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
4837 if (flags & WINEDDBLT_KEYDEST)
4839 /* Destination color keys are taken from the source surface! */
4840 destkeylow = src_surface->container->async.dst_blt_color_key.color_space_low_value;
4841 destkeyhigh = src_surface->container->async.dst_blt_color_key.color_space_high_value;
4843 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
4845 destkeylow = fx->ddckDestColorkey.color_space_low_value;
4846 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
4849 if (bpp == 1)
4851 keymask = 0xff;
4853 else
4855 DWORD masks[3];
4856 get_color_masks(src_format, masks);
4857 keymask = masks[0]
4858 | masks[1]
4859 | masks[2];
4861 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
4864 if (flags & WINEDDBLT_DDFX)
4866 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
4867 LONG tmpxy;
4868 dTopLeft = dbuf;
4869 dTopRight = dbuf + ((dstwidth - 1) * bpp);
4870 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
4871 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
4873 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
4875 /* I don't think we need to do anything about this flag */
4876 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
4878 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
4880 tmp = dTopRight;
4881 dTopRight = dTopLeft;
4882 dTopLeft = tmp;
4883 tmp = dBottomRight;
4884 dBottomRight = dBottomLeft;
4885 dBottomLeft = tmp;
4886 dstxinc = dstxinc * -1;
4888 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
4890 tmp = dTopLeft;
4891 dTopLeft = dBottomLeft;
4892 dBottomLeft = tmp;
4893 tmp = dTopRight;
4894 dTopRight = dBottomRight;
4895 dBottomRight = tmp;
4896 dstyinc = dstyinc * -1;
4898 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
4900 /* I don't think we need to do anything about this flag */
4901 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
4903 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
4905 tmp = dBottomRight;
4906 dBottomRight = dTopLeft;
4907 dTopLeft = tmp;
4908 tmp = dBottomLeft;
4909 dBottomLeft = dTopRight;
4910 dTopRight = tmp;
4911 dstxinc = dstxinc * -1;
4912 dstyinc = dstyinc * -1;
4914 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
4916 tmp = dTopLeft;
4917 dTopLeft = dBottomLeft;
4918 dBottomLeft = dBottomRight;
4919 dBottomRight = dTopRight;
4920 dTopRight = tmp;
4921 tmpxy = dstxinc;
4922 dstxinc = dstyinc;
4923 dstyinc = tmpxy;
4924 dstxinc = dstxinc * -1;
4926 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
4928 tmp = dTopLeft;
4929 dTopLeft = dTopRight;
4930 dTopRight = dBottomRight;
4931 dBottomRight = dBottomLeft;
4932 dBottomLeft = tmp;
4933 tmpxy = dstxinc;
4934 dstxinc = dstyinc;
4935 dstyinc = tmpxy;
4936 dstyinc = dstyinc * -1;
4938 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
4940 /* I don't think we need to do anything about this flag */
4941 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
4943 dbuf = dTopLeft;
4944 flags &= ~(WINEDDBLT_DDFX);
4947 #define COPY_COLORKEY_FX(type) \
4948 do { \
4949 const type *s; \
4950 type *d = (type *)dbuf, *dx, tmp; \
4951 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
4953 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
4954 dx = d; \
4955 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4957 tmp = s[sx >> 16]; \
4958 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
4959 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
4961 dx[0] = tmp; \
4963 dx = (type *)(((BYTE *)dx) + dstxinc); \
4965 d = (type *)(((BYTE *)d) + dstyinc); \
4967 } while(0)
4969 switch (bpp)
4971 case 1:
4972 COPY_COLORKEY_FX(BYTE);
4973 break;
4974 case 2:
4975 COPY_COLORKEY_FX(WORD);
4976 break;
4977 case 4:
4978 COPY_COLORKEY_FX(DWORD);
4979 break;
4980 case 3:
4982 const BYTE *s;
4983 BYTE *d = dbuf, *dx;
4984 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4986 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4987 dx = d;
4988 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
4990 DWORD pixel, dpixel = 0;
4991 s = sbuf + 3 * (sx>>16);
4992 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4993 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
4994 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
4995 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
4997 dx[0] = (pixel ) & 0xff;
4998 dx[1] = (pixel >> 8) & 0xff;
4999 dx[2] = (pixel >> 16) & 0xff;
5001 dx += dstxinc;
5003 d += dstyinc;
5005 break;
5007 default:
5008 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
5009 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
5010 hr = WINED3DERR_NOTAVAILABLE;
5011 goto error;
5012 #undef COPY_COLORKEY_FX
5017 error:
5018 if (flags && FIXME_ON(d3d_surface))
5020 FIXME("\tUnsupported flags: %#x.\n", flags);
5023 release:
5024 wined3d_surface_unmap(dst_surface);
5025 if (src_surface && src_surface != dst_surface)
5026 wined3d_surface_unmap(src_surface);
5027 /* Release the converted surface, if any. */
5028 if (src_texture)
5029 wined3d_texture_decref(src_texture);
5031 return hr;
5034 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
5035 const RECT *dst_rect, const struct wined3d_color *color)
5037 static const RECT src_rect;
5038 WINEDDBLTFX BltFx;
5040 memset(&BltFx, 0, sizeof(BltFx));
5041 BltFx.dwSize = sizeof(BltFx);
5042 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
5043 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
5044 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
5047 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
5048 struct wined3d_surface *surface, const RECT *rect, float depth)
5050 FIXME("Depth filling not implemented by cpu_blit.\n");
5051 return WINED3DERR_INVALIDCALL;
5054 static void cpu_blit_blit_surface(struct wined3d_device *device, DWORD filter,
5055 struct wined3d_surface *src_surface, const RECT *src_rect,
5056 struct wined3d_surface *dst_surface, const RECT *dst_rect,
5057 const struct wined3d_color_key *color_key)
5059 /* FIXME: Remove error returns from surface_blt_cpu. */
5060 ERR("Blit method not implemented by cpu_blit.\n");
5063 const struct blit_shader cpu_blit = {
5064 cpu_blit_alloc,
5065 cpu_blit_free,
5066 cpu_blit_set,
5067 cpu_blit_unset,
5068 cpu_blit_supported,
5069 cpu_blit_color_fill,
5070 cpu_blit_depth_fill,
5071 cpu_blit_blit_surface,
5074 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
5075 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
5076 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
5078 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
5079 struct wined3d_device *device = dst_surface->resource.device;
5080 DWORD src_ds_flags, dst_ds_flags;
5081 RECT src_rect, dst_rect;
5082 BOOL scale, convert;
5084 static const DWORD simple_blit = WINEDDBLT_ASYNC
5085 | WINEDDBLT_COLORFILL
5086 | WINEDDBLT_KEYSRC
5087 | WINEDDBLT_KEYSRCOVERRIDE
5088 | WINEDDBLT_WAIT
5089 | WINEDDBLT_DEPTHFILL
5090 | WINEDDBLT_DONOTWAIT;
5092 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
5093 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
5094 flags, fx, debug_d3dtexturefiltertype(filter));
5095 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
5097 if (fx)
5099 TRACE("dwSize %#x.\n", fx->dwSize);
5100 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
5101 TRACE("dwROP %#x.\n", fx->dwROP);
5102 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
5103 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
5104 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
5105 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
5106 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
5107 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
5108 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
5109 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
5110 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
5111 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
5112 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
5113 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
5114 TRACE("dwReserved %#x.\n", fx->dwReserved);
5115 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
5116 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
5117 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
5118 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
5119 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
5120 TRACE("ddckDestColorkey {%#x, %#x}.\n",
5121 fx->ddckDestColorkey.color_space_low_value,
5122 fx->ddckDestColorkey.color_space_high_value);
5123 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
5124 fx->ddckSrcColorkey.color_space_low_value,
5125 fx->ddckSrcColorkey.color_space_high_value);
5128 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
5130 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
5131 return WINEDDERR_SURFACEBUSY;
5134 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
5136 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
5137 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
5138 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
5139 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
5140 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
5142 WARN("The application gave us a bad destination rectangle.\n");
5143 return WINEDDERR_INVALIDRECT;
5146 if (src_surface)
5148 surface_get_rect(src_surface, src_rect_in, &src_rect);
5150 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
5151 || src_rect.left > src_surface->resource.width || src_rect.left < 0
5152 || src_rect.top > src_surface->resource.height || src_rect.top < 0
5153 || src_rect.right > src_surface->resource.width || src_rect.right < 0
5154 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
5156 WARN("Application gave us bad source rectangle for Blt.\n");
5157 return WINEDDERR_INVALIDRECT;
5160 else
5162 memset(&src_rect, 0, sizeof(src_rect));
5165 if (!fx || !(fx->dwDDFX))
5166 flags &= ~WINEDDBLT_DDFX;
5168 if (flags & WINEDDBLT_WAIT)
5169 flags &= ~WINEDDBLT_WAIT;
5171 if (flags & WINEDDBLT_ASYNC)
5173 static unsigned int once;
5175 if (!once++)
5176 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
5177 flags &= ~WINEDDBLT_ASYNC;
5180 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
5181 if (flags & WINEDDBLT_DONOTWAIT)
5183 static unsigned int once;
5185 if (!once++)
5186 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
5187 flags &= ~WINEDDBLT_DONOTWAIT;
5190 if (!device->d3d_initialized)
5192 WARN("D3D not initialized, using fallback.\n");
5193 goto cpu;
5196 /* We want to avoid invalidating the sysmem location for converted
5197 * surfaces, since otherwise we'd have to convert the data back when
5198 * locking them. */
5199 if (dst_surface->container->flags & WINED3D_TEXTURE_CONVERTED
5200 || dst_surface->container->resource.format->convert
5201 || wined3d_format_get_color_key_conversion(dst_surface->container, TRUE))
5203 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
5204 goto cpu;
5207 if (flags & ~simple_blit)
5209 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
5210 goto fallback;
5213 if (src_surface)
5214 src_swapchain = src_surface->container->swapchain;
5215 else
5216 src_swapchain = NULL;
5218 dst_swapchain = dst_surface->container->swapchain;
5220 /* This isn't strictly needed. FBO blits for example could deal with
5221 * cross-swapchain blits by first downloading the source to a texture
5222 * before switching to the destination context. We just have this here to
5223 * not have to deal with the issue, since cross-swapchain blits should be
5224 * rare. */
5225 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
5227 FIXME("Using fallback for cross-swapchain blit.\n");
5228 goto fallback;
5231 scale = src_surface
5232 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
5233 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
5234 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
5236 dst_ds_flags = dst_surface->container->resource.format_flags
5237 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5238 if (src_surface)
5239 src_ds_flags = src_surface->container->resource.format_flags
5240 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5241 else
5242 src_ds_flags = 0;
5244 if (src_ds_flags || dst_ds_flags)
5246 if (flags & WINEDDBLT_DEPTHFILL)
5248 float depth;
5250 TRACE("Depth fill.\n");
5252 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
5253 return WINED3DERR_INVALIDCALL;
5255 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
5256 return WINED3D_OK;
5258 else
5260 if (src_ds_flags != dst_ds_flags)
5262 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
5263 return WINED3DERR_INVALIDCALL;
5266 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->container->resource.draw_binding,
5267 &src_rect, dst_surface, dst_surface->container->resource.draw_binding, &dst_rect)))
5268 return WINED3D_OK;
5271 else
5273 const struct blit_shader *blitter;
5275 /* In principle this would apply to depth blits as well, but we don't
5276 * implement those in the CPU blitter at the moment. */
5277 if ((dst_surface->locations & dst_surface->resource.map_binding)
5278 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
5280 if (scale)
5281 TRACE("Not doing sysmem blit because of scaling.\n");
5282 else if (convert)
5283 TRACE("Not doing sysmem blit because of format conversion.\n");
5284 else
5285 goto cpu;
5288 if (flags & WINEDDBLT_COLORFILL)
5290 struct wined3d_color color;
5291 const struct wined3d_palette *palette = dst_swapchain ? dst_swapchain->palette : NULL;
5293 TRACE("Color fill.\n");
5295 if (!wined3d_format_convert_color_to_float(dst_surface->resource.format,
5296 palette, fx->u5.dwFillColor, &color))
5297 goto fallback;
5299 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
5300 return WINED3D_OK;
5302 else
5304 enum wined3d_blit_op blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
5305 const struct wined3d_color_key *color_key = NULL;
5307 TRACE("Color blit.\n");
5308 if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5310 color_key = &fx->ddckSrcColorkey;
5311 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
5313 else if (flags & WINEDDBLT_KEYSRC)
5315 color_key = &src_surface->container->async.src_blt_color_key;
5316 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
5318 else if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
5319 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
5321 /* Upload */
5322 if (scale)
5323 TRACE("Not doing upload because of scaling.\n");
5324 else if (convert)
5325 TRACE("Not doing upload because of format conversion.\n");
5326 else
5328 POINT dst_point = {dst_rect.left, dst_rect.top};
5330 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
5332 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
5333 surface_load_location(dst_surface, dst_surface->container->resource.draw_binding);
5334 return WINED3D_OK;
5338 else if (dst_swapchain && dst_swapchain->back_buffers
5339 && dst_surface->container == dst_swapchain->front_buffer
5340 && src_surface->container == dst_swapchain->back_buffers[0])
5342 /* Use present for back -> front blits. The idea behind this is
5343 * that present is potentially faster than a blit, in particular
5344 * when FBO blits aren't available. Some ddraw applications like
5345 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
5346 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
5347 * applications can't blit directly to the frontbuffer. */
5348 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
5350 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
5352 /* Set the swap effect to COPY, we don't want the backbuffer
5353 * to become undefined. */
5354 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
5355 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
5356 dst_swapchain->desc.swap_effect = swap_effect;
5358 return WINED3D_OK;
5361 if (fbo_blit_supported(&device->adapter->gl_info, blit_op,
5362 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5363 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5365 TRACE("Using FBO blit.\n");
5367 surface_blt_fbo(device, filter,
5368 src_surface, src_surface->container->resource.draw_binding, &src_rect,
5369 dst_surface, dst_surface->container->resource.draw_binding, &dst_rect);
5370 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
5371 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
5373 return WINED3D_OK;
5376 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, blit_op,
5377 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5378 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format);
5379 if (blitter)
5381 blitter->blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect, color_key);
5382 return WINED3D_OK;
5387 fallback:
5388 /* Special cases for render targets. */
5389 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
5390 return WINED3D_OK;
5392 cpu:
5394 /* For the rest call the X11 surface implementation. For render targets
5395 * this should be implemented OpenGL accelerated in surface_blt_special(),
5396 * other blits are rather rare. */
5397 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
5400 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
5401 const struct wined3d_resource_desc *desc, GLenum target, unsigned int level, unsigned int layer, DWORD flags)
5403 struct wined3d_device *device = container->resource.device;
5404 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5405 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
5406 UINT multisample_quality = desc->multisample_quality;
5407 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
5408 unsigned int resource_size;
5409 HRESULT hr;
5411 /* Quick lockable sanity check.
5412 * TODO: remove this after surfaces, usage and lockability have been debugged properly
5413 * this function is too deep to need to care about things like this.
5414 * Levels need to be checked too, since they all affect what can be done. */
5415 switch (desc->pool)
5417 case WINED3D_POOL_MANAGED:
5418 if (desc->usage & WINED3DUSAGE_DYNAMIC)
5419 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
5420 break;
5422 case WINED3D_POOL_DEFAULT:
5423 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
5424 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
5425 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
5426 break;
5428 case WINED3D_POOL_SCRATCH:
5429 case WINED3D_POOL_SYSTEM_MEM:
5430 break;
5432 default:
5433 FIXME("Unknown pool %#x.\n", desc->pool);
5434 break;
5437 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
5438 FIXME("Trying to create a render target that isn't in the default pool.\n");
5440 /* FIXME: Check that the format is supported by the device. */
5442 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
5443 if (!resource_size)
5444 return WINED3DERR_INVALIDCALL;
5446 if (device->wined3d->flags & WINED3D_NO3D)
5447 surface->surface_ops = &gdi_surface_ops;
5448 else
5449 surface->surface_ops = &surface_ops;
5451 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE,
5452 format, desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height,
5453 1, resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
5455 WARN("Failed to initialize resource, returning %#x.\n", hr);
5456 return hr;
5459 surface->container = container;
5460 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
5461 list_init(&surface->renderbuffers);
5462 list_init(&surface->overlays);
5464 /* Flags */
5465 if (flags & WINED3D_SURFACE_DISCARD)
5466 surface->flags |= SFLAG_DISCARD;
5467 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
5468 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
5470 surface->texture_target = target;
5471 surface->texture_level = level;
5472 surface->texture_layer = layer;
5474 /* Call the private setup routine */
5475 if (FAILED(hr = surface->surface_ops->surface_private_setup(surface)))
5477 ERR("Private setup failed, hr %#x.\n", hr);
5478 surface_cleanup(surface);
5479 return hr;
5482 /* Similar to lockable rendertargets above, creating the DIB section
5483 * during surface initialization prevents the sysmem pointer from changing
5484 * after a wined3d_surface_getdc() call. */
5485 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
5486 && SUCCEEDED(surface_create_dib_section(surface)))
5487 surface->resource.map_binding = WINED3D_LOCATION_DIB;
5489 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
5491 wined3d_resource_free_sysmem(&surface->resource);
5492 surface_validate_location(surface, WINED3D_LOCATION_DIB);
5493 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
5496 return hr;
5499 HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
5500 GLenum target, unsigned int level, unsigned int layer, DWORD flags, struct wined3d_surface **surface)
5502 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
5503 const struct wined3d_parent_ops *parent_ops;
5504 struct wined3d_surface *object;
5505 void *parent;
5506 HRESULT hr;
5508 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), pool %s, "
5509 "multisample_type %#x, multisample_quality %u, target %#x, level %u, layer %u, flags %#x, surface %p.\n",
5510 container, desc->width, desc->height, debug_d3dformat(desc->format),
5511 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
5512 desc->multisample_type, desc->multisample_quality, target, level, layer, flags, surface);
5514 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
5515 return E_OUTOFMEMORY;
5517 if (FAILED(hr = surface_init(object, container, desc, target, level, layer, flags)))
5519 WARN("Failed to initialize surface, returning %#x.\n", hr);
5520 HeapFree(GetProcessHeap(), 0, object);
5521 return hr;
5524 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
5525 wined3d_texture_get_parent(container), object, &parent, &parent_ops)))
5527 WARN("Failed to create surface parent, hr %#x.\n", hr);
5528 wined3d_surface_destroy(object);
5529 return hr;
5532 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
5534 object->resource.parent = parent;
5535 object->resource.parent_ops = parent_ops;
5536 *surface = object;
5538 return hr;