wined3d: TRACE fixes.
[wine.git] / dlls / wined3d / surface.c
blobb5133b0c386f5856a4fd83b365ae6c450f882e1b
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_is_lost(const struct wined3d_surface *surface)
1888 TRACE("surface %p.\n", surface);
1890 /* D3D8 and 9 loose full devices, ddraw only surfaces. */
1891 return surface->flags & SFLAG_LOST ? WINED3DERR_DEVICELOST : WINED3D_OK;
1894 HRESULT CDECL wined3d_surface_restore(struct wined3d_surface *surface)
1896 TRACE("surface %p.\n", surface);
1898 surface->flags &= ~SFLAG_LOST;
1899 return WINED3D_OK;
1902 DWORD CDECL wined3d_surface_get_pitch(const struct wined3d_surface *surface)
1904 unsigned int alignment;
1905 DWORD pitch;
1907 TRACE("surface %p.\n", surface);
1909 if (surface->pitch)
1910 return surface->pitch;
1912 alignment = surface->resource.device->surface_alignment;
1913 pitch = wined3d_format_calculate_pitch(surface->resource.format, surface->resource.width);
1914 pitch = (pitch + alignment - 1) & ~(alignment - 1);
1916 TRACE("Returning %u.\n", pitch);
1918 return pitch;
1921 HRESULT CDECL wined3d_surface_set_overlay_position(struct wined3d_surface *surface, LONG x, LONG y)
1923 LONG w, h;
1925 TRACE("surface %p, x %d, y %d.\n", surface, x, y);
1927 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1929 WARN("Not an overlay surface.\n");
1930 return WINEDDERR_NOTAOVERLAYSURFACE;
1933 w = surface->overlay_destrect.right - surface->overlay_destrect.left;
1934 h = surface->overlay_destrect.bottom - surface->overlay_destrect.top;
1935 surface->overlay_destrect.left = x;
1936 surface->overlay_destrect.top = y;
1937 surface->overlay_destrect.right = x + w;
1938 surface->overlay_destrect.bottom = y + h;
1940 return WINED3D_OK;
1943 HRESULT CDECL wined3d_surface_get_overlay_position(const struct wined3d_surface *surface, LONG *x, LONG *y)
1945 TRACE("surface %p, x %p, y %p.\n", surface, x, y);
1947 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1949 TRACE("Not an overlay surface.\n");
1950 return WINEDDERR_NOTAOVERLAYSURFACE;
1953 if (!surface->overlay_dest)
1955 TRACE("Overlay not visible.\n");
1956 *x = 0;
1957 *y = 0;
1958 return WINEDDERR_OVERLAYNOTVISIBLE;
1961 *x = surface->overlay_destrect.left;
1962 *y = surface->overlay_destrect.top;
1964 TRACE("Returning position %d, %d.\n", *x, *y);
1966 return WINED3D_OK;
1969 HRESULT CDECL wined3d_surface_update_overlay_z_order(struct wined3d_surface *surface,
1970 DWORD flags, struct wined3d_surface *ref)
1972 FIXME("surface %p, flags %#x, ref %p stub!\n", surface, flags, ref);
1974 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1976 TRACE("Not an overlay surface.\n");
1977 return WINEDDERR_NOTAOVERLAYSURFACE;
1980 return WINED3D_OK;
1983 HRESULT CDECL wined3d_surface_update_overlay(struct wined3d_surface *surface, const RECT *src_rect,
1984 struct wined3d_surface *dst_surface, const RECT *dst_rect, DWORD flags, const WINEDDOVERLAYFX *fx)
1986 TRACE("surface %p, src_rect %s, dst_surface %p, dst_rect %s, flags %#x, fx %p.\n",
1987 surface, wine_dbgstr_rect(src_rect), dst_surface, wine_dbgstr_rect(dst_rect), flags, fx);
1989 if (!(surface->resource.usage & WINED3DUSAGE_OVERLAY))
1991 WARN("Not an overlay surface.\n");
1992 return WINEDDERR_NOTAOVERLAYSURFACE;
1994 else if (!dst_surface)
1996 WARN("Dest surface is NULL.\n");
1997 return WINED3DERR_INVALIDCALL;
2000 surface_get_rect(surface, src_rect, &surface->overlay_srcrect);
2001 surface_get_rect(dst_surface, dst_rect, &surface->overlay_destrect);
2003 if (surface->overlay_dest && (surface->overlay_dest != dst_surface || flags & WINEDDOVER_HIDE))
2005 surface->overlay_dest = NULL;
2006 list_remove(&surface->overlay_entry);
2009 if (flags & WINEDDOVER_SHOW)
2011 if (surface->overlay_dest != dst_surface)
2013 surface->overlay_dest = dst_surface;
2014 list_add_tail(&dst_surface->overlays, &surface->overlay_entry);
2017 else if (flags & WINEDDOVER_HIDE)
2019 /* tests show that the rectangles are erased on hide */
2020 surface->overlay_srcrect.left = 0; surface->overlay_srcrect.top = 0;
2021 surface->overlay_srcrect.right = 0; surface->overlay_srcrect.bottom = 0;
2022 surface->overlay_destrect.left = 0; surface->overlay_destrect.top = 0;
2023 surface->overlay_destrect.right = 0; surface->overlay_destrect.bottom = 0;
2024 surface->overlay_dest = NULL;
2027 return WINED3D_OK;
2030 HRESULT wined3d_surface_update_desc(struct wined3d_surface *surface,
2031 const struct wined3d_gl_info *gl_info, void *mem, unsigned int pitch)
2033 struct wined3d_resource *texture_resource = &surface->container->resource;
2034 unsigned int width, height;
2035 BOOL create_dib = FALSE;
2036 DWORD valid_location = 0;
2037 HRESULT hr;
2039 if (surface->flags & SFLAG_DIBSECTION)
2041 DeleteDC(surface->hDC);
2042 DeleteObject(surface->dib.DIBsection);
2043 surface->dib.bitmap_data = NULL;
2044 surface->flags &= ~SFLAG_DIBSECTION;
2045 create_dib = TRUE;
2048 surface->locations = 0;
2049 wined3d_resource_free_sysmem(&surface->resource);
2051 width = texture_resource->width;
2052 height = texture_resource->height;
2053 surface->resource.width = width;
2054 surface->resource.height = height;
2055 if (gl_info->supported[ARB_TEXTURE_NON_POWER_OF_TWO] || gl_info->supported[ARB_TEXTURE_RECTANGLE]
2056 || gl_info->supported[WINED3D_GL_NORMALIZED_TEXRECT])
2058 surface->pow2Width = width;
2059 surface->pow2Height = height;
2061 else
2063 surface->pow2Width = surface->pow2Height = 1;
2064 while (surface->pow2Width < width)
2065 surface->pow2Width <<= 1;
2066 while (surface->pow2Height < height)
2067 surface->pow2Height <<= 1;
2070 if (surface->pow2Width != width || surface->pow2Height != height)
2071 surface->flags |= SFLAG_NONPOW2;
2072 else
2073 surface->flags &= ~SFLAG_NONPOW2;
2075 if ((surface->user_memory = mem))
2077 surface->resource.map_binding = WINED3D_LOCATION_USER_MEMORY;
2078 valid_location = WINED3D_LOCATION_USER_MEMORY;
2080 surface->pitch = pitch;
2081 surface->resource.format = texture_resource->format;
2082 surface->resource.multisample_type = texture_resource->multisample_type;
2083 surface->resource.multisample_quality = texture_resource->multisample_quality;
2084 if (surface->pitch)
2086 surface->resource.size = height * surface->pitch;
2088 else
2090 /* User memory surfaces don't have the regular surface alignment. */
2091 surface->resource.size = wined3d_format_calculate_size(texture_resource->format,
2092 1, width, height, 1);
2093 surface->pitch = wined3d_format_calculate_pitch(texture_resource->format, width);
2096 /* The format might be changed to a format that needs conversion.
2097 * If the surface didn't use PBOs previously but could now, don't
2098 * change it - whatever made us not use PBOs might come back, e.g.
2099 * color keys. */
2100 if (surface->resource.map_binding == WINED3D_LOCATION_BUFFER && !surface_use_pbo(surface))
2101 surface->resource.map_binding = create_dib ? WINED3D_LOCATION_DIB : WINED3D_LOCATION_SYSMEM;
2103 if (create_dib)
2105 if (FAILED(hr = surface_create_dib_section(surface)))
2107 ERR("Failed to create dib section, hr %#x.\n", hr);
2108 return hr;
2110 if (!valid_location)
2111 valid_location = WINED3D_LOCATION_DIB;
2114 if (!valid_location)
2116 surface_prepare_system_memory(surface);
2117 valid_location = WINED3D_LOCATION_SYSMEM;
2120 surface_validate_location(surface, valid_location);
2122 return WINED3D_OK;
2125 static void convert_r32_float_r16_float(const BYTE *src, BYTE *dst,
2126 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2128 unsigned short *dst_s;
2129 const float *src_f;
2130 unsigned int x, y;
2132 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2134 for (y = 0; y < h; ++y)
2136 src_f = (const float *)(src + y * pitch_in);
2137 dst_s = (unsigned short *) (dst + y * pitch_out);
2138 for (x = 0; x < w; ++x)
2140 dst_s[x] = float_32_to_16(src_f + x);
2145 static void convert_r5g6b5_x8r8g8b8(const BYTE *src, BYTE *dst,
2146 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2148 static const unsigned char convert_5to8[] =
2150 0x00, 0x08, 0x10, 0x19, 0x21, 0x29, 0x31, 0x3a,
2151 0x42, 0x4a, 0x52, 0x5a, 0x63, 0x6b, 0x73, 0x7b,
2152 0x84, 0x8c, 0x94, 0x9c, 0xa5, 0xad, 0xb5, 0xbd,
2153 0xc5, 0xce, 0xd6, 0xde, 0xe6, 0xef, 0xf7, 0xff,
2155 static const unsigned char convert_6to8[] =
2157 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c,
2158 0x20, 0x24, 0x28, 0x2d, 0x31, 0x35, 0x39, 0x3d,
2159 0x41, 0x45, 0x49, 0x4d, 0x51, 0x55, 0x59, 0x5d,
2160 0x61, 0x65, 0x69, 0x6d, 0x71, 0x75, 0x79, 0x7d,
2161 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x96, 0x9a, 0x9e,
2162 0xa2, 0xa6, 0xaa, 0xae, 0xb2, 0xb6, 0xba, 0xbe,
2163 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd7, 0xdb, 0xdf,
2164 0xe3, 0xe7, 0xeb, 0xef, 0xf3, 0xf7, 0xfb, 0xff,
2166 unsigned int x, y;
2168 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2170 for (y = 0; y < h; ++y)
2172 const WORD *src_line = (const WORD *)(src + y * pitch_in);
2173 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2174 for (x = 0; x < w; ++x)
2176 WORD pixel = src_line[x];
2177 dst_line[x] = 0xff000000u
2178 | convert_5to8[(pixel & 0xf800u) >> 11] << 16
2179 | convert_6to8[(pixel & 0x07e0u) >> 5] << 8
2180 | convert_5to8[(pixel & 0x001fu)];
2185 /* We use this for both B8G8R8A8 -> B8G8R8X8 and B8G8R8X8 -> B8G8R8A8, since
2186 * in both cases we're just setting the X / Alpha channel to 0xff. */
2187 static void convert_a8r8g8b8_x8r8g8b8(const BYTE *src, BYTE *dst,
2188 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2190 unsigned int x, y;
2192 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2194 for (y = 0; y < h; ++y)
2196 const DWORD *src_line = (const DWORD *)(src + y * pitch_in);
2197 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2199 for (x = 0; x < w; ++x)
2201 dst_line[x] = 0xff000000 | (src_line[x] & 0xffffff);
2206 static inline BYTE cliptobyte(int x)
2208 return (BYTE)((x < 0) ? 0 : ((x > 255) ? 255 : x));
2211 static void convert_yuy2_x8r8g8b8(const BYTE *src, BYTE *dst,
2212 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2214 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2215 unsigned int x, y;
2217 TRACE("Converting %ux%u pixels, pitches %u %u.\n", w, h, pitch_in, pitch_out);
2219 for (y = 0; y < h; ++y)
2221 const BYTE *src_line = src + y * pitch_in;
2222 DWORD *dst_line = (DWORD *)(dst + y * pitch_out);
2223 for (x = 0; x < w; ++x)
2225 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2226 * C = Y - 16; D = U - 128; E = V - 128;
2227 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2228 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2229 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2230 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2231 * U and V are shared between the pixels. */
2232 if (!(x & 1)) /* For every even pixel, read new U and V. */
2234 d = (int) src_line[1] - 128;
2235 e = (int) src_line[3] - 128;
2236 r2 = 409 * e + 128;
2237 g2 = - 100 * d - 208 * e + 128;
2238 b2 = 516 * d + 128;
2240 c2 = 298 * ((int) src_line[0] - 16);
2241 dst_line[x] = 0xff000000
2242 | cliptobyte((c2 + r2) >> 8) << 16 /* red */
2243 | cliptobyte((c2 + g2) >> 8) << 8 /* green */
2244 | cliptobyte((c2 + b2) >> 8); /* blue */
2245 /* Scale RGB values to 0..255 range,
2246 * then clip them if still not in range (may be negative),
2247 * then shift them within DWORD if necessary. */
2248 src_line += 2;
2253 static void convert_yuy2_r5g6b5(const BYTE *src, BYTE *dst,
2254 DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h)
2256 unsigned int x, y;
2257 int c2, d, e, r2 = 0, g2 = 0, b2 = 0;
2259 TRACE("Converting %ux%u pixels, pitches %u %u\n", w, h, pitch_in, pitch_out);
2261 for (y = 0; y < h; ++y)
2263 const BYTE *src_line = src + y * pitch_in;
2264 WORD *dst_line = (WORD *)(dst + y * pitch_out);
2265 for (x = 0; x < w; ++x)
2267 /* YUV to RGB conversion formulas from http://en.wikipedia.org/wiki/YUV:
2268 * C = Y - 16; D = U - 128; E = V - 128;
2269 * R = cliptobyte((298 * C + 409 * E + 128) >> 8);
2270 * G = cliptobyte((298 * C - 100 * D - 208 * E + 128) >> 8);
2271 * B = cliptobyte((298 * C + 516 * D + 128) >> 8);
2272 * Two adjacent YUY2 pixels are stored as four bytes: Y0 U Y1 V .
2273 * U and V are shared between the pixels. */
2274 if (!(x & 1)) /* For every even pixel, read new U and V. */
2276 d = (int) src_line[1] - 128;
2277 e = (int) src_line[3] - 128;
2278 r2 = 409 * e + 128;
2279 g2 = - 100 * d - 208 * e + 128;
2280 b2 = 516 * d + 128;
2282 c2 = 298 * ((int) src_line[0] - 16);
2283 dst_line[x] = (cliptobyte((c2 + r2) >> 8) >> 3) << 11 /* red */
2284 | (cliptobyte((c2 + g2) >> 8) >> 2) << 5 /* green */
2285 | (cliptobyte((c2 + b2) >> 8) >> 3); /* blue */
2286 /* Scale RGB values to 0..255 range,
2287 * then clip them if still not in range (may be negative),
2288 * then shift them within DWORD if necessary. */
2289 src_line += 2;
2294 struct d3dfmt_converter_desc
2296 enum wined3d_format_id from, to;
2297 void (*convert)(const BYTE *src, BYTE *dst, DWORD pitch_in, DWORD pitch_out, unsigned int w, unsigned int h);
2300 static const struct d3dfmt_converter_desc converters[] =
2302 {WINED3DFMT_R32_FLOAT, WINED3DFMT_R16_FLOAT, convert_r32_float_r16_float},
2303 {WINED3DFMT_B5G6R5_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_r5g6b5_x8r8g8b8},
2304 {WINED3DFMT_B8G8R8A8_UNORM, WINED3DFMT_B8G8R8X8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2305 {WINED3DFMT_B8G8R8X8_UNORM, WINED3DFMT_B8G8R8A8_UNORM, convert_a8r8g8b8_x8r8g8b8},
2306 {WINED3DFMT_YUY2, WINED3DFMT_B8G8R8X8_UNORM, convert_yuy2_x8r8g8b8},
2307 {WINED3DFMT_YUY2, WINED3DFMT_B5G6R5_UNORM, convert_yuy2_r5g6b5},
2310 static inline const struct d3dfmt_converter_desc *find_converter(enum wined3d_format_id from,
2311 enum wined3d_format_id to)
2313 unsigned int i;
2315 for (i = 0; i < (sizeof(converters) / sizeof(*converters)); ++i)
2317 if (converters[i].from == from && converters[i].to == to)
2318 return &converters[i];
2321 return NULL;
2324 static struct wined3d_texture *surface_convert_format(struct wined3d_surface *source, enum wined3d_format_id to_fmt)
2326 struct wined3d_map_desc src_map, dst_map;
2327 const struct d3dfmt_converter_desc *conv;
2328 struct wined3d_texture *ret = NULL;
2329 struct wined3d_resource_desc desc;
2330 struct wined3d_surface *dst;
2332 conv = find_converter(source->resource.format->id, to_fmt);
2333 if (!conv)
2335 FIXME("Cannot find a conversion function from format %s to %s.\n",
2336 debug_d3dformat(source->resource.format->id), debug_d3dformat(to_fmt));
2337 return NULL;
2340 /* FIXME: Multisampled conversion? */
2341 wined3d_resource_get_desc(&source->resource, &desc);
2342 desc.resource_type = WINED3D_RTYPE_TEXTURE;
2343 desc.format = to_fmt;
2344 desc.usage = 0;
2345 desc.pool = WINED3D_POOL_SCRATCH;
2346 if (FAILED(wined3d_texture_create(source->resource.device, &desc, 1,
2347 WINED3D_SURFACE_MAPPABLE | WINED3D_SURFACE_DISCARD, NULL, NULL, &wined3d_null_parent_ops, &ret)))
2349 ERR("Failed to create a destination surface for conversion.\n");
2350 return NULL;
2352 dst = surface_from_resource(wined3d_texture_get_sub_resource(ret, 0));
2354 memset(&src_map, 0, sizeof(src_map));
2355 memset(&dst_map, 0, sizeof(dst_map));
2357 if (FAILED(wined3d_surface_map(source, &src_map, NULL, WINED3D_MAP_READONLY)))
2359 ERR("Failed to lock the source surface.\n");
2360 wined3d_texture_decref(ret);
2361 return NULL;
2363 if (FAILED(wined3d_surface_map(dst, &dst_map, NULL, 0)))
2365 ERR("Failed to lock the destination surface.\n");
2366 wined3d_surface_unmap(source);
2367 wined3d_texture_decref(ret);
2368 return NULL;
2371 conv->convert(src_map.data, dst_map.data, src_map.row_pitch, dst_map.row_pitch,
2372 source->resource.width, source->resource.height);
2374 wined3d_surface_unmap(dst);
2375 wined3d_surface_unmap(source);
2377 return ret;
2380 static HRESULT _Blt_ColorFill(BYTE *buf, unsigned int width, unsigned int height,
2381 unsigned int bpp, UINT pitch, DWORD color)
2383 BYTE *first;
2384 unsigned int x, y;
2386 /* Do first row */
2388 #define COLORFILL_ROW(type) \
2389 do { \
2390 type *d = (type *)buf; \
2391 for (x = 0; x < width; ++x) \
2392 d[x] = (type)color; \
2393 } while(0)
2395 switch (bpp)
2397 case 1:
2398 COLORFILL_ROW(BYTE);
2399 break;
2401 case 2:
2402 COLORFILL_ROW(WORD);
2403 break;
2405 case 3:
2407 BYTE *d = buf;
2408 for (x = 0; x < width; ++x, d += 3)
2410 d[0] = (color ) & 0xff;
2411 d[1] = (color >> 8) & 0xff;
2412 d[2] = (color >> 16) & 0xff;
2414 break;
2416 case 4:
2417 COLORFILL_ROW(DWORD);
2418 break;
2420 default:
2421 FIXME("Color fill not implemented for bpp %u!\n", bpp * 8);
2422 return WINED3DERR_NOTAVAILABLE;
2425 #undef COLORFILL_ROW
2427 /* Now copy first row. */
2428 first = buf;
2429 for (y = 1; y < height; ++y)
2431 buf += pitch;
2432 memcpy(buf, first, width * bpp);
2435 return WINED3D_OK;
2438 struct wined3d_surface * CDECL wined3d_surface_from_resource(struct wined3d_resource *resource)
2440 return surface_from_resource(resource);
2443 HRESULT CDECL wined3d_surface_unmap(struct wined3d_surface *surface)
2445 TRACE("surface %p.\n", surface);
2447 if (!surface->resource.map_count)
2449 WARN("Trying to unmap unmapped surface.\n");
2450 return WINEDDERR_NOTLOCKED;
2452 --surface->resource.map_count;
2454 surface->surface_ops->surface_unmap(surface);
2456 return WINED3D_OK;
2459 HRESULT CDECL wined3d_surface_map(struct wined3d_surface *surface,
2460 struct wined3d_map_desc *map_desc, const RECT *rect, DWORD flags)
2462 const struct wined3d_format *format = surface->resource.format;
2463 unsigned int fmt_flags = surface->container->resource.format_flags;
2464 struct wined3d_device *device = surface->resource.device;
2465 struct wined3d_context *context;
2466 const struct wined3d_gl_info *gl_info;
2467 BYTE *base_memory;
2469 TRACE("surface %p, map_desc %p, rect %s, flags %#x.\n",
2470 surface, map_desc, wine_dbgstr_rect(rect), flags);
2472 if (surface->resource.map_count)
2474 WARN("Surface is already mapped.\n");
2475 return WINED3DERR_INVALIDCALL;
2478 if ((fmt_flags & WINED3DFMT_FLAG_BLOCKS) && rect
2479 && !surface_check_block_align(surface, rect))
2481 WARN("Map rect %s is misaligned for %ux%u blocks.\n",
2482 wine_dbgstr_rect(rect), format->block_width, format->block_height);
2484 if (surface->resource.pool == WINED3D_POOL_DEFAULT)
2485 return WINED3DERR_INVALIDCALL;
2488 ++surface->resource.map_count;
2490 if (!(surface->resource.access_flags & WINED3D_RESOURCE_ACCESS_CPU))
2491 WARN("Trying to lock unlockable surface.\n");
2493 /* Performance optimization: Count how often a surface is mapped, if it is
2494 * mapped regularly do not throw away the system memory copy. This avoids
2495 * the need to download the surface from OpenGL all the time. The surface
2496 * is still downloaded if the OpenGL texture is changed. Note that this
2497 * only really makes sense for managed textures.*/
2498 if (!(surface->container->flags & WINED3D_TEXTURE_DYNAMIC_MAP)
2499 && surface->resource.map_binding == WINED3D_LOCATION_SYSMEM)
2501 if (++surface->lockCount > MAXLOCKCOUNT)
2503 TRACE("Surface is mapped regularly, not freeing the system memory copy any more.\n");
2504 surface->container->flags |= WINED3D_TEXTURE_DYNAMIC_MAP;
2508 surface_prepare_map_memory(surface);
2509 if (flags & WINED3D_MAP_DISCARD)
2511 TRACE("WINED3D_MAP_DISCARD flag passed, marking %s as up to date.\n",
2512 wined3d_debug_location(surface->resource.map_binding));
2513 surface_validate_location(surface, surface->resource.map_binding);
2515 else
2517 if (surface->resource.usage & WINED3DUSAGE_DYNAMIC)
2518 WARN_(d3d_perf)("Mapping a dynamic surface without WINED3D_MAP_DISCARD.\n");
2520 surface_load_location(surface, surface->resource.map_binding);
2523 if (!(flags & (WINED3D_MAP_NO_DIRTY_UPDATE | WINED3D_MAP_READONLY)))
2524 surface_invalidate_location(surface, ~surface->resource.map_binding);
2526 switch (surface->resource.map_binding)
2528 case WINED3D_LOCATION_SYSMEM:
2529 base_memory = surface->resource.heap_memory;
2530 break;
2532 case WINED3D_LOCATION_USER_MEMORY:
2533 base_memory = surface->user_memory;
2534 break;
2536 case WINED3D_LOCATION_DIB:
2537 base_memory = surface->dib.bitmap_data;
2538 break;
2540 case WINED3D_LOCATION_BUFFER:
2541 context = context_acquire(device, NULL);
2542 gl_info = context->gl_info;
2544 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, surface->pbo));
2545 base_memory = GL_EXTCALL(glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_WRITE));
2546 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
2547 checkGLcall("map PBO");
2549 context_release(context);
2550 break;
2552 default:
2553 ERR("Unexpected map binding %s.\n", wined3d_debug_location(surface->resource.map_binding));
2554 base_memory = NULL;
2557 if (fmt_flags & WINED3DFMT_FLAG_BROKEN_PITCH)
2558 map_desc->row_pitch = surface->resource.width * format->byte_count;
2559 else
2560 map_desc->row_pitch = wined3d_surface_get_pitch(surface);
2561 map_desc->slice_pitch = 0;
2563 if (!rect)
2565 map_desc->data = base_memory;
2566 surface->lockedRect.left = 0;
2567 surface->lockedRect.top = 0;
2568 surface->lockedRect.right = surface->resource.width;
2569 surface->lockedRect.bottom = surface->resource.height;
2571 else
2573 if ((fmt_flags & (WINED3DFMT_FLAG_BLOCKS | WINED3DFMT_FLAG_BROKEN_PITCH)) == WINED3DFMT_FLAG_BLOCKS)
2575 /* Compressed textures are block based, so calculate the offset of
2576 * the block that contains the top-left pixel of the locked rectangle. */
2577 map_desc->data = base_memory
2578 + ((rect->top / format->block_height) * map_desc->row_pitch)
2579 + ((rect->left / format->block_width) * format->block_byte_count);
2581 else
2583 map_desc->data = base_memory
2584 + (map_desc->row_pitch * rect->top)
2585 + (rect->left * format->byte_count);
2587 surface->lockedRect.left = rect->left;
2588 surface->lockedRect.top = rect->top;
2589 surface->lockedRect.right = rect->right;
2590 surface->lockedRect.bottom = rect->bottom;
2593 TRACE("Locked rect %s.\n", wine_dbgstr_rect(&surface->lockedRect));
2594 TRACE("Returning memory %p, pitch %u.\n", map_desc->data, map_desc->row_pitch);
2596 return WINED3D_OK;
2599 HRESULT CDECL wined3d_surface_getdc(struct wined3d_surface *surface, HDC *dc)
2601 HRESULT hr;
2603 TRACE("surface %p, dc %p.\n", surface, dc);
2605 /* Give more detailed info for ddraw. */
2606 if (surface->flags & SFLAG_DCINUSE)
2607 return WINEDDERR_DCALREADYCREATED;
2609 /* Can't GetDC if the surface is locked. */
2610 if (surface->resource.map_count)
2611 return WINED3DERR_INVALIDCALL;
2613 /* Create a DIB section if there isn't a dc yet. */
2614 if (!surface->hDC)
2616 if (surface->flags & SFLAG_CLIENT)
2618 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
2619 surface_release_client_storage(surface);
2621 hr = surface_create_dib_section(surface);
2622 if (FAILED(hr))
2623 return WINED3DERR_INVALIDCALL;
2624 if (!(surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2625 || surface->container->flags & WINED3D_TEXTURE_PIN_SYSMEM
2626 || surface->pbo))
2627 surface->resource.map_binding = WINED3D_LOCATION_DIB;
2630 surface_load_location(surface, WINED3D_LOCATION_DIB);
2631 surface_invalidate_location(surface, ~WINED3D_LOCATION_DIB);
2633 surface->flags |= SFLAG_DCINUSE;
2634 surface->resource.map_count++;
2636 *dc = surface->hDC;
2637 TRACE("Returning dc %p.\n", *dc);
2639 return WINED3D_OK;
2642 HRESULT CDECL wined3d_surface_releasedc(struct wined3d_surface *surface, HDC dc)
2644 TRACE("surface %p, dc %p.\n", surface, dc);
2646 if (!(surface->flags & SFLAG_DCINUSE))
2647 return WINEDDERR_NODC;
2649 if (surface->hDC != dc)
2651 WARN("Application tries to release invalid DC %p, surface DC is %p.\n",
2652 dc, surface->hDC);
2653 return WINEDDERR_NODC;
2656 surface->resource.map_count--;
2657 surface->flags &= ~SFLAG_DCINUSE;
2659 if (surface->resource.map_binding == WINED3D_LOCATION_USER_MEMORY
2660 || (surface->container->flags & WINED3D_TEXTURE_PIN_SYSMEM
2661 && surface->resource.map_binding != WINED3D_LOCATION_DIB))
2663 /* The game Salammbo modifies the surface contents without mapping the surface between
2664 * a GetDC/ReleaseDC operation and flipping the surface. If the DIB remains the active
2665 * copy and is copied to the screen, this update, which draws the mouse pointer, is lost.
2666 * Do not only copy the DIB to the map location, but also make sure the map location is
2667 * copied back to the DIB in the next getdc call.
2669 * The same consideration applies to user memory surfaces. */
2670 surface_load_location(surface, surface->resource.map_binding);
2671 surface_invalidate_location(surface, WINED3D_LOCATION_DIB);
2674 return WINED3D_OK;
2677 static void read_from_framebuffer(struct wined3d_surface *surface, DWORD dst_location)
2679 struct wined3d_device *device = surface->resource.device;
2680 const struct wined3d_gl_info *gl_info;
2681 struct wined3d_context *context;
2682 BYTE *mem;
2683 BYTE *row, *top, *bottom;
2684 int i;
2685 BOOL srcIsUpsideDown;
2686 struct wined3d_bo_address data;
2688 surface_get_memory(surface, &data, dst_location);
2690 context = context_acquire(device, surface);
2691 context_apply_blit_state(context, device);
2692 gl_info = context->gl_info;
2694 /* Select the correct read buffer, and give some debug output.
2695 * There is no need to keep track of the current read buffer or reset it, every part of the code
2696 * that reads sets the read buffer as desired.
2698 if (wined3d_resource_is_offscreen(&surface->container->resource))
2700 /* Mapping the primary render target which is not on a swapchain.
2701 * Read from the back buffer. */
2702 TRACE("Mapping offscreen render target.\n");
2703 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2704 srcIsUpsideDown = TRUE;
2706 else
2708 /* Onscreen surfaces are always part of a swapchain */
2709 GLenum buffer = surface_get_gl_buffer(surface);
2710 TRACE("Mapping %#x buffer.\n", buffer);
2711 gl_info->gl_ops.gl.p_glReadBuffer(buffer);
2712 checkGLcall("glReadBuffer");
2713 srcIsUpsideDown = FALSE;
2716 if (data.buffer_object)
2718 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, data.buffer_object));
2719 checkGLcall("glBindBuffer");
2722 /* Setup pixel store pack state -- to glReadPixels into the correct place */
2723 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH,
2724 wined3d_surface_get_pitch(surface) / surface->resource.format->byte_count);
2725 checkGLcall("glPixelStorei");
2727 gl_info->gl_ops.gl.p_glReadPixels(0, 0,
2728 surface->resource.width, surface->resource.height,
2729 surface->resource.format->glFormat,
2730 surface->resource.format->glType, data.addr);
2731 checkGLcall("glReadPixels");
2733 /* Reset previous pixel store pack state */
2734 gl_info->gl_ops.gl.p_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
2735 checkGLcall("glPixelStorei");
2737 if (!srcIsUpsideDown)
2739 /* glReadPixels returns the image upside down, and there is no way to prevent this.
2740 * Flip the lines in software. */
2741 UINT pitch = wined3d_surface_get_pitch(surface);
2743 if (!(row = HeapAlloc(GetProcessHeap(), 0, pitch)))
2744 goto error;
2746 if (data.buffer_object)
2748 mem = GL_EXTCALL(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE));
2749 checkGLcall("glMapBuffer");
2751 else
2752 mem = data.addr;
2754 top = mem;
2755 bottom = mem + pitch * (surface->resource.height - 1);
2756 for (i = 0; i < surface->resource.height / 2; i++)
2758 memcpy(row, top, pitch);
2759 memcpy(top, bottom, pitch);
2760 memcpy(bottom, row, pitch);
2761 top += pitch;
2762 bottom -= pitch;
2764 HeapFree(GetProcessHeap(), 0, row);
2766 if (data.buffer_object)
2767 GL_EXTCALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
2770 error:
2771 if (data.buffer_object)
2773 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
2774 checkGLcall("glBindBuffer");
2777 context_release(context);
2780 /* Read the framebuffer contents into a texture. Note that this function
2781 * doesn't do any kind of flipping. Using this on an onscreen surface will
2782 * result in a flipped D3D texture. */
2783 void surface_load_fb_texture(struct wined3d_surface *surface, BOOL srgb)
2785 struct wined3d_device *device = surface->resource.device;
2786 const struct wined3d_gl_info *gl_info;
2787 struct wined3d_context *context;
2789 context = context_acquire(device, surface);
2790 gl_info = context->gl_info;
2791 device_invalidate_state(device, STATE_FRAMEBUFFER);
2793 wined3d_texture_prepare_texture(surface->container, context, srgb);
2794 wined3d_texture_bind_and_dirtify(surface->container, context, srgb);
2796 TRACE("Reading back offscreen render target %p.\n", surface);
2798 if (wined3d_resource_is_offscreen(&surface->container->resource))
2799 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2800 else
2801 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(surface));
2802 checkGLcall("glReadBuffer");
2804 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(surface->texture_target, surface->texture_level,
2805 0, 0, 0, 0, surface->resource.width, surface->resource.height);
2806 checkGLcall("glCopyTexSubImage2D");
2808 context_release(context);
2811 void surface_prepare_rb(struct wined3d_surface *surface, const struct wined3d_gl_info *gl_info, BOOL multisample)
2813 if (multisample)
2815 DWORD samples;
2817 if (surface->rb_multisample)
2818 return;
2820 /* TODO: Nvidia exposes their Coverage Sample Anti-Aliasing (CSAA) feature
2821 * through type == MULTISAMPLE_XX and quality != 0. This could be mapped
2822 * to GL_NV_framebuffer_multisample_coverage.
2824 * AMD has a similar feature called Enhanced Quality Anti-Aliasing (EQAA),
2825 * but it does not have an equivalent OpenGL extension. */
2826 if (surface->resource.multisample_type == WINED3D_MULTISAMPLE_NON_MASKABLE)
2827 samples = surface->resource.multisample_quality;
2828 else
2829 samples = surface->resource.multisample_type;
2831 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_multisample);
2832 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_multisample);
2833 gl_info->fbo_ops.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
2834 surface->resource.format->glInternal, surface->pow2Width, surface->pow2Height);
2835 checkGLcall("glRenderbufferStorageMultisample()");
2836 TRACE("Created multisample rb %u.\n", surface->rb_multisample);
2838 else
2840 if (surface->rb_resolved)
2841 return;
2843 gl_info->fbo_ops.glGenRenderbuffers(1, &surface->rb_resolved);
2844 gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, surface->rb_resolved);
2845 gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, surface->resource.format->glInternal,
2846 surface->pow2Width, surface->pow2Height);
2847 checkGLcall("glRenderbufferStorage()");
2848 TRACE("Created resolved rb %u.\n", surface->rb_resolved);
2852 /* Does a direct frame buffer -> texture copy. Stretching is done with single
2853 * pixel copy calls. */
2854 static void fb_copy_to_texture_direct(struct wined3d_surface *dst_surface, struct wined3d_surface *src_surface,
2855 const RECT *src_rect, const RECT *dst_rect_in, enum wined3d_texture_filter_type filter)
2857 struct wined3d_device *device = dst_surface->resource.device;
2858 const struct wined3d_gl_info *gl_info;
2859 float xrel, yrel;
2860 struct wined3d_context *context;
2861 BOOL upsidedown = FALSE;
2862 RECT dst_rect = *dst_rect_in;
2864 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
2865 * glCopyTexSubImage is a bit picky about the parameters we pass to it
2867 if(dst_rect.top > dst_rect.bottom) {
2868 UINT tmp = dst_rect.bottom;
2869 dst_rect.bottom = dst_rect.top;
2870 dst_rect.top = tmp;
2871 upsidedown = TRUE;
2874 context = context_acquire(device, src_surface);
2875 gl_info = context->gl_info;
2876 context_apply_blit_state(context, device);
2877 wined3d_texture_load(dst_surface->container, context, FALSE);
2879 /* Bind the target texture */
2880 context_bind_texture(context, dst_surface->container->target, dst_surface->container->texture_rgb.name);
2881 if (wined3d_resource_is_offscreen(&src_surface->container->resource))
2883 TRACE("Reading from an offscreen target\n");
2884 upsidedown = !upsidedown;
2885 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
2887 else
2889 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
2891 checkGLcall("glReadBuffer");
2893 xrel = (float) (src_rect->right - src_rect->left) / (float) (dst_rect.right - dst_rect.left);
2894 yrel = (float) (src_rect->bottom - src_rect->top) / (float) (dst_rect.bottom - dst_rect.top);
2896 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2898 FIXME("Doing a pixel by pixel copy from the framebuffer to a texture, expect major performance issues\n");
2900 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
2901 ERR("Texture filtering not supported in direct blit.\n");
2903 else if ((filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT)
2904 && ((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
2906 ERR("Texture filtering not supported in direct blit\n");
2909 if (upsidedown
2910 && !((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2911 && !((yrel - 1.0f < -eps) || (yrel - 1.0f > eps)))
2913 /* Upside down copy without stretching is nice, one glCopyTexSubImage call will do. */
2914 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2915 dst_rect.left /*xoffset */, dst_rect.top /* y offset */,
2916 src_rect->left, src_surface->resource.height - src_rect->bottom,
2917 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
2919 else
2921 LONG row;
2922 UINT yoffset = src_surface->resource.height - src_rect->top + dst_rect.top - 1;
2923 /* I have to process this row by row to swap the image,
2924 * otherwise it would be upside down, so stretching in y direction
2925 * doesn't cost extra time
2927 * However, stretching in x direction can be avoided if not necessary
2929 for(row = dst_rect.top; row < dst_rect.bottom; row++) {
2930 if ((xrel - 1.0f < -eps) || (xrel - 1.0f > eps))
2932 /* Well, that stuff works, but it's very slow.
2933 * find a better way instead
2935 LONG col;
2937 for (col = dst_rect.left; col < dst_rect.right; ++col)
2939 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2940 dst_rect.left + col /* x offset */, row /* y offset */,
2941 src_rect->left + col * xrel, yoffset - (int) (row * yrel), 1, 1);
2944 else
2946 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(dst_surface->texture_target, dst_surface->texture_level,
2947 dst_rect.left /* x offset */, row /* y offset */,
2948 src_rect->left, yoffset - (int) (row * yrel), dst_rect.right - dst_rect.left, 1);
2952 checkGLcall("glCopyTexSubImage2D");
2954 context_release(context);
2956 /* The texture is now most up to date - If the surface is a render target
2957 * and has a drawable, this path is never entered. */
2958 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
2959 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
2962 /* Uses the hardware to stretch and flip the image */
2963 static void fb_copy_to_texture_hwstretch(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 GLuint src, backup = 0;
2968 float left, right, top, bottom; /* Texture coordinates */
2969 UINT fbwidth = src_surface->resource.width;
2970 UINT fbheight = src_surface->resource.height;
2971 const struct wined3d_gl_info *gl_info;
2972 struct wined3d_context *context;
2973 GLenum drawBuffer = GL_BACK;
2974 GLenum texture_target;
2975 BOOL noBackBufferBackup;
2976 BOOL src_offscreen;
2977 BOOL upsidedown = FALSE;
2978 RECT dst_rect = *dst_rect_in;
2980 TRACE("Using hwstretch blit\n");
2981 /* Activate the Proper context for reading from the source surface, set it up for blitting */
2982 context = context_acquire(device, src_surface);
2983 gl_info = context->gl_info;
2984 context_apply_blit_state(context, device);
2985 wined3d_texture_load(dst_surface->container, context, FALSE);
2987 src_offscreen = wined3d_resource_is_offscreen(&src_surface->container->resource);
2988 noBackBufferBackup = src_offscreen && wined3d_settings.offscreen_rendering_mode == ORM_FBO;
2989 if (!noBackBufferBackup && !src_surface->container->texture_rgb.name)
2991 /* Get it a description */
2992 wined3d_texture_load(src_surface->container, context, FALSE);
2995 /* Try to use an aux buffer for drawing the rectangle. This way it doesn't need restoring.
2996 * This way we don't have to wait for the 2nd readback to finish to leave this function.
2998 if (context->aux_buffers >= 2)
3000 /* Got more than one aux buffer? Use the 2nd aux buffer */
3001 drawBuffer = GL_AUX1;
3003 else if ((!src_offscreen || device->offscreenBuffer == GL_BACK) && context->aux_buffers >= 1)
3005 /* Only one aux buffer, but it isn't used (Onscreen rendering, or non-aux orm)? Use it! */
3006 drawBuffer = GL_AUX0;
3009 if (noBackBufferBackup)
3011 gl_info->gl_ops.gl.p_glGenTextures(1, &backup);
3012 checkGLcall("glGenTextures");
3013 context_bind_texture(context, GL_TEXTURE_2D, backup);
3014 texture_target = GL_TEXTURE_2D;
3016 else
3018 /* Backup the back buffer and copy the source buffer into a texture to draw an upside down stretched quad. If
3019 * we are reading from the back buffer, the backup can be used as source texture
3021 texture_target = src_surface->texture_target;
3022 context_bind_texture(context, texture_target, src_surface->container->texture_rgb.name);
3023 gl_info->gl_ops.gl.p_glEnable(texture_target);
3024 checkGLcall("glEnable(texture_target)");
3026 /* For now invalidate the texture copy of the back buffer. Drawable and sysmem copy are untouched */
3027 src_surface->locations &= ~WINED3D_LOCATION_TEXTURE_RGB;
3030 /* Make sure that the top pixel is always above the bottom pixel, and keep a separate upside down flag
3031 * glCopyTexSubImage is a bit picky about the parameters we pass to it
3033 if(dst_rect.top > dst_rect.bottom) {
3034 UINT tmp = dst_rect.bottom;
3035 dst_rect.bottom = dst_rect.top;
3036 dst_rect.top = tmp;
3037 upsidedown = TRUE;
3040 if (src_offscreen)
3042 TRACE("Reading from an offscreen target\n");
3043 upsidedown = !upsidedown;
3044 gl_info->gl_ops.gl.p_glReadBuffer(device->offscreenBuffer);
3046 else
3048 gl_info->gl_ops.gl.p_glReadBuffer(surface_get_gl_buffer(src_surface));
3051 /* TODO: Only back up the part that will be overwritten */
3052 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target, 0, 0, 0, 0, 0, fbwidth, fbheight);
3054 checkGLcall("glCopyTexSubImage2D");
3056 /* No issue with overriding these - the sampler is dirty due to blit usage */
3057 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, wined3d_gl_mag_filter(filter));
3058 checkGLcall("glTexParameteri");
3059 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER,
3060 wined3d_gl_min_mip_filter(filter, WINED3D_TEXF_NONE));
3061 checkGLcall("glTexParameteri");
3063 if (!src_surface->container->swapchain
3064 || src_surface->container == src_surface->container->swapchain->back_buffers[0])
3066 src = backup ? backup : src_surface->container->texture_rgb.name;
3068 else
3070 gl_info->gl_ops.gl.p_glReadBuffer(GL_FRONT);
3071 checkGLcall("glReadBuffer(GL_FRONT)");
3073 gl_info->gl_ops.gl.p_glGenTextures(1, &src);
3074 checkGLcall("glGenTextures(1, &src)");
3075 context_bind_texture(context, GL_TEXTURE_2D, src);
3077 /* TODO: Only copy the part that will be read. Use src_rect->left, src_rect->bottom as origin, but with the width watch
3078 * out for power of 2 sizes
3080 gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, src_surface->pow2Width,
3081 src_surface->pow2Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
3082 checkGLcall("glTexImage2D");
3083 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, fbwidth, fbheight);
3085 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3086 checkGLcall("glTexParameteri");
3087 gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3088 checkGLcall("glTexParameteri");
3090 gl_info->gl_ops.gl.p_glReadBuffer(GL_BACK);
3091 checkGLcall("glReadBuffer(GL_BACK)");
3093 if (texture_target != GL_TEXTURE_2D)
3095 gl_info->gl_ops.gl.p_glDisable(texture_target);
3096 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3097 texture_target = GL_TEXTURE_2D;
3100 checkGLcall("glEnd and previous");
3102 left = src_rect->left;
3103 right = src_rect->right;
3105 if (!upsidedown)
3107 top = src_surface->resource.height - src_rect->top;
3108 bottom = src_surface->resource.height - src_rect->bottom;
3110 else
3112 top = src_surface->resource.height - src_rect->bottom;
3113 bottom = src_surface->resource.height - src_rect->top;
3116 if (src_surface->container->flags & WINED3D_TEXTURE_NORMALIZED_COORDS)
3118 left /= src_surface->pow2Width;
3119 right /= src_surface->pow2Width;
3120 top /= src_surface->pow2Height;
3121 bottom /= src_surface->pow2Height;
3124 /* draw the source texture stretched and upside down. The correct surface is bound already */
3125 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3126 gl_info->gl_ops.gl.p_glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3128 context_set_draw_buffer(context, drawBuffer);
3129 gl_info->gl_ops.gl.p_glReadBuffer(drawBuffer);
3131 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3132 /* bottom left */
3133 gl_info->gl_ops.gl.p_glTexCoord2f(left, bottom);
3134 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3136 /* top left */
3137 gl_info->gl_ops.gl.p_glTexCoord2f(left, top);
3138 gl_info->gl_ops.gl.p_glVertex2i(0, dst_rect.bottom - dst_rect.top);
3140 /* top right */
3141 gl_info->gl_ops.gl.p_glTexCoord2f(right, top);
3142 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3144 /* bottom right */
3145 gl_info->gl_ops.gl.p_glTexCoord2f(right, bottom);
3146 gl_info->gl_ops.gl.p_glVertex2i(dst_rect.right - dst_rect.left, 0);
3147 gl_info->gl_ops.gl.p_glEnd();
3148 checkGLcall("glEnd and previous");
3150 if (texture_target != dst_surface->texture_target)
3152 gl_info->gl_ops.gl.p_glDisable(texture_target);
3153 gl_info->gl_ops.gl.p_glEnable(dst_surface->texture_target);
3154 texture_target = dst_surface->texture_target;
3157 /* Now read the stretched and upside down image into the destination texture */
3158 context_bind_texture(context, texture_target, dst_surface->container->texture_rgb.name);
3159 gl_info->gl_ops.gl.p_glCopyTexSubImage2D(texture_target,
3161 dst_rect.left, dst_rect.top, /* xoffset, yoffset */
3162 0, 0, /* We blitted the image to the origin */
3163 dst_rect.right - dst_rect.left, dst_rect.bottom - dst_rect.top);
3164 checkGLcall("glCopyTexSubImage2D");
3166 if (drawBuffer == GL_BACK)
3168 /* Write the back buffer backup back. */
3169 if (backup)
3171 if (texture_target != GL_TEXTURE_2D)
3173 gl_info->gl_ops.gl.p_glDisable(texture_target);
3174 gl_info->gl_ops.gl.p_glEnable(GL_TEXTURE_2D);
3175 texture_target = GL_TEXTURE_2D;
3177 context_bind_texture(context, GL_TEXTURE_2D, backup);
3179 else
3181 if (texture_target != src_surface->texture_target)
3183 gl_info->gl_ops.gl.p_glDisable(texture_target);
3184 gl_info->gl_ops.gl.p_glEnable(src_surface->texture_target);
3185 texture_target = src_surface->texture_target;
3187 context_bind_texture(context, src_surface->texture_target, src_surface->container->texture_rgb.name);
3190 gl_info->gl_ops.gl.p_glBegin(GL_QUADS);
3191 /* top left */
3192 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, 0.0f);
3193 gl_info->gl_ops.gl.p_glVertex2i(0, fbheight);
3195 /* bottom left */
3196 gl_info->gl_ops.gl.p_glTexCoord2f(0.0f, (float)fbheight / (float)src_surface->pow2Height);
3197 gl_info->gl_ops.gl.p_glVertex2i(0, 0);
3199 /* bottom right */
3200 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width,
3201 (float)fbheight / (float)src_surface->pow2Height);
3202 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, 0);
3204 /* top right */
3205 gl_info->gl_ops.gl.p_glTexCoord2f((float)fbwidth / (float)src_surface->pow2Width, 0.0f);
3206 gl_info->gl_ops.gl.p_glVertex2i(fbwidth, fbheight);
3207 gl_info->gl_ops.gl.p_glEnd();
3209 gl_info->gl_ops.gl.p_glDisable(texture_target);
3210 checkGLcall("glDisable(texture_target)");
3212 /* Cleanup */
3213 if (src != src_surface->container->texture_rgb.name && src != backup)
3215 gl_info->gl_ops.gl.p_glDeleteTextures(1, &src);
3216 checkGLcall("glDeleteTextures(1, &src)");
3218 if (backup)
3220 gl_info->gl_ops.gl.p_glDeleteTextures(1, &backup);
3221 checkGLcall("glDeleteTextures(1, &backup)");
3224 if (wined3d_settings.strict_draw_ordering)
3225 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3227 context_release(context);
3229 /* The texture is now most up to date - If the surface is a render target
3230 * and has a drawable, this path is never entered. */
3231 surface_validate_location(dst_surface, WINED3D_LOCATION_TEXTURE_RGB);
3232 surface_invalidate_location(dst_surface, ~WINED3D_LOCATION_TEXTURE_RGB);
3235 /* Front buffer coordinates are always full screen coordinates, but our GL
3236 * drawable is limited to the window's client area. The sysmem and texture
3237 * copies do have the full screen size. Note that GL has a bottom-left
3238 * origin, while D3D has a top-left origin. */
3239 void surface_translate_drawable_coords(const struct wined3d_surface *surface, HWND window, RECT *rect)
3241 UINT drawable_height;
3243 if (surface->container->swapchain && surface->container == surface->container->swapchain->front_buffer)
3245 POINT offset = {0, 0};
3246 RECT windowsize;
3248 ScreenToClient(window, &offset);
3249 OffsetRect(rect, offset.x, offset.y);
3251 GetClientRect(window, &windowsize);
3252 drawable_height = windowsize.bottom - windowsize.top;
3254 else
3256 drawable_height = surface->resource.height;
3259 rect->top = drawable_height - rect->top;
3260 rect->bottom = drawable_height - rect->bottom;
3263 static void surface_blt_to_drawable(const struct wined3d_device *device,
3264 enum wined3d_texture_filter_type filter, BOOL alpha_test,
3265 struct wined3d_surface *src_surface, const RECT *src_rect_in,
3266 struct wined3d_surface *dst_surface, const RECT *dst_rect_in)
3268 const struct wined3d_gl_info *gl_info;
3269 struct wined3d_context *context;
3270 RECT src_rect, dst_rect;
3272 src_rect = *src_rect_in;
3273 dst_rect = *dst_rect_in;
3275 context = context_acquire(device, dst_surface);
3276 gl_info = context->gl_info;
3278 /* Make sure the surface is up-to-date. This should probably use
3279 * surface_load_location() and worry about the destination surface too,
3280 * unless we're overwriting it completely. */
3281 wined3d_texture_load(src_surface->container, context, FALSE);
3283 /* Activate the destination context, set it up for blitting */
3284 context_apply_blit_state(context, device);
3286 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
3287 surface_translate_drawable_coords(dst_surface, context->win_handle, &dst_rect);
3289 device->blitter->set_shader(device->blit_priv, context, src_surface, NULL);
3291 if (alpha_test)
3293 gl_info->gl_ops.gl.p_glEnable(GL_ALPHA_TEST);
3294 checkGLcall("glEnable(GL_ALPHA_TEST)");
3296 /* For P8 surfaces, the alpha component contains the palette index.
3297 * Which means that the colorkey is one of the palette entries. In
3298 * other cases pixels that should be masked away have alpha set to 0. */
3299 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT)
3300 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL,
3301 (float)src_surface->container->async.src_blt_color_key.color_space_low_value / 255.0f);
3302 else
3303 gl_info->gl_ops.gl.p_glAlphaFunc(GL_NOTEQUAL, 0.0f);
3304 checkGLcall("glAlphaFunc");
3306 else
3308 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3309 checkGLcall("glDisable(GL_ALPHA_TEST)");
3312 draw_textured_quad(src_surface, context, &src_rect, &dst_rect, filter);
3314 if (alpha_test)
3316 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3317 checkGLcall("glDisable(GL_ALPHA_TEST)");
3320 /* Leave the opengl state valid for blitting */
3321 device->blitter->unset_shader(context->gl_info);
3323 if (wined3d_settings.strict_draw_ordering
3324 || (dst_surface->container->swapchain
3325 && dst_surface->container->swapchain->front_buffer == dst_surface->container))
3326 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3328 context_release(context);
3331 HRESULT surface_color_fill(struct wined3d_surface *s, const RECT *rect, const struct wined3d_color *color)
3333 struct wined3d_device *device = s->resource.device;
3334 const struct blit_shader *blitter;
3336 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, WINED3D_BLIT_OP_COLOR_FILL,
3337 NULL, 0, 0, NULL, rect, s->resource.usage, s->resource.pool, s->resource.format);
3338 if (!blitter)
3340 FIXME("No blitter is capable of performing the requested color fill operation.\n");
3341 return WINED3DERR_INVALIDCALL;
3344 return blitter->color_fill(device, s, rect, color);
3347 static HRESULT surface_blt_special(struct wined3d_surface *dst_surface, const RECT *dst_rect,
3348 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags, const WINEDDBLTFX *DDBltFx,
3349 enum wined3d_texture_filter_type filter)
3351 struct wined3d_device *device = dst_surface->resource.device;
3352 const struct wined3d_surface *rt = wined3d_rendertarget_view_get_surface(device->fb.render_targets[0]);
3353 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
3355 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, blt_fx %p, filter %s.\n",
3356 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
3357 flags, DDBltFx, debug_d3dtexturefiltertype(filter));
3359 /* Get the swapchain. One of the surfaces has to be a primary surface */
3360 if (dst_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3362 WARN("Destination is in sysmem, rejecting gl blt\n");
3363 return WINED3DERR_INVALIDCALL;
3366 dst_swapchain = dst_surface->container->swapchain;
3368 if (src_surface)
3370 if (src_surface->resource.pool == WINED3D_POOL_SYSTEM_MEM)
3372 WARN("Src is in sysmem, rejecting gl blt\n");
3373 return WINED3DERR_INVALIDCALL;
3376 src_swapchain = src_surface->container->swapchain;
3378 else
3380 src_swapchain = NULL;
3383 /* Early sort out of cases where no render target is used */
3384 if (!dst_swapchain && !src_swapchain && src_surface != rt && dst_surface != rt)
3386 TRACE("No surface is render target, not using hardware blit.\n");
3387 return WINED3DERR_INVALIDCALL;
3390 /* No destination color keying supported */
3391 if (flags & (WINEDDBLT_KEYDEST | WINEDDBLT_KEYDESTOVERRIDE))
3393 /* Can we support that with glBlendFunc if blitting to the frame buffer? */
3394 TRACE("Destination color key not supported in accelerated Blit, falling back to software\n");
3395 return WINED3DERR_INVALIDCALL;
3398 if (dst_swapchain && dst_swapchain == src_swapchain)
3400 FIXME("Implement hardware blit between two surfaces on the same swapchain\n");
3401 return WINED3DERR_INVALIDCALL;
3404 if (dst_swapchain && src_swapchain)
3406 FIXME("Implement hardware blit between two different swapchains\n");
3407 return WINED3DERR_INVALIDCALL;
3410 if (dst_swapchain)
3412 /* Handled with regular texture -> swapchain blit */
3413 if (src_surface == rt)
3414 TRACE("Blit from active render target to a swapchain\n");
3416 else if (src_swapchain && dst_surface == rt)
3418 FIXME("Implement blit from a swapchain to the active render target\n");
3419 return WINED3DERR_INVALIDCALL;
3422 if ((src_swapchain || src_surface == rt) && !dst_swapchain)
3424 /* Blit from render target to texture */
3425 BOOL stretchx;
3427 /* P8 read back is not implemented */
3428 if (src_surface->resource.format->id == WINED3DFMT_P8_UINT
3429 || dst_surface->resource.format->id == WINED3DFMT_P8_UINT)
3431 TRACE("P8 read back not supported by frame buffer to texture blit\n");
3432 return WINED3DERR_INVALIDCALL;
3435 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYSRCOVERRIDE))
3437 TRACE("Color keying not supported by frame buffer to texture blit\n");
3438 return WINED3DERR_INVALIDCALL;
3439 /* Destination color key is checked above */
3442 if (dst_rect->right - dst_rect->left != src_rect->right - src_rect->left)
3443 stretchx = TRUE;
3444 else
3445 stretchx = FALSE;
3447 /* Blt is a pretty powerful call, while glCopyTexSubImage2D is not. glCopyTexSubImage cannot
3448 * flip the image nor scale it.
3450 * -> If the app asks for an unscaled, upside down copy, just perform one glCopyTexSubImage2D call
3451 * -> If the app wants an image width an unscaled width, copy it line per line
3452 * -> If the app wants an image that is scaled on the x axis, and the destination rectangle is smaller
3453 * than the frame buffer, draw an upside down scaled image onto the fb, read it back and restore the
3454 * back buffer. This is slower than reading line per line, thus not used for flipping
3455 * -> If the app wants a scaled image with a dest rect that is bigger than the fb, it has to be copied
3456 * pixel by pixel. */
3457 if (!stretchx || dst_rect->right - dst_rect->left > src_surface->resource.width
3458 || dst_rect->bottom - dst_rect->top > src_surface->resource.height)
3460 TRACE("No stretching in x direction, using direct framebuffer -> texture copy.\n");
3461 fb_copy_to_texture_direct(dst_surface, src_surface, src_rect, dst_rect, filter);
3463 else
3465 TRACE("Using hardware stretching to flip / stretch the texture.\n");
3466 fb_copy_to_texture_hwstretch(dst_surface, src_surface, src_rect, dst_rect, filter);
3469 surface_evict_sysmem(dst_surface);
3471 return WINED3D_OK;
3474 /* Default: Fall back to the generic blt. Not an error, a TRACE is enough */
3475 TRACE("Didn't find any usable render target setup for hw blit, falling back to software\n");
3476 return WINED3DERR_INVALIDCALL;
3479 /* Context activation is done by the caller. */
3480 static void surface_depth_blt(const struct wined3d_surface *surface, struct wined3d_context *context,
3481 GLuint texture, GLint x, GLint y, GLsizei w, GLsizei h, GLenum target)
3483 struct wined3d_device *device = surface->resource.device;
3484 const struct wined3d_gl_info *gl_info = context->gl_info;
3485 GLint compare_mode = GL_NONE;
3486 struct blt_info info;
3487 GLint old_binding = 0;
3488 RECT rect;
3490 gl_info->gl_ops.gl.p_glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_VIEWPORT_BIT);
3492 gl_info->gl_ops.gl.p_glDisable(GL_CULL_FACE);
3493 gl_info->gl_ops.gl.p_glDisable(GL_BLEND);
3494 gl_info->gl_ops.gl.p_glDisable(GL_ALPHA_TEST);
3495 gl_info->gl_ops.gl.p_glDisable(GL_SCISSOR_TEST);
3496 gl_info->gl_ops.gl.p_glDisable(GL_STENCIL_TEST);
3497 gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
3498 gl_info->gl_ops.gl.p_glDepthFunc(GL_ALWAYS);
3499 gl_info->gl_ops.gl.p_glDepthMask(GL_TRUE);
3500 gl_info->gl_ops.gl.p_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
3501 gl_info->gl_ops.gl.p_glViewport(x, y, w, h);
3502 gl_info->gl_ops.gl.p_glDepthRange(0.0, 1.0);
3504 SetRect(&rect, 0, h, w, 0);
3505 surface_get_blt_info(target, &rect, surface->pow2Width, surface->pow2Height, &info);
3506 context_active_texture(context, context->gl_info, 0);
3507 gl_info->gl_ops.gl.p_glGetIntegerv(info.binding, &old_binding);
3508 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, texture);
3509 if (gl_info->supported[ARB_SHADOW])
3511 gl_info->gl_ops.gl.p_glGetTexParameteriv(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, &compare_mode);
3512 if (compare_mode != GL_NONE)
3513 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
3516 device->shader_backend->shader_select_depth_blt(device->shader_priv,
3517 gl_info, info.tex_type, &surface->ds_current_size);
3519 gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
3520 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[0]);
3521 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, -1.0f);
3522 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[1]);
3523 gl_info->gl_ops.gl.p_glVertex2f(1.0f, -1.0f);
3524 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[2]);
3525 gl_info->gl_ops.gl.p_glVertex2f(-1.0f, 1.0f);
3526 gl_info->gl_ops.gl.p_glTexCoord3fv(info.coords[3]);
3527 gl_info->gl_ops.gl.p_glVertex2f(1.0f, 1.0f);
3528 gl_info->gl_ops.gl.p_glEnd();
3530 if (compare_mode != GL_NONE)
3531 gl_info->gl_ops.gl.p_glTexParameteri(info.bind_target, GL_TEXTURE_COMPARE_MODE_ARB, compare_mode);
3532 gl_info->gl_ops.gl.p_glBindTexture(info.bind_target, old_binding);
3534 gl_info->gl_ops.gl.p_glPopAttrib();
3536 device->shader_backend->shader_deselect_depth_blt(device->shader_priv, gl_info);
3539 void surface_modify_ds_location(struct wined3d_surface *surface,
3540 DWORD location, UINT w, UINT h)
3542 TRACE("surface %p, new location %#x, w %u, h %u.\n", surface, location, w, h);
3544 if (((surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && !(location & WINED3D_LOCATION_TEXTURE_RGB))
3545 || (!(surface->locations & WINED3D_LOCATION_TEXTURE_RGB) && (location & WINED3D_LOCATION_TEXTURE_RGB)))
3546 wined3d_texture_set_dirty(surface->container);
3548 surface->ds_current_size.cx = w;
3549 surface->ds_current_size.cy = h;
3550 surface->locations = location;
3553 /* Context activation is done by the caller. */
3554 void surface_load_ds_location(struct wined3d_surface *surface, struct wined3d_context *context, DWORD location)
3556 const struct wined3d_gl_info *gl_info = context->gl_info;
3557 struct wined3d_device *device = surface->resource.device;
3558 GLsizei w, h;
3560 TRACE("surface %p, context %p, new location %#x.\n", surface, context, location);
3562 /* TODO: Make this work for modes other than FBO */
3563 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return;
3565 if (!(surface->locations & location))
3567 w = surface->ds_current_size.cx;
3568 h = surface->ds_current_size.cy;
3569 surface->ds_current_size.cx = 0;
3570 surface->ds_current_size.cy = 0;
3572 else
3574 w = surface->resource.width;
3575 h = surface->resource.height;
3578 if (surface->ds_current_size.cx == surface->resource.width
3579 && surface->ds_current_size.cy == surface->resource.height)
3581 TRACE("Location (%#x) is already up to date.\n", location);
3582 return;
3585 if (surface->current_renderbuffer)
3587 FIXME("Not supported with fixed up depth stencil.\n");
3588 return;
3591 if (surface->locations & WINED3D_LOCATION_DISCARDED)
3593 TRACE("Surface was discarded, no need copy data.\n");
3594 switch (location)
3596 case WINED3D_LOCATION_TEXTURE_RGB:
3597 wined3d_texture_prepare_texture(surface->container, context, FALSE);
3598 break;
3599 case WINED3D_LOCATION_RB_MULTISAMPLE:
3600 surface_prepare_rb(surface, gl_info, TRUE);
3601 break;
3602 case WINED3D_LOCATION_RB_RESOLVED:
3603 surface_prepare_rb(surface, gl_info, FALSE);
3604 break;
3605 case WINED3D_LOCATION_DRAWABLE:
3606 /* Nothing to do */
3607 break;
3608 default:
3609 FIXME("Unhandled location %#x\n", location);
3611 surface->locations &= ~WINED3D_LOCATION_DISCARDED;
3612 surface->locations |= location;
3613 surface->ds_current_size.cx = surface->resource.width;
3614 surface->ds_current_size.cy = surface->resource.height;
3615 return;
3618 if (!surface->locations)
3620 FIXME("No up to date depth stencil location.\n");
3621 surface->locations |= location;
3622 surface->ds_current_size.cx = surface->resource.width;
3623 surface->ds_current_size.cy = surface->resource.height;
3624 return;
3627 if (location == WINED3D_LOCATION_TEXTURE_RGB)
3629 GLint old_binding = 0;
3630 GLenum bind_target;
3632 /* The render target is allowed to be smaller than the depth/stencil
3633 * buffer, so the onscreen depth/stencil buffer is potentially smaller
3634 * than the offscreen surface. Don't overwrite the offscreen surface
3635 * with undefined data. */
3636 w = min(w, context->swapchain->desc.backbuffer_width);
3637 h = min(h, context->swapchain->desc.backbuffer_height);
3639 TRACE("Copying onscreen depth buffer to depth texture.\n");
3641 if (!device->depth_blt_texture)
3642 gl_info->gl_ops.gl.p_glGenTextures(1, &device->depth_blt_texture);
3644 /* Note that we use depth_blt here as well, rather than glCopyTexImage2D
3645 * directly on the FBO texture. That's because we need to flip. */
3646 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3647 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3648 NULL, WINED3D_LOCATION_DRAWABLE);
3649 if (surface->texture_target == GL_TEXTURE_RECTANGLE_ARB)
3651 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &old_binding);
3652 bind_target = GL_TEXTURE_RECTANGLE_ARB;
3654 else
3656 gl_info->gl_ops.gl.p_glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_binding);
3657 bind_target = GL_TEXTURE_2D;
3659 gl_info->gl_ops.gl.p_glBindTexture(bind_target, device->depth_blt_texture);
3660 /* We use GL_DEPTH_COMPONENT instead of the surface's specific
3661 * internal format, because the internal format might include stencil
3662 * data. In principle we should copy stencil data as well, but unless
3663 * the driver supports stencil export it's hard to do, and doesn't
3664 * seem to be needed in practice. If the hardware doesn't support
3665 * writing stencil data, the glCopyTexImage2D() call might trigger
3666 * software fallbacks. */
3667 gl_info->gl_ops.gl.p_glCopyTexImage2D(bind_target, 0, GL_DEPTH_COMPONENT, 0, 0, w, h, 0);
3668 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3669 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3670 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3671 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3672 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
3673 gl_info->gl_ops.gl.p_glTexParameteri(bind_target, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
3674 gl_info->gl_ops.gl.p_glBindTexture(bind_target, old_binding);
3676 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3677 NULL, surface, WINED3D_LOCATION_TEXTURE_RGB);
3678 context_set_draw_buffer(context, GL_NONE);
3680 /* Do the actual blit */
3681 surface_depth_blt(surface, context, device->depth_blt_texture, 0, 0, w, h, bind_target);
3682 checkGLcall("depth_blt");
3684 context_invalidate_state(context, STATE_FRAMEBUFFER);
3686 if (wined3d_settings.strict_draw_ordering)
3687 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3689 else if (location == WINED3D_LOCATION_DRAWABLE)
3691 TRACE("Copying depth texture to onscreen depth buffer.\n");
3693 context_apply_fbo_state_blit(context, GL_FRAMEBUFFER,
3694 surface_from_resource(wined3d_texture_get_sub_resource(context->swapchain->front_buffer, 0)),
3695 NULL, WINED3D_LOCATION_DRAWABLE);
3696 surface_depth_blt(surface, context, surface->container->texture_rgb.name,
3697 0, surface->pow2Height - h, w, h, surface->texture_target);
3698 checkGLcall("depth_blt");
3700 context_invalidate_state(context, STATE_FRAMEBUFFER);
3702 if (wined3d_settings.strict_draw_ordering)
3703 gl_info->gl_ops.gl.p_glFlush(); /* Flush to ensure ordering across contexts. */
3705 else
3707 ERR("Invalid location (%#x) specified.\n", location);
3710 surface->locations |= location;
3711 surface->ds_current_size.cx = surface->resource.width;
3712 surface->ds_current_size.cy = surface->resource.height;
3715 void surface_validate_location(struct wined3d_surface *surface, DWORD location)
3717 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3719 surface->locations |= location;
3722 void surface_invalidate_location(struct wined3d_surface *surface, DWORD location)
3724 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
3726 if (location & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3727 wined3d_texture_set_dirty(surface->container);
3728 surface->locations &= ~location;
3730 if (!surface->locations)
3731 ERR("Surface %p does not have any up to date location.\n", surface);
3734 static DWORD resource_access_from_location(DWORD location)
3736 switch (location)
3738 case WINED3D_LOCATION_SYSMEM:
3739 case WINED3D_LOCATION_USER_MEMORY:
3740 case WINED3D_LOCATION_DIB:
3741 case WINED3D_LOCATION_BUFFER:
3742 return WINED3D_RESOURCE_ACCESS_CPU;
3744 case WINED3D_LOCATION_DRAWABLE:
3745 case WINED3D_LOCATION_TEXTURE_SRGB:
3746 case WINED3D_LOCATION_TEXTURE_RGB:
3747 case WINED3D_LOCATION_RB_MULTISAMPLE:
3748 case WINED3D_LOCATION_RB_RESOLVED:
3749 return WINED3D_RESOURCE_ACCESS_GPU;
3751 default:
3752 FIXME("Unhandled location %#x.\n", location);
3753 return 0;
3757 static void surface_copy_simple_location(struct wined3d_surface *surface, DWORD location)
3759 struct wined3d_device *device = surface->resource.device;
3760 struct wined3d_context *context;
3761 const struct wined3d_gl_info *gl_info;
3762 struct wined3d_bo_address dst, src;
3763 UINT size = surface->resource.size;
3765 surface_get_memory(surface, &dst, location);
3766 surface_get_memory(surface, &src, surface->locations);
3768 if (dst.buffer_object)
3770 context = context_acquire(device, NULL);
3771 gl_info = context->gl_info;
3772 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, dst.buffer_object));
3773 GL_EXTCALL(glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, src.addr));
3774 GL_EXTCALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
3775 checkGLcall("Upload PBO");
3776 context_release(context);
3777 return;
3779 if (src.buffer_object)
3781 context = context_acquire(device, NULL);
3782 gl_info = context->gl_info;
3783 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, src.buffer_object));
3784 GL_EXTCALL(glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, size, dst.addr));
3785 GL_EXTCALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
3786 checkGLcall("Download PBO");
3787 context_release(context);
3788 return;
3790 memcpy(dst.addr, src.addr, size);
3793 static void surface_load_sysmem(struct wined3d_surface *surface,
3794 const struct wined3d_gl_info *gl_info, DWORD dst_location)
3796 if (surface->locations & surface_simple_locations)
3798 surface_copy_simple_location(surface, dst_location);
3799 return;
3802 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED))
3803 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
3805 /* Download the surface to system memory. */
3806 if (surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | WINED3D_LOCATION_TEXTURE_SRGB))
3808 struct wined3d_device *device = surface->resource.device;
3809 struct wined3d_context *context;
3811 /* TODO: Use already acquired context when possible. */
3812 context = context_acquire(device, NULL);
3814 wined3d_texture_bind_and_dirtify(surface->container, context,
3815 !(surface->locations & WINED3D_LOCATION_TEXTURE_RGB));
3816 surface_download_data(surface, gl_info, dst_location);
3818 context_release(context);
3820 return;
3823 if (surface->locations & WINED3D_LOCATION_DRAWABLE)
3825 read_from_framebuffer(surface, dst_location);
3826 return;
3829 FIXME("Can't load surface %p with location flags %s into sysmem.\n",
3830 surface, wined3d_debug_location(surface->locations));
3833 static HRESULT surface_load_drawable(struct wined3d_surface *surface,
3834 const struct wined3d_gl_info *gl_info)
3836 RECT r;
3838 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO
3839 && wined3d_resource_is_offscreen(&surface->container->resource))
3841 ERR("Trying to load offscreen surface into WINED3D_LOCATION_DRAWABLE.\n");
3842 return WINED3DERR_INVALIDCALL;
3845 surface_get_rect(surface, NULL, &r);
3846 surface_load_location(surface, WINED3D_LOCATION_TEXTURE_RGB);
3847 surface_blt_to_drawable(surface->resource.device,
3848 WINED3D_TEXF_POINT, FALSE, surface, &r, surface, &r);
3850 return WINED3D_OK;
3853 static HRESULT surface_load_texture(struct wined3d_surface *surface,
3854 const struct wined3d_gl_info *gl_info, BOOL srgb)
3856 RECT src_rect = {0, 0, surface->resource.width, surface->resource.height};
3857 struct wined3d_device *device = surface->resource.device;
3858 const struct wined3d_color_key_conversion *conversion;
3859 struct wined3d_texture *texture = surface->container;
3860 struct wined3d_context *context;
3861 UINT width, src_pitch, dst_pitch;
3862 struct wined3d_bo_address data;
3863 struct wined3d_format format;
3864 POINT dst_point = {0, 0};
3865 BYTE *mem = NULL;
3867 if (wined3d_settings.offscreen_rendering_mode != ORM_FBO
3868 && wined3d_resource_is_offscreen(&texture->resource)
3869 && (surface->locations & WINED3D_LOCATION_DRAWABLE))
3871 surface_load_fb_texture(surface, srgb);
3873 return WINED3D_OK;
3876 if (surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | WINED3D_LOCATION_TEXTURE_RGB)
3877 && (surface->container->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB)
3878 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3879 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3880 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3882 if (srgb)
3883 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_RGB,
3884 &src_rect, surface, WINED3D_LOCATION_TEXTURE_SRGB, &src_rect);
3885 else
3886 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, WINED3D_LOCATION_TEXTURE_SRGB,
3887 &src_rect, surface, WINED3D_LOCATION_TEXTURE_RGB, &src_rect);
3889 return WINED3D_OK;
3892 if (surface->locations & (WINED3D_LOCATION_RB_MULTISAMPLE | WINED3D_LOCATION_RB_RESOLVED)
3893 && (!srgb || (surface->container->resource.format_flags & WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB))
3894 && fbo_blit_supported(gl_info, WINED3D_BLIT_OP_COLOR_BLIT,
3895 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format,
3896 NULL, surface->resource.usage, surface->resource.pool, surface->resource.format))
3898 DWORD src_location = surface->locations & WINED3D_LOCATION_RB_RESOLVED ?
3899 WINED3D_LOCATION_RB_RESOLVED : WINED3D_LOCATION_RB_MULTISAMPLE;
3900 DWORD dst_location = srgb ? WINED3D_LOCATION_TEXTURE_SRGB : WINED3D_LOCATION_TEXTURE_RGB;
3901 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
3903 surface_blt_fbo(device, WINED3D_TEXF_POINT, surface, src_location,
3904 &rect, surface, dst_location, &rect);
3906 return WINED3D_OK;
3909 /* Upload from system memory */
3911 if (srgb)
3913 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_RGB | surface->resource.map_binding))
3914 == WINED3D_LOCATION_TEXTURE_RGB)
3916 /* Performance warning... */
3917 FIXME("Downloading RGB surface %p to reload it as sRGB.\n", surface);
3918 surface_prepare_map_memory(surface);
3919 surface_load_location(surface, surface->resource.map_binding);
3922 else
3924 if ((surface->locations & (WINED3D_LOCATION_TEXTURE_SRGB | surface->resource.map_binding))
3925 == WINED3D_LOCATION_TEXTURE_SRGB)
3927 /* Performance warning... */
3928 FIXME("Downloading sRGB surface %p to reload it as RGB.\n", surface);
3929 surface_prepare_map_memory(surface);
3930 surface_load_location(surface, surface->resource.map_binding);
3934 if (!(surface->locations & surface_simple_locations))
3936 WARN("Trying to load a texture from sysmem, but no simple location is valid.\n");
3937 /* Lets hope we get it from somewhere... */
3938 surface_prepare_system_memory(surface);
3939 surface_load_location(surface, WINED3D_LOCATION_SYSMEM);
3942 /* TODO: Use already acquired context when possible. */
3943 context = context_acquire(device, NULL);
3945 wined3d_texture_prepare_texture(texture, context, srgb);
3946 wined3d_texture_bind_and_dirtify(texture, context, srgb);
3948 width = surface->resource.width;
3949 src_pitch = wined3d_surface_get_pitch(surface);
3951 format = *texture->resource.format;
3952 if ((conversion = wined3d_format_get_color_key_conversion(texture, TRUE)))
3953 format = *wined3d_get_format(gl_info, conversion->dst_format);
3955 /* Don't use PBOs for converted surfaces. During PBO conversion we look at
3956 * WINED3D_TEXTURE_CONVERTED but it isn't set (yet) in all cases it is
3957 * getting called. */
3958 if ((format.convert || conversion) && surface->pbo)
3960 TRACE("Removing the pbo attached to surface %p.\n", surface);
3962 if (surface->flags & SFLAG_DIBSECTION)
3963 surface->resource.map_binding = WINED3D_LOCATION_DIB;
3964 else
3965 surface->resource.map_binding = WINED3D_LOCATION_SYSMEM;
3967 surface_prepare_map_memory(surface);
3968 surface_load_location(surface, surface->resource.map_binding);
3969 surface_remove_pbo(surface, gl_info);
3972 surface_get_memory(surface, &data, surface->locations);
3973 if (format.convert)
3975 /* This code is entered for texture formats which need a fixup. */
3976 UINT height = surface->resource.height;
3978 format.byte_count = format.conv_byte_count;
3979 dst_pitch = wined3d_format_calculate_pitch(&format, width);
3981 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
3983 ERR("Out of memory (%u).\n", dst_pitch * height);
3984 context_release(context);
3985 return E_OUTOFMEMORY;
3987 format.convert(data.addr, mem, src_pitch, src_pitch * height,
3988 dst_pitch, dst_pitch * height, width, height, 1);
3989 src_pitch = dst_pitch;
3990 data.addr = mem;
3992 else if (conversion)
3994 /* This code is only entered for color keying fixups */
3995 struct wined3d_palette *palette = NULL;
3996 UINT height = surface->resource.height;
3998 dst_pitch = wined3d_format_calculate_pitch(&format, width);
3999 dst_pitch = (dst_pitch + device->surface_alignment - 1) & ~(device->surface_alignment - 1);
4001 if (!(mem = HeapAlloc(GetProcessHeap(), 0, dst_pitch * height)))
4003 ERR("Out of memory (%u).\n", dst_pitch * height);
4004 context_release(context);
4005 return E_OUTOFMEMORY;
4007 if (texture->swapchain && texture->swapchain->palette)
4008 palette = texture->swapchain->palette;
4009 conversion->convert(data.addr, src_pitch, mem, dst_pitch,
4010 width, height, palette, &texture->async.gl_color_key);
4011 src_pitch = dst_pitch;
4012 data.addr = mem;
4015 wined3d_surface_upload_data(surface, gl_info, &format, &src_rect,
4016 src_pitch, &dst_point, srgb, wined3d_const_bo_address(&data));
4018 context_release(context);
4020 HeapFree(GetProcessHeap(), 0, mem);
4022 return WINED3D_OK;
4025 static void surface_multisample_resolve(struct wined3d_surface *surface)
4027 RECT rect = {0, 0, surface->resource.width, surface->resource.height};
4029 if (!(surface->locations & WINED3D_LOCATION_RB_MULTISAMPLE))
4030 ERR("Trying to resolve multisampled surface %p, but location WINED3D_LOCATION_RB_MULTISAMPLE not current.\n",
4031 surface);
4033 surface_blt_fbo(surface->resource.device, WINED3D_TEXF_POINT,
4034 surface, WINED3D_LOCATION_RB_MULTISAMPLE, &rect, surface, WINED3D_LOCATION_RB_RESOLVED, &rect);
4037 HRESULT surface_load_location(struct wined3d_surface *surface, DWORD location)
4039 struct wined3d_device *device = surface->resource.device;
4040 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
4041 HRESULT hr;
4043 TRACE("surface %p, location %s.\n", surface, wined3d_debug_location(location));
4045 if (surface->resource.usage & WINED3DUSAGE_DEPTHSTENCIL)
4047 if (location == WINED3D_LOCATION_TEXTURE_RGB
4048 && surface->locations & (WINED3D_LOCATION_DRAWABLE | WINED3D_LOCATION_DISCARDED))
4050 struct wined3d_context *context = context_acquire(device, NULL);
4051 surface_load_ds_location(surface, context, location);
4052 context_release(context);
4053 return WINED3D_OK;
4055 else if (location & surface->locations
4056 && surface->container->resource.draw_binding != WINED3D_LOCATION_DRAWABLE)
4058 /* Already up to date, nothing to do. */
4059 return WINED3D_OK;
4061 else
4063 FIXME("Unimplemented copy from %s to %s for depth/stencil buffers.\n",
4064 wined3d_debug_location(surface->locations), wined3d_debug_location(location));
4065 return WINED3DERR_INVALIDCALL;
4069 if (surface->locations & location)
4071 TRACE("Location already up to date.\n");
4072 return WINED3D_OK;
4075 if (WARN_ON(d3d_surface))
4077 DWORD required_access = resource_access_from_location(location);
4078 if ((surface->resource.access_flags & required_access) != required_access)
4079 WARN("Operation requires %#x access, but surface only has %#x.\n",
4080 required_access, surface->resource.access_flags);
4083 if (!surface->locations)
4085 ERR("Surface %p does not have any up to date location.\n", surface);
4086 surface->flags |= SFLAG_LOST;
4087 return WINED3DERR_DEVICELOST;
4090 switch (location)
4092 case WINED3D_LOCATION_DIB:
4093 case WINED3D_LOCATION_USER_MEMORY:
4094 case WINED3D_LOCATION_SYSMEM:
4095 case WINED3D_LOCATION_BUFFER:
4096 surface_load_sysmem(surface, gl_info, location);
4097 break;
4099 case WINED3D_LOCATION_DRAWABLE:
4100 if (FAILED(hr = surface_load_drawable(surface, gl_info)))
4101 return hr;
4102 break;
4104 case WINED3D_LOCATION_RB_RESOLVED:
4105 surface_multisample_resolve(surface);
4106 break;
4108 case WINED3D_LOCATION_TEXTURE_RGB:
4109 case WINED3D_LOCATION_TEXTURE_SRGB:
4110 if (FAILED(hr = surface_load_texture(surface, gl_info, location == WINED3D_LOCATION_TEXTURE_SRGB)))
4111 return hr;
4112 break;
4114 default:
4115 ERR("Don't know how to handle location %#x.\n", location);
4116 break;
4119 surface_validate_location(surface, location);
4121 if (location != WINED3D_LOCATION_SYSMEM && (surface->locations & WINED3D_LOCATION_SYSMEM))
4122 surface_evict_sysmem(surface);
4124 return WINED3D_OK;
4127 static HRESULT ffp_blit_alloc(struct wined3d_device *device) { return WINED3D_OK; }
4128 /* Context activation is done by the caller. */
4129 static void ffp_blit_free(struct wined3d_device *device) { }
4131 /* Context activation is done by the caller. */
4132 static HRESULT ffp_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
4133 const struct wined3d_color_key *color_key)
4135 const struct wined3d_gl_info *gl_info = context->gl_info;
4137 gl_info->gl_ops.gl.p_glEnable(surface->container->target);
4138 checkGLcall("glEnable(target)");
4140 return WINED3D_OK;
4143 /* Context activation is done by the caller. */
4144 static void ffp_blit_unset(const struct wined3d_gl_info *gl_info)
4146 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_2D);
4147 checkGLcall("glDisable(GL_TEXTURE_2D)");
4148 if (gl_info->supported[ARB_TEXTURE_CUBE_MAP])
4150 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_CUBE_MAP_ARB);
4151 checkGLcall("glDisable(GL_TEXTURE_CUBE_MAP_ARB)");
4153 if (gl_info->supported[ARB_TEXTURE_RECTANGLE])
4155 gl_info->gl_ops.gl.p_glDisable(GL_TEXTURE_RECTANGLE_ARB);
4156 checkGLcall("glDisable(GL_TEXTURE_RECTANGLE_ARB)");
4160 static BOOL ffp_blit_supported(const struct wined3d_gl_info *gl_info,
4161 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
4162 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4163 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4165 if (src_pool == WINED3D_POOL_SYSTEM_MEM || dst_pool == WINED3D_POOL_SYSTEM_MEM)
4167 TRACE("Source or destination is in system memory.\n");
4168 return FALSE;
4171 switch (blit_op)
4173 case WINED3D_BLIT_OP_COLOR_BLIT_CKEY:
4174 if (d3d_info->shader_color_key)
4176 TRACE("Color keying requires converted textures.\n");
4177 return FALSE;
4179 case WINED3D_BLIT_OP_COLOR_BLIT:
4180 if (TRACE_ON(d3d_surface) && TRACE_ON(d3d))
4182 TRACE("Checking support for fixup:\n");
4183 dump_color_fixup_desc(src_format->color_fixup);
4186 /* We only support identity conversions. */
4187 if (!is_identity_fixup(src_format->color_fixup)
4188 || !is_identity_fixup(dst_format->color_fixup))
4190 TRACE("Fixups are not supported.\n");
4191 return FALSE;
4194 if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4196 TRACE("Can only blit to render targets.\n");
4197 return FALSE;
4199 return TRUE;
4201 case WINED3D_BLIT_OP_COLOR_FILL:
4202 if (wined3d_settings.offscreen_rendering_mode == ORM_FBO)
4204 if (!((dst_format->flags[WINED3D_GL_RES_TYPE_TEX_2D] & WINED3DFMT_FLAG_FBO_ATTACHABLE)
4205 || (dst_usage & WINED3DUSAGE_RENDERTARGET)))
4206 return FALSE;
4208 else if (!(dst_usage & WINED3DUSAGE_RENDERTARGET))
4210 TRACE("Color fill not supported\n");
4211 return FALSE;
4214 /* FIXME: We should reject color fills on formats with fixups,
4215 * but this would break P8 color fills for example. */
4217 return TRUE;
4219 case WINED3D_BLIT_OP_DEPTH_FILL:
4220 return TRUE;
4222 default:
4223 TRACE("Unsupported blit_op=%d\n", blit_op);
4224 return FALSE;
4228 static HRESULT ffp_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4229 const RECT *dst_rect, const struct wined3d_color *color)
4231 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4232 struct wined3d_rendertarget_view *view;
4233 struct wined3d_fb_state fb = {&view, NULL};
4234 HRESULT hr;
4236 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4237 NULL, &wined3d_null_parent_ops, &view)))
4239 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4240 return hr;
4243 device_clear_render_targets(device, 1, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_TARGET, color, 0.0f, 0);
4244 wined3d_rendertarget_view_decref(view);
4246 return WINED3D_OK;
4249 static HRESULT ffp_blit_depth_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4250 const RECT *dst_rect, float depth)
4252 const RECT draw_rect = {0, 0, dst_surface->resource.width, dst_surface->resource.height};
4253 struct wined3d_fb_state fb = {NULL, NULL};
4254 HRESULT hr;
4256 if (FAILED(hr = wined3d_rendertarget_view_create_from_surface(dst_surface,
4257 NULL, &wined3d_null_parent_ops, &fb.depth_stencil)))
4259 ERR("Failed to create rendertarget view, hr %#x.\n", hr);
4260 return hr;
4263 device_clear_render_targets(device, 0, &fb, 1, dst_rect, &draw_rect, WINED3DCLEAR_ZBUFFER, 0, depth, 0);
4264 wined3d_rendertarget_view_decref(fb.depth_stencil);
4266 return WINED3D_OK;
4269 static void ffp_blit_blit_surface(struct wined3d_device *device, DWORD filter,
4270 struct wined3d_surface *src_surface, const RECT *src_rect,
4271 struct wined3d_surface *dst_surface, const RECT *dst_rect,
4272 const struct wined3d_color_key *color_key)
4274 /* Blit from offscreen surface to render target */
4275 struct wined3d_color_key old_blt_key = src_surface->container->async.src_blt_color_key;
4276 DWORD old_color_key_flags = src_surface->container->async.color_key_flags;
4278 TRACE("Blt from surface %p to rendertarget %p\n", src_surface, dst_surface);
4280 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT, color_key);
4282 surface_blt_to_drawable(device, filter,
4283 !!color_key, src_surface, src_rect, dst_surface, dst_rect);
4285 /* Restore the color key parameters */
4286 wined3d_texture_set_color_key(src_surface->container, WINED3D_CKEY_SRC_BLT,
4287 (old_color_key_flags & WINED3D_CKEY_SRC_BLT) ? &old_blt_key : NULL);
4289 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
4290 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
4293 const struct blit_shader ffp_blit = {
4294 ffp_blit_alloc,
4295 ffp_blit_free,
4296 ffp_blit_set,
4297 ffp_blit_unset,
4298 ffp_blit_supported,
4299 ffp_blit_color_fill,
4300 ffp_blit_depth_fill,
4301 ffp_blit_blit_surface,
4304 static HRESULT cpu_blit_alloc(struct wined3d_device *device)
4306 return WINED3D_OK;
4309 /* Context activation is done by the caller. */
4310 static void cpu_blit_free(struct wined3d_device *device)
4314 /* Context activation is done by the caller. */
4315 static HRESULT cpu_blit_set(void *blit_priv, struct wined3d_context *context, const struct wined3d_surface *surface,
4316 const struct wined3d_color_key *color_key)
4318 return WINED3D_OK;
4321 /* Context activation is done by the caller. */
4322 static void cpu_blit_unset(const struct wined3d_gl_info *gl_info)
4326 static BOOL cpu_blit_supported(const struct wined3d_gl_info *gl_info,
4327 const struct wined3d_d3d_info *d3d_info, enum wined3d_blit_op blit_op,
4328 const RECT *src_rect, DWORD src_usage, enum wined3d_pool src_pool, const struct wined3d_format *src_format,
4329 const RECT *dst_rect, DWORD dst_usage, enum wined3d_pool dst_pool, const struct wined3d_format *dst_format)
4331 if (blit_op == WINED3D_BLIT_OP_COLOR_FILL)
4333 return TRUE;
4336 return FALSE;
4339 static HRESULT surface_cpu_blt_compressed(const BYTE *src_data, BYTE *dst_data,
4340 UINT src_pitch, UINT dst_pitch, UINT update_w, UINT update_h,
4341 const struct wined3d_format *format, DWORD flags, const WINEDDBLTFX *fx)
4343 UINT row_block_count;
4344 const BYTE *src_row;
4345 BYTE *dst_row;
4346 UINT x, y;
4348 src_row = src_data;
4349 dst_row = dst_data;
4351 row_block_count = (update_w + format->block_width - 1) / format->block_width;
4353 if (!flags)
4355 for (y = 0; y < update_h; y += format->block_height)
4357 memcpy(dst_row, src_row, row_block_count * format->block_byte_count);
4358 src_row += src_pitch;
4359 dst_row += dst_pitch;
4362 return WINED3D_OK;
4365 if (flags == WINEDDBLT_DDFX && fx->dwDDFX == WINEDDBLTFX_MIRRORUPDOWN)
4367 src_row += (((update_h / format->block_height) - 1) * src_pitch);
4369 switch (format->id)
4371 case WINED3DFMT_DXT1:
4372 for (y = 0; y < update_h; y += format->block_height)
4374 struct block
4376 WORD color[2];
4377 BYTE control_row[4];
4380 const struct block *s = (const struct block *)src_row;
4381 struct block *d = (struct block *)dst_row;
4383 for (x = 0; x < row_block_count; ++x)
4385 d[x].color[0] = s[x].color[0];
4386 d[x].color[1] = s[x].color[1];
4387 d[x].control_row[0] = s[x].control_row[3];
4388 d[x].control_row[1] = s[x].control_row[2];
4389 d[x].control_row[2] = s[x].control_row[1];
4390 d[x].control_row[3] = s[x].control_row[0];
4392 src_row -= src_pitch;
4393 dst_row += dst_pitch;
4395 return WINED3D_OK;
4397 case WINED3DFMT_DXT2:
4398 case WINED3DFMT_DXT3:
4399 for (y = 0; y < update_h; y += format->block_height)
4401 struct block
4403 WORD alpha_row[4];
4404 WORD color[2];
4405 BYTE control_row[4];
4408 const struct block *s = (const struct block *)src_row;
4409 struct block *d = (struct block *)dst_row;
4411 for (x = 0; x < row_block_count; ++x)
4413 d[x].alpha_row[0] = s[x].alpha_row[3];
4414 d[x].alpha_row[1] = s[x].alpha_row[2];
4415 d[x].alpha_row[2] = s[x].alpha_row[1];
4416 d[x].alpha_row[3] = s[x].alpha_row[0];
4417 d[x].color[0] = s[x].color[0];
4418 d[x].color[1] = s[x].color[1];
4419 d[x].control_row[0] = s[x].control_row[3];
4420 d[x].control_row[1] = s[x].control_row[2];
4421 d[x].control_row[2] = s[x].control_row[1];
4422 d[x].control_row[3] = s[x].control_row[0];
4424 src_row -= src_pitch;
4425 dst_row += dst_pitch;
4427 return WINED3D_OK;
4429 default:
4430 FIXME("Compressed flip not implemented for format %s.\n",
4431 debug_d3dformat(format->id));
4432 return E_NOTIMPL;
4436 FIXME("Unsupported blit on compressed surface (format %s, flags %#x, DDFX %#x).\n",
4437 debug_d3dformat(format->id), flags, flags & WINEDDBLT_DDFX ? fx->dwDDFX : 0);
4439 return E_NOTIMPL;
4442 static HRESULT surface_cpu_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect,
4443 struct wined3d_surface *src_surface, const RECT *src_rect, DWORD flags,
4444 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
4446 int bpp, srcheight, srcwidth, dstheight, dstwidth, width;
4447 const struct wined3d_format *src_format, *dst_format;
4448 unsigned int src_fmt_flags, dst_fmt_flags;
4449 struct wined3d_texture *src_texture = NULL;
4450 struct wined3d_map_desc dst_map, src_map;
4451 const BYTE *sbase = NULL;
4452 HRESULT hr = WINED3D_OK;
4453 const BYTE *sbuf;
4454 BYTE *dbuf;
4455 int x, y;
4457 TRACE("dst_surface %p, dst_rect %s, src_surface %p, src_rect %s, flags %#x, fx %p, filter %s.\n",
4458 dst_surface, wine_dbgstr_rect(dst_rect), src_surface, wine_dbgstr_rect(src_rect),
4459 flags, fx, debug_d3dtexturefiltertype(filter));
4461 if (src_surface == dst_surface)
4463 wined3d_surface_map(dst_surface, &dst_map, NULL, 0);
4464 src_map = dst_map;
4465 src_format = dst_surface->resource.format;
4466 dst_format = src_format;
4467 dst_fmt_flags = dst_surface->container->resource.format_flags;
4468 src_fmt_flags = dst_fmt_flags;
4470 else
4472 dst_format = dst_surface->resource.format;
4473 dst_fmt_flags = dst_surface->container->resource.format_flags;
4474 if (src_surface)
4476 if (dst_surface->resource.format->id != src_surface->resource.format->id)
4478 if (!(src_texture = surface_convert_format(src_surface, dst_format->id)))
4480 /* The conv function writes a FIXME */
4481 WARN("Cannot convert source surface format to dest format.\n");
4482 goto release;
4484 src_surface = surface_from_resource(wined3d_texture_get_sub_resource(src_texture, 0));
4486 wined3d_surface_map(src_surface, &src_map, NULL, WINED3D_MAP_READONLY);
4487 src_format = src_surface->resource.format;
4488 src_fmt_flags = src_surface->container->resource.format_flags;
4490 else
4492 src_format = dst_format;
4493 src_fmt_flags = dst_fmt_flags;
4496 wined3d_surface_map(dst_surface, &dst_map, dst_rect, 0);
4499 bpp = dst_surface->resource.format->byte_count;
4500 srcheight = src_rect->bottom - src_rect->top;
4501 srcwidth = src_rect->right - src_rect->left;
4502 dstheight = dst_rect->bottom - dst_rect->top;
4503 dstwidth = dst_rect->right - dst_rect->left;
4504 width = (dst_rect->right - dst_rect->left) * bpp;
4506 if (src_surface)
4507 sbase = (BYTE *)src_map.data
4508 + ((src_rect->top / src_format->block_height) * src_map.row_pitch)
4509 + ((src_rect->left / src_format->block_width) * src_format->block_byte_count);
4510 if (src_surface != dst_surface)
4511 dbuf = dst_map.data;
4512 else
4513 dbuf = (BYTE *)dst_map.data
4514 + ((dst_rect->top / dst_format->block_height) * dst_map.row_pitch)
4515 + ((dst_rect->left / dst_format->block_width) * dst_format->block_byte_count);
4517 if (src_fmt_flags & dst_fmt_flags & WINED3DFMT_FLAG_BLOCKS)
4519 TRACE("%s -> %s copy.\n", debug_d3dformat(src_format->id), debug_d3dformat(dst_format->id));
4521 if (src_surface == dst_surface)
4523 FIXME("Only plain blits supported on compressed surfaces.\n");
4524 hr = E_NOTIMPL;
4525 goto release;
4528 if (srcheight != dstheight || srcwidth != dstwidth)
4530 WARN("Stretching not supported on compressed surfaces.\n");
4531 hr = WINED3DERR_INVALIDCALL;
4532 goto release;
4535 if (!surface_check_block_align(src_surface, src_rect))
4537 WARN("Source rectangle not block-aligned.\n");
4538 hr = WINED3DERR_INVALIDCALL;
4539 goto release;
4542 if (!surface_check_block_align(dst_surface, dst_rect))
4544 WARN("Destination rectangle not block-aligned.\n");
4545 hr = WINED3DERR_INVALIDCALL;
4546 goto release;
4549 hr = surface_cpu_blt_compressed(sbase, dbuf,
4550 src_map.row_pitch, dst_map.row_pitch, dstwidth, dstheight,
4551 src_format, flags, fx);
4552 goto release;
4555 /* First, all the 'source-less' blits */
4556 if (flags & WINEDDBLT_COLORFILL)
4558 hr = _Blt_ColorFill(dbuf, dstwidth, dstheight, bpp, dst_map.row_pitch, fx->u5.dwFillColor);
4559 flags &= ~WINEDDBLT_COLORFILL;
4562 if (flags & WINEDDBLT_DEPTHFILL)
4564 FIXME("DDBLT_DEPTHFILL needs to be implemented!\n");
4566 if (flags & WINEDDBLT_DDROPS)
4568 FIXME("\tDdraw Raster Ops: %08x Pattern: %p\n", fx->dwDDROP, fx->u5.lpDDSPattern);
4570 /* Now the 'with source' blits. */
4571 if (src_surface)
4573 int sx, xinc, sy, yinc;
4575 if (!dstwidth || !dstheight) /* Hmm... stupid program? */
4576 goto release;
4578 if (filter != WINED3D_TEXF_NONE && filter != WINED3D_TEXF_POINT
4579 && (srcwidth != dstwidth || srcheight != dstheight))
4581 /* Can happen when d3d9 apps do a StretchRect() call which isn't handled in GL. */
4582 FIXME("Filter %s not supported in software blit.\n", debug_d3dtexturefiltertype(filter));
4585 xinc = (srcwidth << 16) / dstwidth;
4586 yinc = (srcheight << 16) / dstheight;
4588 if (!flags)
4590 /* No effects, we can cheat here. */
4591 if (dstwidth == srcwidth)
4593 if (dstheight == srcheight)
4595 /* No stretching in either direction. This needs to be as
4596 * fast as possible. */
4597 sbuf = sbase;
4599 /* Check for overlapping surfaces. */
4600 if (src_surface != dst_surface || dst_rect->top < src_rect->top
4601 || dst_rect->right <= src_rect->left || src_rect->right <= dst_rect->left)
4603 /* No overlap, or dst above src, so copy from top downwards. */
4604 for (y = 0; y < dstheight; ++y)
4606 memcpy(dbuf, sbuf, width);
4607 sbuf += src_map.row_pitch;
4608 dbuf += dst_map.row_pitch;
4611 else if (dst_rect->top > src_rect->top)
4613 /* Copy from bottom upwards. */
4614 sbuf += src_map.row_pitch * dstheight;
4615 dbuf += dst_map.row_pitch * dstheight;
4616 for (y = 0; y < dstheight; ++y)
4618 sbuf -= src_map.row_pitch;
4619 dbuf -= dst_map.row_pitch;
4620 memcpy(dbuf, sbuf, width);
4623 else
4625 /* Src and dst overlapping on the same line, use memmove. */
4626 for (y = 0; y < dstheight; ++y)
4628 memmove(dbuf, sbuf, width);
4629 sbuf += src_map.row_pitch;
4630 dbuf += dst_map.row_pitch;
4634 else
4636 /* Stretching in y direction only. */
4637 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4639 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4640 memcpy(dbuf, sbuf, width);
4641 dbuf += dst_map.row_pitch;
4645 else
4647 /* Stretching in X direction. */
4648 int last_sy = -1;
4649 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4651 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4653 if ((sy >> 16) == (last_sy >> 16))
4655 /* This source row is the same as last source row -
4656 * Copy the already stretched row. */
4657 memcpy(dbuf, dbuf - dst_map.row_pitch, width);
4659 else
4661 #define STRETCH_ROW(type) \
4662 do { \
4663 const type *s = (const type *)sbuf; \
4664 type *d = (type *)dbuf; \
4665 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4666 d[x] = s[sx >> 16]; \
4667 } while(0)
4669 switch(bpp)
4671 case 1:
4672 STRETCH_ROW(BYTE);
4673 break;
4674 case 2:
4675 STRETCH_ROW(WORD);
4676 break;
4677 case 4:
4678 STRETCH_ROW(DWORD);
4679 break;
4680 case 3:
4682 const BYTE *s;
4683 BYTE *d = dbuf;
4684 for (x = sx = 0; x < dstwidth; x++, sx+= xinc)
4686 DWORD pixel;
4688 s = sbuf + 3 * (sx >> 16);
4689 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4690 d[0] = (pixel ) & 0xff;
4691 d[1] = (pixel >> 8) & 0xff;
4692 d[2] = (pixel >> 16) & 0xff;
4693 d += 3;
4695 break;
4697 default:
4698 FIXME("Stretched blit not implemented for bpp %u!\n", bpp * 8);
4699 hr = WINED3DERR_NOTAVAILABLE;
4700 goto error;
4702 #undef STRETCH_ROW
4704 dbuf += dst_map.row_pitch;
4705 last_sy = sy;
4709 else
4711 LONG dstyinc = dst_map.row_pitch, dstxinc = bpp;
4712 DWORD keylow = 0xffffffff, keyhigh = 0, keymask = 0xffffffff;
4713 DWORD destkeylow = 0x0, destkeyhigh = 0xffffffff, destkeymask = 0xffffffff;
4714 if (flags & (WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE))
4716 /* The color keying flags are checked for correctness in ddraw */
4717 if (flags & WINEDDBLT_KEYSRC)
4719 keylow = src_surface->container->async.src_blt_color_key.color_space_low_value;
4720 keyhigh = src_surface->container->async.src_blt_color_key.color_space_high_value;
4722 else if (flags & WINEDDBLT_KEYSRCOVERRIDE)
4724 keylow = fx->ddckSrcColorkey.color_space_low_value;
4725 keyhigh = fx->ddckSrcColorkey.color_space_high_value;
4728 if (flags & WINEDDBLT_KEYDEST)
4730 /* Destination color keys are taken from the source surface! */
4731 destkeylow = src_surface->container->async.dst_blt_color_key.color_space_low_value;
4732 destkeyhigh = src_surface->container->async.dst_blt_color_key.color_space_high_value;
4734 else if (flags & WINEDDBLT_KEYDESTOVERRIDE)
4736 destkeylow = fx->ddckDestColorkey.color_space_low_value;
4737 destkeyhigh = fx->ddckDestColorkey.color_space_high_value;
4740 if (bpp == 1)
4742 keymask = 0xff;
4744 else
4746 DWORD masks[3];
4747 get_color_masks(src_format, masks);
4748 keymask = masks[0]
4749 | masks[1]
4750 | masks[2];
4752 flags &= ~(WINEDDBLT_KEYSRC | WINEDDBLT_KEYDEST | WINEDDBLT_KEYSRCOVERRIDE | WINEDDBLT_KEYDESTOVERRIDE);
4755 if (flags & WINEDDBLT_DDFX)
4757 BYTE *dTopLeft, *dTopRight, *dBottomLeft, *dBottomRight, *tmp;
4758 LONG tmpxy;
4759 dTopLeft = dbuf;
4760 dTopRight = dbuf + ((dstwidth - 1) * bpp);
4761 dBottomLeft = dTopLeft + ((dstheight - 1) * dst_map.row_pitch);
4762 dBottomRight = dBottomLeft + ((dstwidth - 1) * bpp);
4764 if (fx->dwDDFX & WINEDDBLTFX_ARITHSTRETCHY)
4766 /* I don't think we need to do anything about this flag */
4767 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_ARITHSTRETCHY\n");
4769 if (fx->dwDDFX & WINEDDBLTFX_MIRRORLEFTRIGHT)
4771 tmp = dTopRight;
4772 dTopRight = dTopLeft;
4773 dTopLeft = tmp;
4774 tmp = dBottomRight;
4775 dBottomRight = dBottomLeft;
4776 dBottomLeft = tmp;
4777 dstxinc = dstxinc * -1;
4779 if (fx->dwDDFX & WINEDDBLTFX_MIRRORUPDOWN)
4781 tmp = dTopLeft;
4782 dTopLeft = dBottomLeft;
4783 dBottomLeft = tmp;
4784 tmp = dTopRight;
4785 dTopRight = dBottomRight;
4786 dBottomRight = tmp;
4787 dstyinc = dstyinc * -1;
4789 if (fx->dwDDFX & WINEDDBLTFX_NOTEARING)
4791 /* I don't think we need to do anything about this flag */
4792 WARN("flags=DDBLT_DDFX nothing done for WINEDDBLTFX_NOTEARING\n");
4794 if (fx->dwDDFX & WINEDDBLTFX_ROTATE180)
4796 tmp = dBottomRight;
4797 dBottomRight = dTopLeft;
4798 dTopLeft = tmp;
4799 tmp = dBottomLeft;
4800 dBottomLeft = dTopRight;
4801 dTopRight = tmp;
4802 dstxinc = dstxinc * -1;
4803 dstyinc = dstyinc * -1;
4805 if (fx->dwDDFX & WINEDDBLTFX_ROTATE270)
4807 tmp = dTopLeft;
4808 dTopLeft = dBottomLeft;
4809 dBottomLeft = dBottomRight;
4810 dBottomRight = dTopRight;
4811 dTopRight = tmp;
4812 tmpxy = dstxinc;
4813 dstxinc = dstyinc;
4814 dstyinc = tmpxy;
4815 dstxinc = dstxinc * -1;
4817 if (fx->dwDDFX & WINEDDBLTFX_ROTATE90)
4819 tmp = dTopLeft;
4820 dTopLeft = dTopRight;
4821 dTopRight = dBottomRight;
4822 dBottomRight = dBottomLeft;
4823 dBottomLeft = tmp;
4824 tmpxy = dstxinc;
4825 dstxinc = dstyinc;
4826 dstyinc = tmpxy;
4827 dstyinc = dstyinc * -1;
4829 if (fx->dwDDFX & WINEDDBLTFX_ZBUFFERBASEDEST)
4831 /* I don't think we need to do anything about this flag */
4832 WARN("flags=WINEDDBLT_DDFX nothing done for WINEDDBLTFX_ZBUFFERBASEDEST\n");
4834 dbuf = dTopLeft;
4835 flags &= ~(WINEDDBLT_DDFX);
4838 #define COPY_COLORKEY_FX(type) \
4839 do { \
4840 const type *s; \
4841 type *d = (type *)dbuf, *dx, tmp; \
4842 for (y = sy = 0; y < dstheight; ++y, sy += yinc) \
4844 s = (const type *)(sbase + (sy >> 16) * src_map.row_pitch); \
4845 dx = d; \
4846 for (x = sx = 0; x < dstwidth; ++x, sx += xinc) \
4848 tmp = s[sx >> 16]; \
4849 if (((tmp & keymask) < keylow || (tmp & keymask) > keyhigh) \
4850 && ((dx[0] & destkeymask) >= destkeylow && (dx[0] & destkeymask) <= destkeyhigh)) \
4852 dx[0] = tmp; \
4854 dx = (type *)(((BYTE *)dx) + dstxinc); \
4856 d = (type *)(((BYTE *)d) + dstyinc); \
4858 } while(0)
4860 switch (bpp)
4862 case 1:
4863 COPY_COLORKEY_FX(BYTE);
4864 break;
4865 case 2:
4866 COPY_COLORKEY_FX(WORD);
4867 break;
4868 case 4:
4869 COPY_COLORKEY_FX(DWORD);
4870 break;
4871 case 3:
4873 const BYTE *s;
4874 BYTE *d = dbuf, *dx;
4875 for (y = sy = 0; y < dstheight; ++y, sy += yinc)
4877 sbuf = sbase + (sy >> 16) * src_map.row_pitch;
4878 dx = d;
4879 for (x = sx = 0; x < dstwidth; ++x, sx+= xinc)
4881 DWORD pixel, dpixel = 0;
4882 s = sbuf + 3 * (sx>>16);
4883 pixel = s[0] | (s[1] << 8) | (s[2] << 16);
4884 dpixel = dx[0] | (dx[1] << 8 ) | (dx[2] << 16);
4885 if (((pixel & keymask) < keylow || (pixel & keymask) > keyhigh)
4886 && ((dpixel & keymask) >= destkeylow || (dpixel & keymask) <= keyhigh))
4888 dx[0] = (pixel ) & 0xff;
4889 dx[1] = (pixel >> 8) & 0xff;
4890 dx[2] = (pixel >> 16) & 0xff;
4892 dx += dstxinc;
4894 d += dstyinc;
4896 break;
4898 default:
4899 FIXME("%s color-keyed blit not implemented for bpp %u!\n",
4900 (flags & WINEDDBLT_KEYSRC) ? "Source" : "Destination", bpp * 8);
4901 hr = WINED3DERR_NOTAVAILABLE;
4902 goto error;
4903 #undef COPY_COLORKEY_FX
4908 error:
4909 if (flags && FIXME_ON(d3d_surface))
4911 FIXME("\tUnsupported flags: %#x.\n", flags);
4914 release:
4915 wined3d_surface_unmap(dst_surface);
4916 if (src_surface && src_surface != dst_surface)
4917 wined3d_surface_unmap(src_surface);
4918 /* Release the converted surface, if any. */
4919 if (src_texture)
4920 wined3d_texture_decref(src_texture);
4922 return hr;
4925 static HRESULT cpu_blit_color_fill(struct wined3d_device *device, struct wined3d_surface *dst_surface,
4926 const RECT *dst_rect, const struct wined3d_color *color)
4928 static const RECT src_rect;
4929 WINEDDBLTFX BltFx;
4931 memset(&BltFx, 0, sizeof(BltFx));
4932 BltFx.dwSize = sizeof(BltFx);
4933 BltFx.u5.dwFillColor = wined3d_format_convert_from_float(dst_surface, color);
4934 return surface_cpu_blt(dst_surface, dst_rect, NULL, &src_rect,
4935 WINEDDBLT_COLORFILL, &BltFx, WINED3D_TEXF_POINT);
4938 static HRESULT cpu_blit_depth_fill(struct wined3d_device *device,
4939 struct wined3d_surface *surface, const RECT *rect, float depth)
4941 FIXME("Depth filling not implemented by cpu_blit.\n");
4942 return WINED3DERR_INVALIDCALL;
4945 static void cpu_blit_blit_surface(struct wined3d_device *device, DWORD filter,
4946 struct wined3d_surface *src_surface, const RECT *src_rect,
4947 struct wined3d_surface *dst_surface, const RECT *dst_rect,
4948 const struct wined3d_color_key *color_key)
4950 /* FIXME: Remove error returns from surface_blt_cpu. */
4951 ERR("Blit method not implemented by cpu_blit.\n");
4954 const struct blit_shader cpu_blit = {
4955 cpu_blit_alloc,
4956 cpu_blit_free,
4957 cpu_blit_set,
4958 cpu_blit_unset,
4959 cpu_blit_supported,
4960 cpu_blit_color_fill,
4961 cpu_blit_depth_fill,
4962 cpu_blit_blit_surface,
4965 HRESULT CDECL wined3d_surface_blt(struct wined3d_surface *dst_surface, const RECT *dst_rect_in,
4966 struct wined3d_surface *src_surface, const RECT *src_rect_in, DWORD flags,
4967 const WINEDDBLTFX *fx, enum wined3d_texture_filter_type filter)
4969 struct wined3d_swapchain *src_swapchain, *dst_swapchain;
4970 struct wined3d_device *device = dst_surface->resource.device;
4971 DWORD src_ds_flags, dst_ds_flags;
4972 RECT src_rect, dst_rect;
4973 BOOL scale, convert;
4975 static const DWORD simple_blit = WINEDDBLT_ASYNC
4976 | WINEDDBLT_COLORFILL
4977 | WINEDDBLT_KEYSRC
4978 | WINEDDBLT_KEYSRCOVERRIDE
4979 | WINEDDBLT_WAIT
4980 | WINEDDBLT_DEPTHFILL
4981 | WINEDDBLT_DONOTWAIT;
4983 TRACE("dst_surface %p, dst_rect_in %s, src_surface %p, src_rect_in %s, flags %#x, fx %p, filter %s.\n",
4984 dst_surface, wine_dbgstr_rect(dst_rect_in), src_surface, wine_dbgstr_rect(src_rect_in),
4985 flags, fx, debug_d3dtexturefiltertype(filter));
4986 TRACE("Usage is %s.\n", debug_d3dusage(dst_surface->resource.usage));
4988 if (fx)
4990 TRACE("dwSize %#x.\n", fx->dwSize);
4991 TRACE("dwDDFX %#x.\n", fx->dwDDFX);
4992 TRACE("dwROP %#x.\n", fx->dwROP);
4993 TRACE("dwDDROP %#x.\n", fx->dwDDROP);
4994 TRACE("dwRotationAngle %#x.\n", fx->dwRotationAngle);
4995 TRACE("dwZBufferOpCode %#x.\n", fx->dwZBufferOpCode);
4996 TRACE("dwZBufferLow %#x.\n", fx->dwZBufferLow);
4997 TRACE("dwZBufferHigh %#x.\n", fx->dwZBufferHigh);
4998 TRACE("dwZBufferBaseDest %#x.\n", fx->dwZBufferBaseDest);
4999 TRACE("dwZDestConstBitDepth %#x.\n", fx->dwZDestConstBitDepth);
5000 TRACE("lpDDSZBufferDest %p.\n", fx->u1.lpDDSZBufferDest);
5001 TRACE("dwZSrcConstBitDepth %#x.\n", fx->dwZSrcConstBitDepth);
5002 TRACE("lpDDSZBufferSrc %p.\n", fx->u2.lpDDSZBufferSrc);
5003 TRACE("dwAlphaEdgeBlendBitDepth %#x.\n", fx->dwAlphaEdgeBlendBitDepth);
5004 TRACE("dwAlphaEdgeBlend %#x.\n", fx->dwAlphaEdgeBlend);
5005 TRACE("dwReserved %#x.\n", fx->dwReserved);
5006 TRACE("dwAlphaDestConstBitDepth %#x.\n", fx->dwAlphaDestConstBitDepth);
5007 TRACE("lpDDSAlphaDest %p.\n", fx->u3.lpDDSAlphaDest);
5008 TRACE("dwAlphaSrcConstBitDepth %#x.\n", fx->dwAlphaSrcConstBitDepth);
5009 TRACE("lpDDSAlphaSrc %p.\n", fx->u4.lpDDSAlphaSrc);
5010 TRACE("lpDDSPattern %p.\n", fx->u5.lpDDSPattern);
5011 TRACE("ddckDestColorkey {%#x, %#x}.\n",
5012 fx->ddckDestColorkey.color_space_low_value,
5013 fx->ddckDestColorkey.color_space_high_value);
5014 TRACE("ddckSrcColorkey {%#x, %#x}.\n",
5015 fx->ddckSrcColorkey.color_space_low_value,
5016 fx->ddckSrcColorkey.color_space_high_value);
5019 if (dst_surface->resource.map_count || (src_surface && src_surface->resource.map_count))
5021 WARN("Surface is busy, returning WINEDDERR_SURFACEBUSY.\n");
5022 return WINEDDERR_SURFACEBUSY;
5025 surface_get_rect(dst_surface, dst_rect_in, &dst_rect);
5027 if (dst_rect.left >= dst_rect.right || dst_rect.top >= dst_rect.bottom
5028 || dst_rect.left > dst_surface->resource.width || dst_rect.left < 0
5029 || dst_rect.top > dst_surface->resource.height || dst_rect.top < 0
5030 || dst_rect.right > dst_surface->resource.width || dst_rect.right < 0
5031 || dst_rect.bottom > dst_surface->resource.height || dst_rect.bottom < 0)
5033 WARN("The application gave us a bad destination rectangle.\n");
5034 return WINEDDERR_INVALIDRECT;
5037 if (src_surface)
5039 surface_get_rect(src_surface, src_rect_in, &src_rect);
5041 if (src_rect.left >= src_rect.right || src_rect.top >= src_rect.bottom
5042 || src_rect.left > src_surface->resource.width || src_rect.left < 0
5043 || src_rect.top > src_surface->resource.height || src_rect.top < 0
5044 || src_rect.right > src_surface->resource.width || src_rect.right < 0
5045 || src_rect.bottom > src_surface->resource.height || src_rect.bottom < 0)
5047 WARN("Application gave us bad source rectangle for Blt.\n");
5048 return WINEDDERR_INVALIDRECT;
5051 else
5053 memset(&src_rect, 0, sizeof(src_rect));
5056 if (!fx || !(fx->dwDDFX))
5057 flags &= ~WINEDDBLT_DDFX;
5059 if (flags & WINEDDBLT_WAIT)
5060 flags &= ~WINEDDBLT_WAIT;
5062 if (flags & WINEDDBLT_ASYNC)
5064 static unsigned int once;
5066 if (!once++)
5067 FIXME("Can't handle WINEDDBLT_ASYNC flag.\n");
5068 flags &= ~WINEDDBLT_ASYNC;
5071 /* WINEDDBLT_DONOTWAIT appeared in DX7. */
5072 if (flags & WINEDDBLT_DONOTWAIT)
5074 static unsigned int once;
5076 if (!once++)
5077 FIXME("Can't handle WINEDDBLT_DONOTWAIT flag.\n");
5078 flags &= ~WINEDDBLT_DONOTWAIT;
5081 if (!device->d3d_initialized)
5083 WARN("D3D not initialized, using fallback.\n");
5084 goto cpu;
5087 /* We want to avoid invalidating the sysmem location for converted
5088 * surfaces, since otherwise we'd have to convert the data back when
5089 * locking them. */
5090 if (dst_surface->container->flags & WINED3D_TEXTURE_CONVERTED
5091 || dst_surface->container->resource.format->convert
5092 || wined3d_format_get_color_key_conversion(dst_surface->container, TRUE))
5094 WARN_(d3d_perf)("Converted surface, using CPU blit.\n");
5095 goto cpu;
5098 if (flags & ~simple_blit)
5100 WARN_(d3d_perf)("Using fallback for complex blit (%#x).\n", flags);
5101 goto fallback;
5104 if (src_surface)
5105 src_swapchain = src_surface->container->swapchain;
5106 else
5107 src_swapchain = NULL;
5109 dst_swapchain = dst_surface->container->swapchain;
5111 /* This isn't strictly needed. FBO blits for example could deal with
5112 * cross-swapchain blits by first downloading the source to a texture
5113 * before switching to the destination context. We just have this here to
5114 * not have to deal with the issue, since cross-swapchain blits should be
5115 * rare. */
5116 if (src_swapchain && dst_swapchain && src_swapchain != dst_swapchain)
5118 FIXME("Using fallback for cross-swapchain blit.\n");
5119 goto fallback;
5122 scale = src_surface
5123 && (src_rect.right - src_rect.left != dst_rect.right - dst_rect.left
5124 || src_rect.bottom - src_rect.top != dst_rect.bottom - dst_rect.top);
5125 convert = src_surface && src_surface->resource.format->id != dst_surface->resource.format->id;
5127 dst_ds_flags = dst_surface->container->resource.format_flags
5128 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5129 if (src_surface)
5130 src_ds_flags = src_surface->container->resource.format_flags
5131 & (WINED3DFMT_FLAG_DEPTH | WINED3DFMT_FLAG_STENCIL);
5132 else
5133 src_ds_flags = 0;
5135 if (src_ds_flags || dst_ds_flags)
5137 if (flags & WINEDDBLT_DEPTHFILL)
5139 float depth;
5141 TRACE("Depth fill.\n");
5143 if (!surface_convert_depth_to_float(dst_surface, fx->u5.dwFillDepth, &depth))
5144 return WINED3DERR_INVALIDCALL;
5146 if (SUCCEEDED(wined3d_surface_depth_fill(dst_surface, &dst_rect, depth)))
5147 return WINED3D_OK;
5149 else
5151 if (src_ds_flags != dst_ds_flags)
5153 WARN("Rejecting depth / stencil blit between incompatible formats.\n");
5154 return WINED3DERR_INVALIDCALL;
5157 if (SUCCEEDED(wined3d_surface_depth_blt(src_surface, src_surface->container->resource.draw_binding,
5158 &src_rect, dst_surface, dst_surface->container->resource.draw_binding, &dst_rect)))
5159 return WINED3D_OK;
5162 else
5164 const struct blit_shader *blitter;
5166 /* In principle this would apply to depth blits as well, but we don't
5167 * implement those in the CPU blitter at the moment. */
5168 if ((dst_surface->locations & dst_surface->resource.map_binding)
5169 && (!src_surface || (src_surface->locations & src_surface->resource.map_binding)))
5171 if (scale)
5172 TRACE("Not doing sysmem blit because of scaling.\n");
5173 else if (convert)
5174 TRACE("Not doing sysmem blit because of format conversion.\n");
5175 else
5176 goto cpu;
5179 if (flags & WINEDDBLT_COLORFILL)
5181 struct wined3d_color color;
5182 const struct wined3d_palette *palette = dst_swapchain ? dst_swapchain->palette : NULL;
5184 TRACE("Color fill.\n");
5186 if (!wined3d_format_convert_color_to_float(dst_surface->resource.format,
5187 palette, fx->u5.dwFillColor, &color))
5188 goto fallback;
5190 if (SUCCEEDED(surface_color_fill(dst_surface, &dst_rect, &color)))
5191 return WINED3D_OK;
5193 else
5195 enum wined3d_blit_op blit_op = WINED3D_BLIT_OP_COLOR_BLIT;
5196 const struct wined3d_color_key *color_key = NULL;
5198 TRACE("Color blit.\n");
5199 if (flags & WINEDDBLT_KEYSRCOVERRIDE)
5201 color_key = &fx->ddckSrcColorkey;
5202 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
5204 else if (flags & WINEDDBLT_KEYSRC)
5206 color_key = &src_surface->container->async.src_blt_color_key;
5207 blit_op = WINED3D_BLIT_OP_COLOR_BLIT_CKEY;
5209 else if ((src_surface->locations & WINED3D_LOCATION_SYSMEM)
5210 && !(dst_surface->locations & WINED3D_LOCATION_SYSMEM))
5212 /* Upload */
5213 if (scale)
5214 TRACE("Not doing upload because of scaling.\n");
5215 else if (convert)
5216 TRACE("Not doing upload because of format conversion.\n");
5217 else
5219 POINT dst_point = {dst_rect.left, dst_rect.top};
5221 if (SUCCEEDED(surface_upload_from_surface(dst_surface, &dst_point, src_surface, &src_rect)))
5223 if (!wined3d_resource_is_offscreen(&dst_surface->container->resource))
5224 surface_load_location(dst_surface, dst_surface->container->resource.draw_binding);
5225 return WINED3D_OK;
5229 else if (dst_swapchain && dst_swapchain->back_buffers
5230 && dst_surface->container == dst_swapchain->front_buffer
5231 && src_surface->container == dst_swapchain->back_buffers[0])
5233 /* Use present for back -> front blits. The idea behind this is
5234 * that present is potentially faster than a blit, in particular
5235 * when FBO blits aren't available. Some ddraw applications like
5236 * Half-Life and Prince of Persia 3D use Blt() from the backbuffer
5237 * to the frontbuffer instead of doing a Flip(). D3D8 and D3D9
5238 * applications can't blit directly to the frontbuffer. */
5239 enum wined3d_swap_effect swap_effect = dst_swapchain->desc.swap_effect;
5241 TRACE("Using present for backbuffer -> frontbuffer blit.\n");
5243 /* Set the swap effect to COPY, we don't want the backbuffer
5244 * to become undefined. */
5245 dst_swapchain->desc.swap_effect = WINED3D_SWAP_EFFECT_COPY;
5246 wined3d_swapchain_present(dst_swapchain, NULL, NULL, dst_swapchain->win_handle, NULL, 0);
5247 dst_swapchain->desc.swap_effect = swap_effect;
5249 return WINED3D_OK;
5252 if (fbo_blit_supported(&device->adapter->gl_info, blit_op,
5253 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5254 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format))
5256 TRACE("Using FBO blit.\n");
5258 surface_blt_fbo(device, filter,
5259 src_surface, src_surface->container->resource.draw_binding, &src_rect,
5260 dst_surface, dst_surface->container->resource.draw_binding, &dst_rect);
5261 surface_validate_location(dst_surface, dst_surface->container->resource.draw_binding);
5262 surface_invalidate_location(dst_surface, ~dst_surface->container->resource.draw_binding);
5264 return WINED3D_OK;
5267 blitter = wined3d_select_blitter(&device->adapter->gl_info, &device->adapter->d3d_info, blit_op,
5268 &src_rect, src_surface->resource.usage, src_surface->resource.pool, src_surface->resource.format,
5269 &dst_rect, dst_surface->resource.usage, dst_surface->resource.pool, dst_surface->resource.format);
5270 if (blitter)
5272 blitter->blit_surface(device, filter, src_surface, &src_rect, dst_surface, &dst_rect, color_key);
5273 return WINED3D_OK;
5278 fallback:
5279 /* Special cases for render targets. */
5280 if (SUCCEEDED(surface_blt_special(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter)))
5281 return WINED3D_OK;
5283 cpu:
5285 /* For the rest call the X11 surface implementation. For render targets
5286 * this should be implemented OpenGL accelerated in surface_blt_special(),
5287 * other blits are rather rare. */
5288 return surface_cpu_blt(dst_surface, &dst_rect, src_surface, &src_rect, flags, fx, filter);
5291 static HRESULT surface_init(struct wined3d_surface *surface, struct wined3d_texture *container,
5292 const struct wined3d_resource_desc *desc, GLenum target, unsigned int level, unsigned int layer, DWORD flags)
5294 struct wined3d_device *device = container->resource.device;
5295 const struct wined3d_gl_info *gl_info = &device->adapter->gl_info;
5296 const struct wined3d_format *format = wined3d_get_format(gl_info, desc->format);
5297 UINT multisample_quality = desc->multisample_quality;
5298 BOOL lockable = flags & WINED3D_SURFACE_MAPPABLE;
5299 unsigned int resource_size;
5300 HRESULT hr;
5302 /* Quick lockable sanity check.
5303 * TODO: remove this after surfaces, usage and lockability have been debugged properly
5304 * this function is too deep to need to care about things like this.
5305 * Levels need to be checked too, since they all affect what can be done. */
5306 switch (desc->pool)
5308 case WINED3D_POOL_MANAGED:
5309 if (desc->usage & WINED3DUSAGE_DYNAMIC)
5310 FIXME("Called with a pool of MANAGED and a usage of DYNAMIC which are mutually exclusive.\n");
5311 break;
5313 case WINED3D_POOL_DEFAULT:
5314 if (lockable && !(desc->usage & (WINED3DUSAGE_DYNAMIC
5315 | WINED3DUSAGE_RENDERTARGET | WINED3DUSAGE_DEPTHSTENCIL)))
5316 WARN("Creating a lockable surface with a POOL of DEFAULT, that doesn't specify DYNAMIC usage.\n");
5317 break;
5319 case WINED3D_POOL_SCRATCH:
5320 case WINED3D_POOL_SYSTEM_MEM:
5321 break;
5323 default:
5324 FIXME("Unknown pool %#x.\n", desc->pool);
5325 break;
5328 if (desc->usage & WINED3DUSAGE_RENDERTARGET && desc->pool != WINED3D_POOL_DEFAULT)
5329 FIXME("Trying to create a render target that isn't in the default pool.\n");
5331 /* FIXME: Check that the format is supported by the device. */
5333 resource_size = wined3d_format_calculate_size(format, device->surface_alignment, desc->width, desc->height, 1);
5334 if (!resource_size)
5335 return WINED3DERR_INVALIDCALL;
5337 if (device->wined3d->flags & WINED3D_NO3D)
5338 surface->surface_ops = &gdi_surface_ops;
5339 else
5340 surface->surface_ops = &surface_ops;
5342 if (FAILED(hr = resource_init(&surface->resource, device, WINED3D_RTYPE_SURFACE,
5343 format, desc->multisample_type, multisample_quality, desc->usage, desc->pool, desc->width, desc->height,
5344 1, resource_size, NULL, &wined3d_null_parent_ops, &surface_resource_ops)))
5346 WARN("Failed to initialize resource, returning %#x.\n", hr);
5347 return hr;
5350 surface->container = container;
5351 surface_validate_location(surface, WINED3D_LOCATION_SYSMEM);
5352 list_init(&surface->renderbuffers);
5353 list_init(&surface->overlays);
5355 /* Flags */
5356 if (flags & WINED3D_SURFACE_DISCARD)
5357 surface->flags |= SFLAG_DISCARD;
5358 if (lockable || desc->format == WINED3DFMT_D16_LOCKABLE)
5359 surface->resource.access_flags |= WINED3D_RESOURCE_ACCESS_CPU;
5361 surface->texture_target = target;
5362 surface->texture_level = level;
5363 surface->texture_layer = layer;
5365 /* Call the private setup routine */
5366 if (FAILED(hr = surface->surface_ops->surface_private_setup(surface)))
5368 ERR("Private setup failed, hr %#x.\n", hr);
5369 surface_cleanup(surface);
5370 return hr;
5373 /* Similar to lockable rendertargets above, creating the DIB section
5374 * during surface initialization prevents the sysmem pointer from changing
5375 * after a wined3d_surface_getdc() call. */
5376 if ((desc->usage & WINED3DUSAGE_OWNDC) && !surface->hDC
5377 && SUCCEEDED(surface_create_dib_section(surface)))
5378 surface->resource.map_binding = WINED3D_LOCATION_DIB;
5380 if (surface->resource.map_binding == WINED3D_LOCATION_DIB)
5382 wined3d_resource_free_sysmem(&surface->resource);
5383 surface_validate_location(surface, WINED3D_LOCATION_DIB);
5384 surface_invalidate_location(surface, WINED3D_LOCATION_SYSMEM);
5387 return hr;
5390 HRESULT wined3d_surface_create(struct wined3d_texture *container, const struct wined3d_resource_desc *desc,
5391 GLenum target, unsigned int level, unsigned int layer, DWORD flags, struct wined3d_surface **surface)
5393 struct wined3d_device_parent *device_parent = container->resource.device->device_parent;
5394 const struct wined3d_parent_ops *parent_ops;
5395 struct wined3d_surface *object;
5396 void *parent;
5397 HRESULT hr;
5399 TRACE("container %p, width %u, height %u, format %s, usage %s (%#x), pool %s, "
5400 "multisample_type %#x, multisample_quality %u, target %#x, level %u, layer %u, flags %#x, surface %p.\n",
5401 container, desc->width, desc->height, debug_d3dformat(desc->format),
5402 debug_d3dusage(desc->usage), desc->usage, debug_d3dpool(desc->pool),
5403 desc->multisample_type, desc->multisample_quality, target, level, layer, flags, surface);
5405 if (!(object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object))))
5406 return E_OUTOFMEMORY;
5408 if (FAILED(hr = surface_init(object, container, desc, target, level, layer, flags)))
5410 WARN("Failed to initialize surface, returning %#x.\n", hr);
5411 HeapFree(GetProcessHeap(), 0, object);
5412 return hr;
5415 if (FAILED(hr = device_parent->ops->surface_created(device_parent,
5416 wined3d_texture_get_parent(container), object, &parent, &parent_ops)))
5418 WARN("Failed to create surface parent, hr %#x.\n", hr);
5419 wined3d_surface_destroy(object);
5420 return hr;
5423 TRACE("Created surface %p, parent %p, parent_ops %p.\n", object, parent, parent_ops);
5425 object->resource.parent = parent;
5426 object->resource.parent_ops = parent_ops;
5427 *surface = object;
5429 return hr;